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

时间:2022-12-26 11:55:41

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

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

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