Ниже приводится реализация функции, которая применяется для сравнения двух строк электронной таблицы:
01 bool SpreadsheetCompare::operator()(const QStringList &row1,
02 const QStringList &row2) const
03 {
04 for (int i = 0; i < KeyCount; ++i) {
05 int column = keys[i];
06 if (column != -1) {
07 if (row1[column] != row2[column]) {
08 if (ascending[i]) {
09 return row1[column] < row2[column];
10 } else {
11 return row1[column] > row2[column];
12 }
13 }
14 }
15 }
16 return false;
17 }
Этот оператор возвращает true, если первая строка меньше второй; в противном случае он возвращает false. Функция qStableSort() для выполнения сортировки использует результат этой функции.
Массивы keys и ascending объекта SpreadsheetCompare заполняются при работе функции MainWindow::sort() (она приводится в главе 2). Каждый ключ содержит индекс столбца или имеет значение —1 («None» — нет значения).
Мы сравниваем значения соответствующих ячеек двух строк, учитывая порядок ключей сортировки. Как только оказывается, что они различны, мы возвращаем соответствующее значение: true или false. Если все значения оказываются равными, мы возвращаем false. При совпадении значений функция qStableSort() сохраняет порядок до сортировки; если строка row1 располагалась первоначально перед строкой row2 и ни одна из них не оказалась «меньше другой», то в результате строка row1 по-прежнему будет предшествовать строке row2. Именно этим функция qStableSort() отличается от своего нестабильного «родственника» qSort().
Теперь мы закончили класс Spreadsheet. В следующем разделе мы рассмотрим класс Cell. Этот класс применяется для хранения формул ячеек и обеспечивает переопределение функции QTableWidgetltem::data(), которая вызывается в Spreadsheet через функцию QTableWidgetItem::text() для отображения результата вычисления формулы ячейки.
Создание подкласса QTableWidgetltem
Класс Cell наследует QTableWidgetltem. Этот класс спроектирован для удобства работы с Spreadsheet, но он не имеет никаких особых связей с данным классом электронной таблицы и теоретически может применяться для любого объекта QTableWidget. Ниже приводится заголовочный файл:
01 #ifndef CELL_H
02 #define CELL_H
03 #include <QTableWidgetItem>
04 class Cell : public QTableWidgetltem
05 {
06 public:
07 Cell();
08 QTableWidgetltem *clone() const;
09 void setData(int role, const QVariant &value);
10 QVariant data(int role) const;
11 void setFormula(const QString &formula);
12 QString formula() const;
13 void setDirty();
14 private:
15 QVariant value() const;
16 QVariant evalExpression(const QString &str, int &pos) const;
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.