Представление данных в табличной форме
Представление данных в табличной форме
Во многих случаях табличное представление является самым простым представлением набора данных для пользователей. В этом и последующих разделах мы рассмотрим простое приложение 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 и выбираем этого артиста. Остальная часть программного кода конструктора используется для создания кнопок и подключения к ним слотов, а также для компоновки дочерних виджетов в диалоговом окне.
01 void ArtistForm::addArtist()
02 {
03 int row = model->rowCount();
04 model->insertRow(row);
05 QModelIndex index = model->index(row, Artist_Name);
06 tableView->setCurrentIndex(index);
07 tableView->edit(index);
08 }
Для добавления нового артиста мы вставляем одну пустую строку в конец табличного представления QTableView. Теперь пользователь может вводить имя нового артиста и его страну. Если пользователь подтверждает вставку, нажимая кнопку Enter, генерируется сигнал beforeInsert(), и после этого новая запись вставляется в базу данных.
01 void ArtistForm::beforeInsertArtist(QSqlRecord &record)
02 {
03 record.setValue("id", generateId("artist"));
04 }
В конструкторе мы связываем сигнал модели beforeInsert() с этим слотом. Мы передаем неконстантную ссылку на запись непосредственно перед ее вставкой в базу данных. Здесь мы устанавливаем значение поля id.
Поскольку нам потребуется вызывать функцию generateId() несколько раз, мы определяем ее как inline—функцию в заголовочном файле и включаем ее каждый раз по мере необходимости. Ниже дается простой (и неэффективный) способ ее реализации:
01 inline int generateId(const QString &table)
02 {
03 QSqlQuery query;
04 query.exec("SELECT MAX(id) FROM " + table);
05 int id = 0;
06 if (query.next())
07 id = query.value(0).tolnt() + 1;
08 return id;
09 }
Функция generateId() может гарантированно работать правильно, если она выполняется в рамках контекста одной транзакции соответствующей команды INSERT. Некоторые базы данных поддерживают средство автоматической генерации полей, и обычно значительно лучше использовать предусмотренные в базе данных специальные средства поддержки этой операции.
Удаление — это последняя операция, которую позволяет сделать диалоговое окно ArtistForm. Вместо каскадного удаления (вскоре будет рассмотрено) мы разрешаем удалять артистов только в том случае, если в коллекции нет их компакт-дисков.
01 void ArtistForm::deleteArtist()
02 {
03 tableView->setFocus();
04 QModelIndex index = tableView->currentIndex();
05 if (!index.isValid())
06 return;
07 QSqlRecord record = model->record(index.row());
08 QSqlTableModel cdModel;
09 cdModel.setTable("cd");
10 cdModel.setFilter("artistid = " + record.value("id").toString());
11 cdModel.select();
12 if (cdModel.rowCount() == 0) {
13 model->removeRow(tableView->currentIndex().row());
14 } else {
15 QMessageBox::information(this, tr("Delete Artist"),
16 tr("Cannot delete %1 because there are CDs associated "
17 "with this artist in the collection.")
18 .arg(record.value("name").toString()));
19 }
20 }
Если выделена какая-то запись, мы проверяем наличие компакт-дисков у данного артиста, и если они отсутствуют, мы сразу же удаляем эту запись артиста. В противном случае мы выводим на экран окно с сообщением о причине невыполнения удаления. Строго говоря, здесь следовало бы использовать транзакцию, потому что из программного кода видно, что между вызовами функций cdModel.select() и model->removeRow() у артиста может появиться свой компакт-диск. Транзакция будет рассмотрена в следующем разделе.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Представление данных в табличном виде
Представление данных в табличном виде В рассматриваемом примере мы оформим в виде таблицы перечни товаров и услуг. Для этого изменим программный код таким образом, чтобы они отображались не в виде маркированного и нумерованного списков, а в виде одной таблицы.Напомним,
Представление данных в табличном виде
Представление данных в табличном виде Как мы уже неоднократно отмечали ранее, представление данных в табличном виде имеет немало преимуществ: наглядность, компактность, эргономичность, хорошая восприимчивость информации, и др. В данном разделе мы рассмотрим, как
Представление данных
Представление данных Когда клиент и сервер выполняются в одной системе на одном компьютере, проблем с несовместимостью данных не возникает. И для клиента и для сервера данные в двоичном виде представляются одинаково. В случае удаленного вызова дело осложняется тем, что
10.2. Приведение формул к стандартной форме
10.2. Приведение формул к стандартной форме Как было показано в предыдущем разделе, формулы исчисления предикатов, записанные с использованием связок -› (импликация) и ‹-› (эквивалентность), могут быть переписаны лишь с использованием связок& (конъюнкция), # (дизъюнкция) и
Компоновка виджетов на форме
Компоновка виджетов на форме Существует три основных способа управления компоновкой дочерних виджетов формы: абсолютное позиционирование, ручная компоновка и применение менеджеров компоновки. Мы рассмотрим по очереди каждый из этих методов, используя в качестве
16.8. XDR: представление внешних данных
16.8. XDR: представление внешних данных В предыдущей главе мы использовали двери для вызова процедуры одного процесса из другого процесса. При этом оба процесса выполнялись на одном узле, поэтому необходимости в преобразовании данных не возникало. Однако RPC используется для
Создание новых записей в форме, связанной с данными
Создание новых записей в форме, связанной с данными Для создания новой записи в связанном с данными приложении на основе Windows Forms нужно использовать метод AddNew объекта BindingContext. При выполнении этого метода любые связанные с данными элементы управления очищаются для ввода
Проверка введенных данных в форме, связанной с данными
Проверка введенных данных в форме, связанной с данными В программировании баз данных проверка введенных данных (validation) гарантирует, что эти данные отвечают правилам, определенным при проектировании приложения. Эти правила называются правилами проверки данных (validation
5 Текстовое представление данных: ясные протоколы лежат в основе хорошей практики
5 Текстовое представление данных: ясные протоколы лежат в основе хорошей практики В данной главе рассматриваются традиции Unix в аспекте двух различных, но тесно связанных друг с другом видов проектирования: проектирования форматов файлов для сохранения данных
5 Текстовое представление данных: ясные протоколы лежат в основе хорошей практики
5 Текстовое представление данных: ясные протоколы лежат в основе хорошей практики В данной главе рассматриваются традиции Unix в аспекте двух различных, но тесно связанных друг с другом видов проектирования: проектирования форматов файлов для сохранения данных
3.5. Лексический анализ строки, содержащей число в экспоненциальной форме
3.5. Лексический анализ строки, содержащей число в экспоненциальной форме ПроблемаИмеется строка, содержащая число в экспоненциальной форме, и требуется сохранить значение числа в переменной типа double.РешениеНаиболее простым способом анализа числа в экспоненциальной
1. Требования к табличной форме представления отношений
1. Требования к табличной форме представления отношений 1. Самое первое требование, предъявляемое к табличной форме представления отношений, – это конечность. Работать с бесконечными таблицами, отношениями или любыми другими представлениями и организациями данных
Представление данных
Представление данных Рассмотрим двойственность природы данных: с одной стороны, содержимое информации, а с другой - ее физическое представление. В 1950 году Клод Шеннон (Claude Shannon) заложил основы теории информации, в том числе идею о том, что данные могут быть представлены
Кафедра Ваннаха: Инновации в изощренной форме Ваннах Михаил
Кафедра Ваннаха: Инновации в изощренной форме Ваннах Михаил Опубликовано 19 мая 2010 года Май 2010 года. Под свежей листвой каштанов мелькает яркое пятно жакетика. Носительница его бредёт по проспекту в полной прострации. Нет, её не терзают страсти,