17 QVariant evalTerm(const QString &str, int &pos) const;
18 QVariant evalFactor(const QString &str, int &pos) const;
19 mutable QVariant cachedValue;
20 mutable bool cacheIsDirty;
21 };
22 #endif
Класс Cell
расширяет QTableWidgetltem, добавляя две закрытые переменные:• переменная cachedValue
кэширует значение ячейки в виде значения типа QVariant;• переменная cacheIsDirty
принимает значение true, если кэшируемое значение устарело.Мы используем QVariant,
поскольку некоторые ячейки имеют тип числа двойной точности double, а другие имеют тип строки QString.При объявлении переменных cachedValue
и cacheIsDirty используется ключевое слово mutable языка С++. Это позволяет нам модифицировать эти переменные в функциях с модификатором const. Мы могли бы поступить по-другому и заново выполнять расчет при каждом вызове функции text, но эта неэффективность будет не оправдана.Следует отметить, что в определении класса не используется макрос Q_OBJECT.
Класс Cell является «чистым» классом С++, который не имеет сигналов и слотов. На самом деле из-за того, что QTableWidgetltem не является наследником QObject, мы не можем использовать в Cell как таковые сигналы и слоты. Классы элементов Qt не наследуют QObject, чтобы свести к минимуму затраты на их обработку. Если сигналы и слоты необходимы, они могут быть реализованы в виджете, содержащем элементы, или (в виде исключения) при помощи множественного наследования класса QObject.Теперь мы перейдем к написанию cell.cpp:
01 #include
02 #include "cell.h"
03 Cell::Cell
04 {
05 setDirty;
06 }
В конструкторе нам необходимо установить признак «dirty» («грязный») только для кэша. Передавать родительский объект нет необходимости; когда делается вставка ячейки в QTableWidget
с помощью setItem, QTableWidget автоматически станет ее владельцем.Каждый элемент QTableWidgetltem
может иметь некоторые данные — до одного типа QVariant на каждую «роль» данных. Наиболее распространенными ролями являются Qt::EditRole и Qt::DisplayRole (роль правки и роль отображения). Роль правки используется для данных, которые должны редактироваться, а роль отображения — для данных, которые должны отображаться на экране. Часто обе роли используются для одних и тех же данных, однако в Cell роль правки соответствует формуле ячейки, а роль отображения — значению ячейки (результату вычисления формулы).02 QTableWidgetltem *Cell::clone const
03 {
04 return new Cell(*this);
05 }
Функция clone
вызывается в QTableWidget, когда необходимо создать новую ячейку, например когда пользователь начинает вводить данные в пустую ячейку, которая до сих пор не использовалась. Переданный функции QTableWidget::setItemPrototype экземпляр является дубликатом. Поскольку для копирования Cell можно ограничиться функцией—членом, мы полагаемся на используемый по умолчанию конструктор копирования, автоматически создаваемый С++ при создании экземпляров новых ячеек Cell в функции clone.06 void Cell::setFormula(const QString &formula)
07 {
08 setData(Qt::EditRole, formula);
09 }
Функция setFormula
задает формулу ячейки. Это просто удобная функция для вызова setData с указанием роли правки. Она вызывается из функции Spreadsheet::setFormula.10 QString Cell::formula const
11 {
12 return data(Qt::EditRole).toString;
13 }
Функция formula
вызывается из Spreadsheet::formula. Подобно setFormula этой функцией удобно пользоваться на этот раз для получения данных EditRole заданного элемента.14 void Cell::setData(int role, const QVariant &value)
15 {
16 QTableWidgetltem::setData(role, value);
17 if (role == Qt::EditRole)
18 setDirty;
19 }
Если мы имеем новую формулу, мы устанавливаем cacheIsDirty
на значение true, чтобы обеспечить перерасчет ячейки при последующем вызове text.