Выбрать главу

27 new TrackDelegate(Track_Duration, this));

28 trackTableView->setSelectionMode(QAbstractItemView::SingleSelection);

29 trackTableView->setSelectionBehavior(QAbstractItemView::SelectRows);

Для дорожек мы собираемся выводить на экран только названия песен и их длительности, поэтому достаточно использовать модель QSqlTableModel. (Поля id и cdid, используемые в рассмотренном ниже слоте currentCdChanged(), не выводятся на экран.) Единственно, на что следует обратить внимание в этой части программного кода, — это использование разработанного в главе 10 класса TrackDelegate, показывающего времена дорожек в виде «минуты:секунды» и позволяющего их редактировать с помощью удобного класса QTimeEdit.

Создание представлений и кнопок, их компоновка и соединения сигнал—слот не содержат ничего особенного, поэтому из оставшейся части конструктора мы покажем только несколько не совсем очевидных соединений.

30 …

31 connect(cdTableView->selectionModel(),

32 SIGNAL(currentRowChanged(const QModelIndex &,

33 const QModelIndex &)),

34 this, SLOT(currentCdChanged(const QModelIndex &)));

35 connect(cdModel, SIGNAL(beforeInsert(QSqlRecord &)),

36 this, SLOT(beforeInsertCd(QSqlRecord &)));

37 connect(trackModel, SIGNAL(beforeInsert(QSqlRecord &)),

38 this, SLOT(beforeInsertTrack(QSqlRecord &)));

39 connect(trackModel, SIGNAL(rowsInserted(

40 const QModelIndex &, int, int)),

41 this, SLOT(refreshTrackViewHeader()));

42 …

43 }

Первое соединение необычно, поскольку вместо связывания виджета мы связываем модель выборки. Класс QItemSelectionModel используется для отслеживания выборок в представлениях. Связанный с моделью выборки представления таблицы, наш слот currentCdChanged() будет вызываться при всяком перемещении пользователя от одной записи к другой.

01 void MainForm::currentCdChanged(const QModelIndex &index)

02 {

03 if (index.isValid()) {

04 QSqlRecord record = cdModel->record(index.row());

05 int id = record.value("id").toInt();

06 trackModel->setFilter(QString("cdid = %1").arg(id));

07 } else {

08 trackModel->setFilter("cdid = -1");

09 }

10 trackModel->select();

11 refreshTrackViewHeader();

12 }

Этот слот вызывается при каждой смене текущего компакт-диска. Это происходит при переходе пользователя к другому компакт-диску (щелкая мышкой по соответствующей строке или используя клавиши Up и Down). Если компакт-диск недействителен (например, если вообще нет компакт-дисков или был вставлен новый компакт-диск, или текущий компакт-диск был только что удален), мы устанавливаем идентификатор cdid таблицы дорожек track в значение —1 (недействительный идентификатор, которому не соответствует никакая запись).

Затем, установив фильтр, мы выбираем ему соответствующие записи дорожек. Функция refreshTrackViewHeader() будет рассмотрена вскоре.

01 void MainForm::addCd()

02 {

03 int row = 0;

04 if (cdTableView->currentIndex().isValid())

05 row = cdTableView->currentIndex().row();

06 cdModel->insertRow(row);

07 cdModel->setData(cdModel->index(row, Cd_Year),

08 QDate::currentDate().year());

09 QModelIndex index = cdModel->index(row, Cd_Title);

10 cdTableView->setCurrentIndex(index);

11 cdTableView->edit(index);

12 }

Когда пользователь нажимает клавишу Add CD (добавить компакт-диск), в таблицу cdTableView вставляется новая пустая строка и мы переходим в режим редактирования. Мы также устанавливаем значение по умолчанию для поля year. В этот момент пользователь может редактировать запись, заполняя пустые поля и выбирая артиста из выпадающего списка, который автоматически выдается моделью QSqlRelationalTableModel благодаря вызову setRelation(), a также изменяя год, если не подходит значение по умолчанию. Если пользователь подтверждает вставку нажатием клавиши Enter, запись вставляется. Пользователь может отменить вставку, нажав клавишу Esc.

01 void MainForm::beforeInsertCd(QSqlRecord &record)

02 {

03 record.setValue("id", generateId("cd"));

04 }

Этот слот вызывается, когда cdModel генерирует свой сигнал beforeInsert(). Мы используем его для заполнения поля id, как это делалось при вставке нового артиста, и здесь применимо то же самое предостережение: данная операция должна выполняться в рамках транзакции, а в идеальном случае должно использоваться зависимое от базы данных средство создания идентификаторов (например, автоматическая генерация идентификаторов).

01 void MainForm::deleteCd()