Реализация пунктов меню Инструменты и Настройки

   
На этом шаге рассмотрим реализацию пунктов меню Инструменты и Настройки в приложении Электронная таблица.

   
Вид меню Tools и Options (Инструменты и Настройки), приведен на рисунке 1.


Рис.1. Меню Tools и Options приложения Электронная таблица

   
Слот recalculate() соответствует пункту меню Tools | Recalculate (Инструменты | Пересчитать). Он также вызывается в Электронной таблице автоматически по мере необходимости.

void Spreadsheet::recalculate()
{
    /*выполняем цикл по всем ячейкам и вызываем функцию setDirty(),
    которая помечает каждую из них для перерасчета значения. Следующий раз, когда
    QTableWidget для получения отображаемого в электронной таблице значения вызовет
    text() для некоторой ячейки Cell, значение этой ячейки будет пересчитано*/
    for (int row = 0; row < RowCount; ++row) {
        for (int column = 0; column < ColumnCount; ++column) {
            if (cell(row, column))
                cell(row, column)->setDirty();
        }
    }
    /*вызываем для области отображения функцию update() перерисовки
    всей электронной таблицы. При этом используемый в QTableWidget программный
    код по перерисовке вызывает функцию text() для каждой видимой ячейки для
    получения отображаемого значения. Поскольку функция setDirty() вызывалась
    нами для каждой ячейки, в вызовах text() будет использовано новое
    рассчитанное значение. В этом случае может потребоваться расчет невидимых
    ячеек, который будет проводиться до тех пор, пока не будут рассчитаны
    все ячейки, влияющие на правильное отображение текста в перерассчитанной
    области отображения. Этот расчет выполняется в классе Cell*/
    viewport()->update();
}

   
Слот setAutoRecalculate() соответствует пункту меню Options | Auto-Recalculate (Настройки | Автоподсчет). Если эта опция включена, мы сразу же пересчитаем всю электронную таблицу и будем уверены, что она показывает обновленные значения; впоследствии функция recalculate() будет автоматически вызываться из somethingChanged().

void Spreadsheet::setAutoRecalculate(bool recalc)
{
    autoRecalc = recalc;
    if (autoRecalc)
        recalculate();
}

   
Нам не нужно реализовывать специальную функцию для пункта меню Options | Show Grid (Настройки | Показать сетку), поскольку в QTableWidget уже содержится слот setShowGrid(), который наследуется от QTableView. Остается только реализовать функцию Spreadsheet::sort(), которая вызывается из MainWindow::sort():

void Spreadsheet::sort(const SpreadsheetCompare &compare)
{
    /*сортировка работает на текущей выделенной области и переупорядочивает
    строки в соответствии со значениями ключей порядка сортировки и хранящимися
    в объекте compare. Представляем каждую строку данных в QStringList,
    а выделенную область храним в виде списка строк*/
    QList<QStringList> rows;
    QTableWidgetSelectionRange range = selectedRange();
    int i;
    for (i = 0; i < range.rowCount(); ++i) {
        QStringList row;
        for (int j = 0; j < range.columnCount(); ++j)
            row.append(formula(range.topRow() + i,
                               range.leftColumn() + j));
        rows.append(row);
    }
    /*используем алгоритм Qt qStableSort() и для простоты сортируем по выражениям
    формул, а не по их значениям. В качестве аргументов функции qStableSort()
    используется итератор начала, итератор конца и функция сравнения. Функция
    сравнения имеет два аргумента (оба имеют тип QStringLists), и она возвращает
    true, когда первый аргумент «больше, чем» второй аргумент, и false в
    противном случае. Передаваемый как функция сравнения объект compare
    фактически не является функцией, но может использоваться и в таком качестве*/
    qStableSort(rows.begin(), rows.end(), compare);
    //помещаем данные обратно в таблицу
    for (i = 0; i < range.rowCount(); ++i) {
        for (int j = 0; j < range.columnCount(); ++j)
            setFormula(range.topRow() + i, range.leftColumn() + j,
                       rows[i][j]);
    }
    //сбрасываем выделение области 
    clearSelection();
    //и вызываем функцию somethingChanged()
    somethingChanged();
}

   
Класс SpreadsheetCompare в spreadsheet.h определен следующим образом:

class SpreadsheetCompare
{
public:
    bool operator()(const QStringList &row1,
                    const QStringList &row2) const;

    enum { KeyCount = 3 };
    int keys[KeyCount];
    bool ascending[KeyCount];
};

   
Класс SpreadsheetCompare является специальным классом, реализующим оператор (). Это позволяет нам применять этот класс в качестве функции. Такие классы называются объектами функций или функторами (functors).

   
Для лучшего понимания работы функторов мы сначала разберем простой пример.

class Square
{
  public:
   int operator()(int x) const { return x * x; }
};

   
Класс Square содержит одну функцию, operator()(int), которая возвращает квадрат переданного ей значения параметра. Обозначая функцию в виде operator()(int), а не в виде, например, compute(int), мы получаем возможность применения объекта типа Square как функции:

Square square;
int у = square(5); //у равно 25

   
Теперь рассмотрим пример с применением объекта SpreadsheetCompare.

QStringList rowl, row2;
QSpreadsheetCompare compare;
if (compare(row1, row2)) {
    // строка rowl меньше, чем row2
}

   
Объект compare можно использовать так же, как если бы он был обычной функцией comparе(). Кроме того, он может быть реализован таким образом, что будет осуществлять доступ ко всем ключам сортировки и всем признакам порядка сортировки, которые хранятся в переменных-членах класса.

   
Можно использовать другой подход, когда ключи сортировки и признаки порядка сортировки хранятся в глобальных переменных и используется функция обычного типа comparе(). Однако связь через глобальные переменные выглядит неизящно и может быть причиной тонких ошибок. Функторы представляют собой более мощное средство связи для таких функций-шаблонов, как qStableSort().

   
Ниже приводится реализация функции, которая применяется для сравнения двух строк электронной таблицы.

bool SpreadsheetCompare::operator()(const QStringList &row1,
                                    const QStringList &row2) const
{
    /*массивы keys и ascending объекта SpreadsheetCompare заполняются
    при работе функции MainWindow::sort(). Каждый ключ содержит индекс столбца
    или имеет значение -1 ("None" - нет значения)*/
    for (int i = 0; i < KeyCount; ++i) {
        int column = keys[i];
        /*сравниваем значения соответствующих ячеек двух строк, учитывая
        порядок ключей сортировки. Как только оказывается, что они различны, мы
        возвращаем соответствующее значение: true или false. Если все значения
        оказываются равными, мы возвращаем false. При совпадении значений функция
        qStableSort() coxpaняет порядок до сортировки; если строка rowl
        располагалась первоначально перед строкой row2 и ни одна из них не
        оказалась "меньше другой", то в результате строка rowl
        по-прежнему будет предшествовать строке row2. Именно этим функция
        qStableSort() отличается от своего нестабильного "родственника" qSort()*/
        if (column != -1) {
            if (row1[column] != row2[column]) {
                if (ascending[i]) {
                    return row1[column] < row2[column];
                } else {
                    return row1[column] > row2[column];
                }
            }
        }
    }
    return false;
}

   
Этот оператор возвращает true, если первая строка меньше второй; в противном случае он возвращает false. Функция qStableSort() для выполнения сортировки использует результат этой функции.

   
На следующем шаге рассмотрим создание подкласса QTableWidgetItem.

Предыдущий шаг
Содержание
Следующий шаг



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий