【QT】C++ GUI Qt4 学习笔记4

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

我决定先直接跳到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(1);
        } else if (formulaStr.startsWith(‘=‘)) { // =开始返回公式
            cachedValue = Invalid;
            QString expr = formulaStr.mid(1);
            expr.replace(" ", "");
            expr.append(QChar::Null);

            int pos = 0;
            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[0].toUpper().unicode() - ‘A‘;
            int row = token.mid(1).toInt() - 1;

            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()实现后终于可以显示一个像样子的界面了。虽然没有实现功能,但也不错了。

时间: 2024-10-12 10:12:18

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

C++ GUI Qt4学习笔记08

C++ GUI Qt4学习笔记08 qtc++signal图形引擎文档 [html] view plaincopy 本章介绍Qt的二维图形引擎,Qt的二维图形引擎是基于QPainter类的.<span style="color:#ff0000;">QPainter既可以绘制几何图形(点.线.矩形等),也可以绘制像素映射.图像和文字.此外QPainter还支持一些高级特性,例如反走样.像素混合.渐变填充和矢量路径等.QPainter也支持线性变换,例如平移.旋转.错切和缩放.

C++ GUI Qt4学习笔记01

C++ GUI Qt4学习笔记01 qtc++signalmakefile文档平台 这一章介绍了如何把基本的C++只是与Qt所提供的功能组合起来创建一些简单的图形用户界面应用程序. 引入两个重要概念:一个是“信号和槽”,另一个是“布局”. 窗口部件(widget)是用户界面的一个可视化元素,相当于windows系统中的“控件”和“容器”.任意窗口部件都可以用作窗口. 1.1Hello Qt 正确安装Qt4开发环境,创建工程目录hello,源代码文件名为hello.cpp,进入hello目录 (1

C++ GUI Qt4学习笔记05

C++ GUI Qt4学习笔记05 qtc++正则表达式 QIntValidator           --  只让用户输入整数 QDoubleValidator     --  只让用户输入浮点数 QRegExpValidator    --  只让用户按照正则表达式定义好的样式进行输入 本章讲解如何使用Qt开发自定义窗口部件. 通过对一个已经存在的Qt窗口部件进行子类化或者直接对QWidget进行子类化,就可以创建自定义窗口部件. 集成自定义窗口到Qt设计师中,这样就可以像使用内置的Qt窗

C++ GUI Qt4学习笔记09

C++ GUI Qt4学习笔记09 qtc++ 本章介绍Qt中的拖放 拖放是一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式.除了剪贴板提供支持外,通常它还提供数据移动和复制的功能. QMimeData是一个可以提供不同格式数据的类. 9.1使拖放生效 拖放操作有两个动作:拖动和放下.Qt窗口部件可以作为拖动点.放下点或者同时作为拖动点和放下点. 9.2支持自定义拖动类型 9.3剪贴板处理技术 多数应用程序都通过某一种或者几种方式来使用Qt的内置剪贴板处理技术. C++ GUI

C++ GUI Qt4学习笔记07

C++ GUI Qt4 qtc++scrollobject编程 事件(event)是由串口系统或者Qt自身产生的,用以响应所发生的各类事情.当用户按下或者松开键盘或者鼠标上的按键时,就可以产生一个键盘或者鼠标事件:当某个窗口第一次显示的时候,就会产生一个绘制事件.用来告知窗口需要重绘制它本身,从而使得该窗口可见. 使用Qt进行编程开发时,基本不需要考虑事件,Qt窗口部件都会发射信号.但是当我们需要编写自己的自定义窗口部件,或者是当我们希望改变已经存在的Qt窗口部件的行为时,事件就变得非常有用了.

C++ GUI Qt4学习笔记03

C++ GUI Qt4学习笔记03 qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QMainWindow可以创建一个窗口 图形用户界面(GUI)应用程序通常会使用很多的图片,最常见的为应用程序提供图片的方法是使用Qt的资源机制(resource mechanism) 使用Qt资源系统,必须创建一个资源文件,并且在识别该资源文件的.pro文件中添加一行代码. RESOURCES = spr

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

Find对话框实现 平台 Qt5.3.2 MinGW4.8.2 注意创建时用QDialog finddialog.h #ifndef FINDDIALOG_H #define FINDDIALOG_H #include <QDialog> #include <QLabel> #include <QCheckBox> #include <QLineEdit> #include <QPushButton> #include <QLayout&g

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

Go To Cell 利用QT Desinger做好界面后加入的代码有 gotocelldialog.h #ifndef GOTOCELLDIALOG_H #define GOTOCELLDIALOG_H #include <QDialog> #include "ui_gotocelldialog.h" class GoToCellDialog : public QDialog, public Ui::GoToCellDialog { Q_OBJECT public: Go

【QT】C++ GUI Qt4 学习笔记5

折腾了好几天,终于把这本书的第三章和第四章给看了个大概. 里面的函数调用关系可谓是复杂. 整理了一部分的函数关系如下: cell关系清理 data(role) 返回应该显示的值 或者对齐方式 或者公式 ->value() 单元格的值,如果是旧的就重新获得 值给cachedValue并返回 对输入的以‘开头和 = 开头的做特殊处理 ->formula() 获得单元格的Edit role ->data(Edit role) ->evalExpression() 计算表达式的值 setF