感觉这本书的顺序设计的太不合理了,出现的最多的一句话就是后面会讲。按照使用的顺序讲不行吗?搞得代码都运行不了。

我决定先直接跳到73页,子类化QTableWidgetItem这一节。因为前面功能的实现都依赖于这一部分。

预备知识:

C++关键字 mutable:

  mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。
  在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
  我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。

----------------------------------------------------------------------------------------------------------------

Start:

QTableWidget是一个QT已经实现了的二维表格的类,每一个单元格的文本都用一个自动创建的QTableWidgetItem来存储。

为了实现更多的功能,我们自己创建一个类Cell来扩展QTableWidgetItem的功能。

QVariant 类: The QVariant class acts like a union for the most common Qt data types.

在Cell类中,用两个私有变量来扩展功能。

  mutable QVariant cachedValue;
   mutable bool cacheIsDirty;

cachedValue 缓存单元格的值

若单元格的值不是最新的 cacheIsDirty 设为 true

好吧 后面变成了纯粹的看这一部分的代码,计算表达式的值时,三个函数的循环调用有些难度。

cell.h

#ifndef CELL_H
#define CELL_H #include <QTableWidgetItem> class Cell : public QTableWidgetItem
{
public:
Cell();
QTableWidgetItem *clone() const;
void setData(int role, const QVariant &value);
QVariant data(int role) const;
void setFormula(const QString &formula); //设置单元格公式
QString formula() const;
void setDirty(); //把值设为旧的 private:
QVariant value() const; //返回单元格的合适的值
QVariant evalExpression(const QString &str, int &pos) const; //解析表达式
QVariant evalTerm(const QString &str, int &pos) const; //解析项
QVariant evalFactor(const QString &str, int &pos) const; //解析因子 mutable QVariant cachedValue;
mutable bool cacheIsDirty;
}; #endif // CELL_H

cell.cpp

#include <QtGui>
#include "cell.h" Cell::Cell()
{
setDirty();
} //新建一个单元格时调用
QTableWidgetItem *Cell::clone() const
{
return new Cell(*this);
} void Cell::setDirty()
{
cacheIsDirty = true;
} const QVariant Invalid; QVariant Cell::value() const //设置单元格的值
{
if (cacheIsDirty) {
cacheIsDirty = false; QString formulaStr = formula();
if (formulaStr.startsWith('\'')) { // ‘开始返回字符串
cachedValue = formulaStr.mid();
} else if (formulaStr.startsWith('=')) { // =开始返回公式
cachedValue = Invalid;
QString expr = formulaStr.mid();
expr.replace(" ", "");
expr.append(QChar::Null); int pos = ;
cachedValue = evalExpression(expr, pos);
if (expr[pos] != QChar::Null) //解析表达式失败 返回无效值
cachedValue = Invalid;
} else {
bool ok;
double d = formulaStr.toDouble(&ok); //转换为数字成功 返回数字
if (ok) {
cachedValue = d;
} else {
cachedValue = formulaStr; //返回字符串
}
}
}
return cachedValue;
} void Cell::setData(int role, const QVariant &value)
{
QTableWidgetItem::setData(role, value);
if (role == Qt::EditRole)
setDirty(); //如果有新的公式就把cacheIsDirty设为True 以保证下次调用text时重新计算值
} QVariant Cell::data(int role) const //重新实现QTableWidgetItem::data
{
if (role == Qt::DisplayRole) { //如果是DisplayRole调用这个函数 返回应该显示的文本
if (value().isValid()) {
return value().toString();
} else {
return "####"; //如果文本无效 返回####
}
} else if (role == Qt::TextAlignmentRole) {//返回合适的对齐方式
if (value().type() == QVariant::String) {
return int(Qt::AlignLeft | Qt::AlignVCenter);
} else {
return int(Qt::AlignRight | Qt::AlignVCenter);
}
} else { //如果 EditRole调用 返回该单元格的公式
return QTableWidgetItem::data(role);
}
} void Cell::setFormula(const QString &formula)
{
setData(Qt::EditRole, formula); //对编辑角色调用setData
} QString Cell::formula() const
{
return data(Qt::EditRole).toString(); //重新获得该项的EditRole数据
} //对于下面三个函数的循环套用没完全看懂
QVariant Cell::evalExpression(const QString &str, int &pos) const
{
QVariant result = evalTerm(str, pos);
while (str[pos] != QChar::Null) {
QChar op = str[pos];
if (op != '+' && op != '-')
return result; //这里的return 使得evalFactor中调用该函数成为可能
++pos; QVariant term = evalTerm(str, pos);
if (result.type() == QVariant::Double
&& term.type() == QVariant::Double) {
if (op == '+') {
result = result.toDouble() + term.toDouble();
} else {
result = result.toDouble() - term.toDouble();
}
} else {
result = Invalid;
}
}
return result;
} QVariant Cell::evalTerm(const QString &str, int &pos) const
{
QVariant result = evalFactor(str, pos);
while (str[pos] != QChar::Null) {
QChar op = str[pos];
if (op != '*' && op != '/')
return result;
++pos; QVariant factor = evalFactor(str, pos);
if (result.type() == QVariant::Double
&& factor.type() == QVariant::Double) {
if (op == '*') {
result = result.toDouble() * factor.toDouble();
} else {
if (factor.toDouble() == 0.0) {
result = Invalid;
} else {
result = result.toDouble() / factor.toDouble();
}
}
} else {
result = Invalid;
}
}
return result;
} QVariant Cell::evalFactor(const QString &str, int &pos) const
{
QVariant result;
bool negative = false; if (str[pos] == '-') {
negative = true;
++pos;
} if (str[pos] == '(') {
++pos;
result = evalExpression(str, pos);
if (str[pos] != ')')
result = Invalid;
++pos;
} else {
QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
QString token; while (str[pos].isLetterOrNumber() || str[pos] == '.') {
token += str[pos];
++pos;
} if (regExp.exactMatch(token)) {
int column = token[].toUpper().unicode() - 'A';
int row = token.mid().toInt() - ; Cell *c = static_cast<Cell *>(
tableWidget()->item(row, column));
if (c) {
result = c->value();
} else {
result = 0.0;
}
} else {
bool ok;
result = token.toDouble(&ok);
if (!ok)
result = Invalid;
}
} if (negative) {
if (result.type() == QVariant::Double) {
result = -result.toDouble();
} else {
result = Invalid;
}
}
return result;
}

然后回去看spreadsheet部分,把定义和clear()实现后终于可以显示一个像样子的界面了。虽然没有实现功能,但也不错了。

【QT】C++ GUI Qt4 学习笔记4的更多相关文章

  1. C++ GUI Qt4学习笔记01

    C++ GUI Qt4学习笔记01   qtc++signalmakefile文档平台 这一章介绍了如何把基本的C++只是与Qt所提供的功能组合起来创建一些简单的图形用户界面应用程序. 引入两个重要概 ...

  2. C++ GUI Qt4学习笔记03

    C++ GUI Qt4学习笔记03   qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QM ...

  3. C++ GUI Qt4学习笔记08

    C++ GUI Qt4学习笔记08   qtc++signal图形引擎文档 本章介绍Qt的二维图形引擎,Qt的二维图形引擎是基于QPainter类的.<span style="colo ...

  4. C++ GUI Qt4学习笔记09

    C++ GUI Qt4学习笔记09   qtc++ 本章介绍Qt中的拖放 拖放是一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式.除了剪贴板提供支持外,通常它还提供数据移动和复制的功 ...

  5. C++ GUI Qt4学习笔记05

    C++ GUI Qt4学习笔记05   qtc++正则表达式 QIntValidator           --  只让用户输入整数 QDoubleValidator     --  只让用户输入浮 ...

  6. C++ GUI Qt4学习笔记07

    C++ GUI Qt4   qtc++scrollobject编程 事件(event)是由串口系统或者Qt自身产生的,用以响应所发生的各类事情.当用户按下或者松开键盘或者鼠标上的按键时,就可以产生一个 ...

  7. 【QT】C++ GUI Qt4 学习笔记1

    Find对话框实现 平台 Qt5.3.2 MinGW4.8.2 注意创建时用QDialog finddialog.h #ifndef FINDDIALOG_H #define FINDDIALOG_H ...

  8. 【QT】C++ GUI Qt4 学习笔记2

    Go To Cell 利用QT Desinger做好界面后加入的代码有 gotocelldialog.h #ifndef GOTOCELLDIALOG_H #define GOTOCELLDIALOG ...

  9. 【QT】C++ GUI Qt4 学习笔记3

    菜单界面的实现. 看书上第三章,好长,好多代码.我敲了半天,想看看效果,结果却显示不出来.仔细一看,发现spreadsheet的实现在第四章.郁闷.... 又到官网上下代码,结果居然不能运行.难道是因 ...

随机推荐

  1. 使用MVVM框架(avalonJS)进行快速开发

    背景 在运营活动开发中,因为工作的重复性很大,同时往往开发时间短,某些情况下也会非常紧急,导致了活动开发时间被大大压缩,同时有些活动逻辑复杂,数据或者状态变更都需要手动渲染,容易出错,正是因为这些问题 ...

  2. ie8及其以下浏览器的document.getElementsByClassName兼容性问题

    使用JavaScript访问DOM的一个重大问题是,此过程需要一种通过元素类名称来选择类的类函数,对DOMContentReady,这种类函数的缺失导致开发人员需要自己编写自定义脚本业执行上述任务,许 ...

  3. PHP如何释放内存之unset销毁变量并释放内存详解

    PHP的unset()函数用来清除.销毁变量,不用的变量,我们可以用unset()将它销毁.但是某些时候,用unset()却无法达到销毁变量占用的内存!我们先看一个例子: <?php $s = ...

  4. C语言的源程序字符集与执行字符集

    我们程序文件的字符集就是我们写出来的.c扩展名的文件的字符集,这里用的是系统默认的 ANSI 字符集,如下图: 上面的字符集我们不关心,我们关心的是 源程序的字符集 和程序的 执行字符集 ,源程序的字 ...

  5. CoreGraphics QuartzCore CGContextTranslateCTM 用法

      原点是: 左下角 旋转: 逆时针 位移: 右上为正, 左下为负 CGContextTranslateCTM CGContextRotateCTM CGContextScaleCTM 而且, 以上几 ...

  6. iTool拷贝app到电脑上

    iTool拷贝app到电脑上 方法一. iTool找到你的app, 归档在桌面, 桌面就生成了ipa, 其实ipa是一个压缩包, 使用解压软件解压之后 生成Payload文件夹, 点开就可以看到Clo ...

  7. Android学习笔记(二)——探究一个活动

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 活动(Activity)是最容易吸引到用户的地方了,它是一种可以包含用户界面的组件,主要用于和用户进行交互.一 ...

  8. 如何使用coding.net

        由于我有一位十分聪明能干的室友会使用coding.net,于是我决定奉献一下室友的智慧,告诉大家如何使用conding.net上交作业.(如有说错的地方希望大家可以指出来) 首先登陆codin ...

  9. Linux下编译安装Apache Http Server

    Linux下编译安装Apache Http Server [TOC] 1.下载httpd-2.4.12.tar.bz2 wget http://mirror.bit.edu.cn/apache/htt ...

  10. phpcms--模型管理,推荐位管理,类别管理

    phpcms的默认设置不一定能满足需求,这个时候必须启用[模型管理],[推荐位管理],[类别管理]三个高级功能 为什么需要使用这些功能呢,因为后台添加内容的时候需要不同的模型 而模型通过什么来展现呢, ...