|
|||||||
Архитектура Модель-Вид-Контроллер в Qt (Небольшой, но полный пример кода)
Время создания: 20.03.2016 22:41
Текстовые метки: qt, MVC, модель, вид, контроллер
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Принципы написания кода
Запись: xintrea/mytetra_syncro/master/base/1438881056ba8iha1mwb/text.html на raw.github.com
|
|||||||
|
|||||||
Архитектура Модель-Вид-Контроллер в Qt Примечание: здесь показан пример простого случая, решение написано так, как того требует документация, так сказать "в лоб". Модель содержит как стандартные методы, необходимые для реализации интерфейса, так и дополнительные "для удобства". Такой подход сыграет злую шутку в тот момент, когда потребутся использовать прокси-модель. Потому что прокси-модель пропускает сквозь себя стандартные методы, и не умеет пропускать нестандартные. Использование классов Qt, основанных на архитектуре модель/представление В предыдущих статьях архитектура «Модель-Вид-Контроллер» была реализована в приложении «вручную»: все классы и соответствующие связи между ними определялись в программе явно. Но в Qt есть ряд классов уже применяющих этот подход. Использование их для решения задачи, в том числе и в качестве базовых классов, позволяет более удобно создавать сложные приложения. Архитектура Qt модель/представление Интерфейс любой Qt-модели базируется на классе QAbstractItemModel. Для того чтобы создать свою собственную модель, необходимо унаследовать либо этот класс, либо унаследованный от него. Например, его наследниками являются класс QAbstractListModel, представляющий собой одномерный список и класс QAbstractTableModel —двумерная таблица. Все классы представлений базируются на классе QAbstractItemView. Для представления данных в Qt используются, в основном, три класса: QListView (отображает данные в виде одномерного списка), QTableView (отображает данные в виде таблицы) и QTreeView (отображает данные в виде иерархического списка). Задача Рассмотрим данные об организации как и в предыдущем примере : название, количество сотрудников, праздник, дата корпоративного собрания по его поводу и количество выпитых напитков. Пусть необходимо хранить и редактировать сведения о нескольких таких организациях. Данные о новой организации вводит пользователь по своему желанию. Модель Основной моделью приложения теперь будет список организаций. Для его создания можно использовать контейнерный класс QList. Это класс-шаблон, параметром которого является тип хранимых объектов. Мы будем хранить в нем объекты класса Organization. Этот класс был описан ранее, для решения данной задачи его можно немного упростить, убрав механизм сигналов и слотов и заменив конструктор с параметрами на конструктор по умолчанию. Заголовочный файл теперь будет выглядеть так: #include <QString> class Organization { Отобразить в окне приложения информацию о нескольких организациях так, чтобы пользователю было удобно ее просматривать и изменять, можно в виде двумерной таблицы. На отдельной строке этой таблицы будут располагаться сведения об одной организации – каждый параметр в отдельной ячейке. Передвигаясь по строкам, пользователь сможет увидеть весь список объектов и отредактировать, то что захочет. Такой способ представления информации реализован в классе QTableView. Информация, которая будет размещена в таблице, должна храниться в соответствующей модели данных. Моделью может служить объект класса QAbstractTableModel или его наследника. В нашем случае можно создать собственный класс-потомок QAbstractTableModel, полем которого будет указатель на QList<Organization>. Каждый элемент модели имеет собственный индекс (объект класса QModelIndex) и набор атрибутов, называемых ролями. В табличной модели компонентами индекса являются номер строки и номер столбца элемента. Индекс позволяет получить доступ к соответствующему элементу. Роль – это значение из набора констант, которое определяет как элемент будет отображен на экране. Например роль Qt::DisplayRole используется для отображения информации, которую надо представить в виде текста. Для реализации модели на базе класса QAbstractTableModel необходимо переопределить методы rowCount(), columnCount(), возвращающие количество строк и столбцов, и метод data(), ответственный за определение данных, которые соответствуют в модели определенному индексу и роли. Метод data() должен возвращать объект класса QVariant. Этот класс имеет большое количество конструкторов, параметрами которых являются объекты и переменные разных типов. Таким образом из различных данных можно создать объекты класса QVariant, используя подходящий конструктор. Переопределения этих трех методов достаточно, если необходимо только отображать данные. Если модель допускает изменение данных, то необходимо переопределить метод flags(), в котором устанавливаются значения флага, определяющие набор действий с моделью. Кроме того, если изменение данных должно происходить непосредственно в таблице QTableView, то переопределяется метод setData(), где описывается изменение соответствующих параметров модели. Переопределение ряда других методов класса QAbstractTableModel позволяет изменить его функциональность в соответствии с поставленной задачей. Например, с помощью метода headerData() можно задать заголовки строк и столбцов таблицы отображающей данные. Нашу модель мы построим так, чтобы можно было просматривать и изменять данные. Редактирование и добавление объектов в список организаций будем осуществлять с помощью диалоговых окон. Поэтому переопределим методы rowCount(), columnCount(), data(), flags(), headerData() и определим собственные методы addOrganization() для добавления элемента в список, delOrganization() для удаления элемента из списка, getOrganization() для получения значения элемента по его индексу, а также getData() для преобразования заданного параметра организации в объект класса QVariant. Заголовочный файл модели будет выглядеть так: #include <QAbstractTableModel> class OrganizationsList : public QAbstractTableModel { Рассмотрим методы подробно. В конструкторе просто создадим пустой список организаций: OrganizationsList::OrganizationsList(QObject *parent) : QAbstractTableModel (parent) { Для объекта класса Organization будем отображать в таблице пять параметров (название, количество сотрудников, праздник, дата корпоративного собрания по его поводу и количество выпитых напитков на одного сотрудника), поэтому количество столбцов в таблице должно быть равно пяти: int OrganizationsList::columnCount(const QModelIndex & parent) const { Количество строк соответствует количеству организаций в списке, т. е. его размеру: int OrganizationsList::rowCount(const QModelIndex & parent) const { Аргументами метода data() являются индекс элемента модели и его роль. Если роль элемента предназначена для отображения данных, то метод возвращает его представление в объекте класса QVariant. Для создания такого объекта вызывается метод нашего класса getData(), в котором по координатам ячейки определяется параметр организации и возвращается соответствующий объект класса QVariant (номер строки определяет организацию в списке, номер столбца – параметр). Если роль элемента другая или происходит обращение к несуществующим данным (последнее проверяется методом isValid() класса QModelIndex), то возвращается пустой объект QVariant:
QVariant OrganizationsList::getData(int num, int position) const { Переопределим также метод headerData() для заголовков в таблице. Первый аргумент определяет номер заголовочной ячейки, второй – расположение: по вертикали размещаются заголовки строк, в них должен быть номер организации по порядку; по горизонтали – названия параметров организации (Name – название, Personnel – количество сотрудников, Holiday – праздник, Date – дата проведения, Drunk by one – сколько выпито на одного человека), третий – роль элемента, в нашем случае это роль для отображения данных в виде текста: QVariant OrganizationsList::headerData(int section, Qt ::Orientation orientation, int role) const { В методе flags() устанавливаем флаги, позволяющие выделять и изменять элементы модели: Qt ::ItemFlags OrganizationsList::flags(const QModelIndex & index) const { Метод getOrganization() возвращает заданный элемент списка организации, который определяется номером строки получаемым из индекса элемента нашей модели. Этот метод потребуется для редактирования соответствующей организации в главном окне приложения:
Organization& OrganizationsList::getOrganization(const QModelIndex & index) const {
void OrganizationsList::addOrganization(Organization& newOrganization) { Похожим образом организован метод delOrganization(), удаляющий элемент из модели и списка. Исключаемая организация определяется номером строки, в которой она хранится и отображается, этот номер мы получаем из индекса соответствующего элемента модели: void OrganizationsList::delOrganization(const QModelIndex & index) { Главное окно приложения В главном окне приложения мы разместим таблицу view, отображающую список организаций (в начале работы программы он будет пустым) и три кнопки класса QPushButton, чтобы пользователь мог добавить организацию, редактировать или удалять выбранный элемент. Кроме виджетов, необходимо создать модель данных dataList класса OrganizationsList и слоты для обработки сигналов нажатия кнопок: #include <QMainWindow> class MainWindow : public QMainWindow { В конструкторе выделим память для модели данных и таблицы, которая будет их отображать, зададим соответствие между моделью и отображением вызовом метода setModel() класса QTableView, создадим кнопки и разместим виджеты на главном окне, а также соединим сигналы кнопок с соответствующими слотами: MainWindow::MainWindow() : QMainWindow () { Для добавления организации создадим диалоговое окно dialog класса OrganizationEditDialog (этот класс и работа с ним были подробно описаны в предыдущих статьях ): void MainWindow::addButtonClicked() { При редактировании данных необходимо убедиться, что они существуют, это можно сделать, проверив индекс текущего выбранного элемента таблицы. Для редактирования также как в предыдущем методе будет вызвано диалоговое окно: void MainWindow::editButtonClicked() { Для удаления элемента необходимо вызвать метод delOrganization() разработанного нами класса модели OrganizationsList, предварительно проверив, что в таблице выбран элемент: void MainWindow::delButtonClicked() { Главная функция приложения: #include <QtGui/QApplication> QApplication app(argc, argv); В заключение приведем картинку с внешним видом приложения: И во время редактирования данных: |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|