18 .arg(record.value("name").toString()));
19 }
20 }
Если выделена какая-то запись, мы проверяем наличие компакт-дисков у данного артиста, и если они отсутствуют, мы сразу же удаляем эту запись артиста. В противном случае мы выводим на экран окно с сообщением о причине невыполнения удаления. Строго говоря, здесь следовало бы использовать транзакцию, потому что из программного кода видно, что между вызовами функций cdModel.select() и model->removeRow() у артиста может появиться свой компакт-диск. Транзакция будет рассмотрена в следующем разделе.
Создание форм по технологии «master—detail»
Теперь мы рассмотрим главную форму, которая реализует подход «master—detail». Главный вид представляет собой список компакт-дисков. Вид описания деталей представляет собой список дорожек текущего компакт-диска. Это диалоговое окно является главным окном приложения CD Collection (Коллекция компакт-дисков); оно показано на рис. 13.1.
01 class MainForm : public QWidget
02 {
03 Q_OBJECT
04 public:
05 MainForm();
06 private slots:
07 void addCd();
08 void deleteCd();
09 void addTrack();
10 void deleteTrack();
11 void editArtists();
12 void currentCdChanged(const QModelIndex &index);
13 void beforeInsertCd(QSqlRecord &record);
14 void beforeInsertTrack(QSqlRecord &record);
15 void refreshTrackViewHeader();
16 private:
17 enum {
18 Cd_Id = 0,
19 Cd_Title = 1,
20 Cd_ArtistId = 2,
21 Cd_Year = 3
22 };
23 enum {
24 Track_Id = 0,
25 Track_Title = 1,
26 Track_Duration = 2,
27 Track_CdId = 3
28 };
29 QSqlRelationalTableModel *cdModel;
30 QSqlTableModel *trackModel;
31 QTableView *cdTableView;
32 QTableView *trackTableView;
33 QPushButton *addCdButton;
34 QPushButton *deleteCdButton;
35 QPushButton *addTrackButton;
36 QPushButton *deleteTrackButton;
37 QPushButton *editArtistsButton;
38 QPushButton *quitButton;
39 };
Мы используем для таблицы компакт-дисков cd модель QSqlRelationalTableModel, а не простую модель QSqlTableModel, потому что нам придется работать с внешними ключами. Мы рассмотрим по очереди все функции, начиная с конструктора, который мы разобьем на несколько секций из-за его большого размера.
01 MainForm::MainForm()
02 {
03 cdModel = new QSqlRelationalTableModel(this);
04 cdModel->setTable("cd");
05 cdModel->setRelation(Cd_ArtistId,
06 QSqlRelation("artist", "id", "name"));
07 cdModel->setSort(Cd_Title, Qt::AscendingOrder);
08 cdModel->setHeaderData(Cd_Title, Qt::Horizontal, tr("Title"));
09 cdModel->setHeaderData(Cd_ArtistId, Qt::Horizontal, tr("Artist"));
10 cdModel->setHeaderData(Cd_Year, Qt::Horizontal, tr("Year"));
11 cdModel->select();
Конструктор начинается с настройки модели QSqlRelationalTableModel, которая управляет таблицей cd. Вызов setRelation() указывает модели на то, что ее поле artistid (индекс которого находится в переменной Cd_ArtistId) содержит идентификатор id внешнего ключа из таблицы артистов artist и что вместо идентификаторов необходимо выводить на экран содержимое соответствующего поля name. Если пользователь переходит в режим редактирования этого поля (например, нажимая клавишу F2), модель автоматически выведет на экран поле с выпадающим списком имен всех артистов, и если пользователь выбирает другого артиста, таблица cd будет обновлена.
12 cdTableView = new QTableView;
13 cdTableView->setModel(cdModel);
14 cdTableView->setItemDelegate(new QSqlRelationalDelegate(this));
15 cdTableView->setSelectionMode(QAbstractItemView::SingleSelection);
16 cdTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
17 cdTableView->setColumnHidden(Cd_Id, true);
18 cdTableView->resizeColumnsToContents();
Настройка представления таблицы cd выполняется аналогично тому, что мы уже делали. Единственным существенным отличием является применение QSqlRelationalDelegate вместо делегата по умолчанию. Именно этот делегат обеспечивает работу с внешними ключами.
19 trackModel = new QSqlTableModel(this);
20 trackModel->setTable("track");
21 trackModel->setHeaderData(Track_Title, Qt::Horizontal, tr("Title"));
22 trackModel->setHeaderData(Track_Duration, Qt::Horizontal,
23 tr("Duration"));
24 trackTableView = new QTableView;
25 trackTableView->setModel(trackModel);
26 trackTableView->setItemDelegate(