Функция createIndex()
используется для формирования индекса модели. Она нужна для получения индекса модели элемента, который расположен по другую сторону от главной диагонали и который соответствует элементу с установленным значением, поскольку оба элемента должны показывать одинаковые данные. Функция createIndex() принимает сначала строку и затем столбец; здесь мы передаем параметры в обратном порядке, чтобы получить индекс модели элемента, расположенного по другую строну диагонали напротив элемента, определенного индексом index.Мы генерируем сигнал dataChanged()
с указанием индекса модели элемента, который изменился. Эта функция принимает два индекса модели, поскольку возможна ситуация, когда изменения относятся к некоторой прямоугольной области, охватывающей несколько строк и столбцов, поэтому передаются индекс верхнего левого угла и индекс нижнего правого угла этой области. Генерируем также сигнал dataChanged() для индекса противоположного элемента, чтобы представление обновило его отображение на экране. Наконец, мы возвращаем true или false, указывая на успешность или неуспешность редактирования.01 Qt::ItemFiags CityModel::flags(const QModelIndex &index) const
02 {
03 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
04 if (index.row() != index.column())
05 flags |= Qt::ItemIsEditable;
06 return flags;
07 }
Функция flags()
используется моделью для того, чтобы можно было сообщить о допустимых действиях с элементом (например, допускает ли он редактирование). По умолчанию эта функция для модели QAbstractTableModel возвращает Qt::ItemIsSelectable | Qt::ItemIsEnabled. Мы добавляем флажок Qt::ItemIsEditable для всех элементов, кроме расположенных по диагонали (которые всегда равны 0).01 void CityModel::setCities(const QStringList &cityNames)
02 {
03 cities = cityNames;
04 distances.resize(cities.count() * (cities.count() - 1) / 2);
05 distances.fill(0);
06 reset();
07 }
Если задан новый список городов, мы устанавливаем закрытую переменную типа QStringList
на новый список, изменяем размеры и очищаем вектор расстояний, а затем вызываем функцию QAbstractItemModel::reset(), чтобы уведомить все представления о необходимости обновления всех видимых элементов.01 int CityModel::offsetOf(int row, int column) const
02 {
03 if (row < column)
04 qSwap(row, column);
05 return (row * (row - 1) / 2) + column;
06 }
Закрытая функция offsetOf()
вычисляет индекс заданной пары городов для вектора расстояний distances. Например, предположим, что мы имеем города А, В, С и D, и пользователь обновляет элемент со строкой 3 и столбцом 1, т. е. (B, D). Тогда индекс вектора расстояний будет равен 3 × (3 — 1) / 2 + 1 = 4. Если бы пользователь вместо этого изменил элемент со строкой 1 и столбцом 3, т.е. (D, В), благодаря применению функции qSwap(), выполнялись бы точно такие же вычисления и возвращалось бы то же самое значение.Рис. 10.13. Структуры данных cities и distances и табличная модель.
Последний пример в данном разделе представляет собой модель, которая показывает дерево грамматического разбора заданного регулярного выражения. Регулярное выражение состоит из одного или нескольких термов, разделяемых символами '|'. Так, регулярное выражение «alpha|bravo|charlie» содержит три терма. Каждый терм представляет собой последовательность из одного или нескольких факторов: например, терм «bravo» состоит из пяти факторов (каждая буква является фактором). Факторы могут состоять из атома и необязательного квантификатора (quantifier), например '*', '+' и '?'. Поскольку регулярные выражения могут иметь подвыражения, заключенные в скобки, они могут быть представлены рекурсивными деревьями грамматического разбора.
Регулярное выражение, показанное на рис. 10.14, «ab|(cd)?e» означает, что за 'a' следует 'b' или допускается два варианта: за 'c' идет 'd' и затем 'e' или просто имеется 'e'. Поэтому подойдут строки «ab» и «cde», но не подойдут строки «bc» или «cd».
Рис. 10.14. Приложение Парсер регулярных выражений.
Приложение Парсер регулярных выражений (Regexp Parser) состоит из четырех классов: