Разработка графического интерфейса с помощью библиотеки Qt3 | ||
---|---|---|
Пред. | Глава 4. Реализация функциональности приложения. | След. |
Теперь перейдем к рассмотрению реализации слотов меню Tools и Options.
Рисунок 4.9. Меню Tools и Options.
void Spreadsheet::recalculate()
{
int row;
for (row = 0; row < NumRows; ++row) {
for (int col = 0; col < NumCols; ++col) {
if (cell(row, col))
cell(row, col)->setDirty();
}
}
for (row = 0; row < NumRows; ++row) {
for (int col = 0; col < NumCols; ++col) {
if (cell(row, col))
updateCell(row, col);
}
}
}
Слот recalculate() соответствует
пункту меню Tools|Recalculate. Он,
кроме того, в случае необходимости, вызывается программой
автоматически.В первой группе циклов все ячейки помечаются, вызовом setDirty(), как требующие пересчета. В результате, когда в следующий раз QTable вызовет метод text() ячейки Cell, то ее содержимое будет пересчитано.
Вторая группа циклов выполняет updateCell() каждой ячейки, чтобы перерисовать ее. В результате, QTable вызовет метод text() ячейки Cell, чтобы получить ее значение, а поскольку все ячейки были помечены вызовом setDirty(), то будет выполнен пересчет.
void Spreadsheet::setAutoRecalculate(bool on)
{
autoRecalc = on;
if (autoRecalc)
recalculate();
}
Слот setAutoRecalculate()
соответствует пункту меню Options|Auto-recalculate. Если этот флаг включен, то
как только в таблице появляются какие либо изменения, выполняется
автоматический пересчет всех ячеек таблицы. В этом случае,
recalculate() вызывается из somethingChanged().
void Spreadsheet::sort(const SpreadsheetCompare &compare)
{
vector<QStringList> rows;
QTableSelection sel = selection();
int i;
for (i = 0; i < sel.numRows(); ++i) {
QStringList row;
for (int j = 0; j < sel.numCols(); ++j)
row.push_back(formula(sel.topRow() + i,
sel.leftCol() + j));
rows.push_back(row);
}
stable_sort(rows.begin(), rows.end(), compare);
for (i = 0; i < sel.numRows(); ++i) {
for (int j = 0; j < sel.numCols(); ++j)
setFormula(sel.topRow() + i, sel.leftCol() + j,
rows[i][j]);
}
clearSelection();
somethingChanged();
}
Сортировка выполняется на выделенной области и переупорядочивает
строки в соответствии с заданными ключами и порядком сортировки,
хранящимися в объекте compare. Функция
сортировки представляет каждую строку таблицы в виде QStringList, а выделенную область -- как массив строк.
Класс vector<?> -- это стандартный
класс C++, мы подробно опишем его в Главе 11.
Для простоты будем выполнять сортировку по формулам, а не по значениям.
Рисунок 4.10. Сохранение выделенной области в виде массива строк.
Рисунок 4.11. Перемещение отсортированных данных обратно в таблицу.
В spreadsheet.h, класс SpreadsheetCompare определен как:
class SpreadsheetCompare
{
public:
bool operator()(const QStringList &row1,
const QStringList &row2) const;
enum { NumKeys = 3 };
int keys[NumKeys];
bool ascending[NumKeys];
};
Это особый класс, поскольку он реализует оператор (), что позволяет использовать его так, как будто это
обычная функция. Такие классы называют функторами
(functor).
Чтобы до конца понять принцип работы функторов, рассмотрим простой
пример:
class Square
{
public:
int operator()(int x) const { return x * x; }
};
Класс Square реализует единственную
функцию -- operator()(int), которая
возвращает квадрат входного аргумента. Такое именование функции, а
скажем не compute(int), дает нам
возможность использовать экземпляр класса Square как обычную функцию:
Square square;
int y = square(5);
Теперь вернемся к классу SpreadsheetCompare:
QStringList row1, row2;
SpreadsheetCompare compare;
...
if (compare(row1, row2)) {
// row1 меньше чем row2
}
Отсюда видно, что объект compare может
использоваться как обычная функция compare(). Дополнительно, он имеет доступ к
параметрам сортировки, которые хранятся в виде переменных-членов.Альтернативный подход вынудил бы нас хранить параметры сортировки в глобальных переменных и использовать обычную функцию сравнения. Это очень неэлегантное решение, которое может породить трудноуловимые ошибки. Функторы -- это более мощная идиома взаимодействия с шаблонными функциями, такими как stable_sort(). Ниже приводится реализация функции, которая сравнивает две строки таблицы:
bool SpreadsheetCompare::operator()(const QStringList &row1,
const QStringList &row2) const
{
for (int i = 0; i < NumKeys; ++i) {
int column = keys[i];
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 -- в противном случае. Стандартная функция
stable_sort() использует результат
сравнения для выполнения сортировки.Массивы keys и ascending, заполняются внутри функции MainWindow::sort() (описаной в Главе 2). Каждый ключ сортировки -- это индекс столбца или -1 (в случае "None").
Сравнению подвергаются части строк, соответствующие ячейкам, заданным ключами сортировки, с учетом порядка сортировки. В зависимости от того были найдены отличия или нет -- возвращается значение true или false. Если строки равны, то возвращается false.
На этом мы завершаем рассмотрение класса Spreadsheet. В следующем разделе мы обсудим класс Cell. Он используется для хранения формулы и реализует свой метод text(), который используется для получения значения ячейки.
Пред. | В начало | След. |
Реализация меню Edit. | На уровень выше | Создание дочернего класса от QTableItem. |