Жасмин Бланшет - QT 4: программирование GUI на С++
Ниже приводится пример использования QSqlTableModel для выполнения команды SELECT:
QSqlTableModel model;
model.setTable("cd");
model.setFilter("year >= 1998");
model.select();
Это эквивалентно запросу
SELECT * FROM cd WHERE year >= 1998
Просмотр результирующего набора выполняется путем получения заданной записи функцией QSqlTableModel::record() и доступа к отдельным полям с помощью функции value():
for (int i = 0; i < model.rowCount(); ++i) {
QSqlRecord record = model.record(i);
QString title = record.value("title").toString();
int year = record.value("year").toInt();
cerr << qPrintable(title) << ": " << year << endl;
}
Функция QSqlRecord::value() принимает либо имя поля, либо индекс поля. При работе с большими наборами данных рекомендуется задавать поля с помощью их индексов. Например:
int titleIndex = model.record().indexOf("title");
int yearIndex = model.record().indexOf("year");
for (int i = 0; i < model.rowCount(); ++i) {
QSqlRecord record = model.record(i);
QString title = record.value(titleIndex).toString();
int year = record.value(yearIndex).toInt();
cerr << qPrintable(title) << ": " << year << endl;
}
Для вставки записи в таблицу базы данных мы действуем так же, как если бы делали вставку в двумерную модель: сначала вызываем функцию insertRow() для создания новой пустой строки (записи) и затем используем setData() для установки значения каждого столбца (поля записи).
QSqlTableModel model;
model.setTable("cd");
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 0), 113);
model.setData(model.index(row, 1), "Shanghai My Heart");
model.setData(model.index(row, 2), 224);
model.setData(model.index(row, 3), 2003);
model.submitAll();
После вызова submitAll() запись может быть перемещена в другую позицию, зависящую от упорядоченности таблицы. Вызов submitAll() возвратит false, если вставка окажется неудачной.
Важным отличием модели SQL от стандартной модели является необходимость вызова в модели SQL функции submitAll() для записи всех изменений в базу данных
Для обновления записи мы должны сначала установить QSqlTableModel на запись, которую мы хотим модифицировать (например, используя функции select()). Затем мы извлекаем запись, обновляем соответствующие поля и записываем наши изменения обратно в базу данных:
QSqlTableModel model;
model.setTable("cd");
model.setFilter("id = 125");
model.select();
if (model.rowCount() == 1) {
QSqlRecord record = model.record(0);
record.setValue("title", "Melody A.M.");
record.setValue("year", record.value("year").toInt() + 1);
model.setRecord(0, record);
model.submitAll();
}
Если имеется запись, удовлетворяющая заданному фильтру, доступ к ней мы получаем при помощи функции QSqlTableModel::record(). Мы осуществляем наши изменения и вновь записываем в базу данных запись с новыми значениями полей.
Кроме того, обновление можно выполнить при помощи функции setData(), как это делается для модели, отличной от SQL—модели. Для получения доступа к полям записи используются индексы модели с указанием номера строки (записи) и столбца (поля):
model.select();
if (model.rowCount() == 1) {
model.setData(model.index(0, 1), "Melody A.M.");
model.setData(model.index(0, 3),
model.data(model.index(0, 3)).toInt() + 1);
model.submitAll();
}
Удаление записи напоминает ее обновление:
model.setTable("cd");
model.setFilter("id = 125");
model.select();
if (model.rowCount() == 1) {
model.removeRows(0, 1);
model.submitAll();
}
В вызове removeRows() указываются номер строки первой удаляемой записи и количество удаляемых записей. В следующем примере удаляются все записи, удовлетворяющие фильтру:
model.setTable("cd");
model.setFilter("year < 1990");
model.select();
if (model.rowCount() > 0) {
model.removeRows(0, model.rowCount());
model.submitAll();
}
Классы QSqlQuery и QSqlTableModel обеспечивают интерфейс между Qt и базой данных SQL. Используя эти классы, можно создавать формы, представляющие данные пользователям и позволяющие им вставлять, обновлять и удалять записи.
Представление данных в табличной форме
Во многих случаях табличное представление является самым простым представлением набора данных для пользователей. В этом и последующих разделах мы рассмотрим простое приложение CD Collection (Коллекция компакт-дисков), в котором модель QSqlTableModel и ее подкласс QSqlRelationalTableModel используются для просмотра и взаимодействия пользователей с данными, хранимыми в базе данных.
Главная форма показывает представление «master—detail» для компакт-дисков и дорожек текущего компакт-диска (рис. 13.1).
Рис. 13.1. Приложение CD Collection.
В приложении используются три таблицы, определенные следующим образом:
CREATE TABLE artist (
id INTEGER PRIMARY KEY,
name VARCHAR(40) NOT NULL,
country VARCHAR(40));
CREATE TABLE cd (
id INTEGER PRIMARY KEY,
title VARCHAR(40) NOT NULL,
artistid INTEGER NOT NULL,
year INTEGER N0T NULL,
FOREIGN KEY (artistid) REFERENCES artist);
CREATE TABLE track (
id INTEGER PRIMARY KEY,
title VARCHAR(40) NOT NULL,
duration INTEGER NOT NULL,
cdid INTEGER NOT NULL,
FOREIGN KEY (cdid) REFERENCES cd);
Некоторые базы данных не поддерживают внешние ключи. В этом случае мы должны убрать фразы FOREIGN KEY. Пример будет все-таки работать, но база данных не будет поддерживать целостность на уровне ссылок.
Рис. 13.2. Таблицы приложения CD Collection.
В этом разделе мы создадим диалоговое окно, позволяющее пользователю редактировать список артистов, используя простую форму с таблицей. Пользователь может вставлять, обновлять или удалять артистов при помощи кнопок формы. Обновления можно делать напрямую, просто редактируя текст ячеек. Изменения вносятся в базу данных при нажатии пользователем кнопки Enter или при переходе на другую запись.
Рис. 13.3. Диалоговое окно ArtistForm.
Ниже приводится определение класса для диалогового окна ArtistForm:
01 class ArtistForm : public QDialog
02 {
03 Q_OBJECT
04 public:
05 ArtistForm(const QString &name, QWidget *parent = 0);
06 private slots:
07 void addArtist();
08 void deleteArtist();
09 void beforeInsertArtist(QSqlRecord &record);
10 private:
11 enum {
12 Artist_Id = 0,
13 Artist_Name = 1,
14 Artist_Country = 2
15 };
16 QSqlTableModel *model;
17 QTableView *tableView;
18 QPushButton *addButton;
19 QPushButton *deleteButton;
20 QPushButton *closeButton;
21 };
Конструктор этого класса очень похож на конструктор, который использовался бы для создания формы, построенной для модели, отличной от SQL—модели:
01 ArtistForm::ArtistForm(const QString &name, QWidget *parent)
02 : QDialog(parent)
03 {
04 model = new QSqlTableModel(this);
05 model->setTable("artist");
06 model->setSort(Artist_Name, Qt::AscendingOrder);
07 model->setHeaderData(Artist_Name, Qt::Horizontal, tr("Name"));
08 model->setHeaderData(Artist_Country, Qt::Horizontal, tr("Country"));
09 model->select();
10 connect(model, SIGNAL(beforeInsert(QSqlRecord &)),
11 this, SLOT(beforeInsertArtist(QSqlRecord &)));
12 tableView = new QTableView;
13 tableView->setModel(model);
14 tableView->setColumnHidden(Artist_Id, true);
15 tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
16 tableView->resizeColumnsToContents();
17 for (int row = 0; row < model->rowCount(); ++row) {
18 QSqlRecord record = model->record(row);
19 if (record.value(Artist_Name).toString() == name) {
20 tableView->selectRow(row);
21 break;
22 }
23 }
24 …
25 }
Конструктор начинается с создания объекта QSqlTableModel. Мы передаем this в качестве родителя, чтобы владельцем модели стала форма. Нами выбрана сортировка по столбцу 1 (задается константой Artist_Name), который соответствует полю имени. Если бы мы не задали заголовки столбцов, то использовались бы имена полей. Мы предпочитаем их указать, чтобы обеспечить правильный регистр и локализацию.
Затем создается QTableView для визуального отображения модели. Мы не показываем поле id и устанавливаем такую ширину столбцов, которая будет достаточна для размещения в них текста без необходимости вывода многоточия.
Конструктор ArtistForm принимает имя артиста, который будет выбран при выводе на экран диалогового окна. Мы проходим по записям таблицы artist и выбираем этого артиста. Остальная часть программного кода конструктора используется для создания кнопок и подключения к ним слотов, а также для компоновки дочерних виджетов в диалоговом окне.