“Модель-Представление”
Руководство по расширяемой архитектуре “модель-представление” в PyQt.
Архитектура “Модель-представление”
Модель-представление-контроллер (Model-View-Controller, MVC) — паттерн
проектирования, ведущий свою историю от языка Smalltalk, который часто
используется для построения пользовательских интерфейсов. В книге “Паттерны
проектирования” (Design Patterns, Gamma et. al.) написано следующее:
Архитектура “Модель-представление”
Модели
Все модели основываются на абстрактном классе QAbstractItemModel ,
определяющем интерфейс, используемый представлениями и делегатами для
доступа к данным. Модель сама по себе не хранит данные; они могут находиться
как в некоторой структуре данных или хранилище, предоставляемом другим
классом, так и в базе данных, файле или в другом компоненте приложения.
Представления
Полноценные реализации предоставлены для различных видов представлений:
Сортировка
Существуют два подхода к сортировке в архитектуре “модель-представление”,
выбор между которыми основан на модели, лежащей в основе.
Вспомогательные классы
На основе стандартных классов представлений создано несколько
вспомогательных классов для использования в приложениях, опирающихся на
элементо-ориентированные классы представлений PyQt. Данные классы не
предназначены для расширения.
def main():
app = QApplication(sys.argv)
splitter = QSplitter()
model = QFileSystemModel()
model.setRootPath(os.getcwd())
tree = QTreeView(splitter)
tree.setModel(model)
tree.setRootIndex(model.index(os.getcwd()))
list = QListView(splitter)
list.setModel(model)
list.setRootIndex(model.index(os.getcwd()))
import os
import sys
def main():
app = QApplication(sys.argv)
splitter = QSplitter()
splitter.setWindowTitle("Two views onto the same directory")
model = QFileSystemModel()
model.setRootPath(os.getcwd())
tree_view = QTreeView(splitter)
tree_view.setModel(model)
tree_view.setRootIndex(model.index(os.getcwd()))
list_view = QListView(splitter)
list_view.setModel(model)
list_view.setRootIndex(model.index(os.getcwd()))
splitter.show()
return app.exec_()
if __name__ == '__main__':
main()
Классы моделей
Прежде чем рассматривать вопрос обработки выделения элементов, Вам может
оказаться полезным обратиться к концепциям, используемым в фреймворке
«модель-представление».
Базовые концепции
В архитектуре «модель-представление» модель предоставлеяет стандартный
интерфейс, используемый представлениями и делегатами для доступа к данным.
В PyQt стандарт для такого интерфейса определяется классом
QAbstractItemModel . Вне зависимости от способа хранения данных в структуре
данных, на которой основана модель, все классы-наследники QAbstractItemModel
представляют данные как иерархическую структуру, содержащую таблицы
элементов. Представления используют эту конвенцию для доступа к данным
модели, но они не ограничены в способах представления полученных данных
пользователю.
Индексы моделей
Чтобы проиллюстрировать, что представление данных отделено от способа
доступа к ним, введём концепцию индексов модели. Каждый фрагмент
информации, который может быть получен с помощью модели, отображается с
помощью индекса модели. Представления и делегаты используют эти индексы для
запросов на отображение элементов данных.
model = index.model()
номер строки;
номер столбца;
индекс модели для родительского элемента.
Строки и столбцы
В простейшем случае модель может быть представлена как простая таблица, в
которой элементы расположены в соответствии с номерами их строк и столбцов.
Из этого не следует, что соответствующие фрагменты данных хранятся в
массиве (в смысле структуры данных); использование строк и столбцов является
просто договорённостью для обеспечения взаимодействия компонентов
программы друг с другом. Мы можем получить информацию о любом данном
элементе, просто указав модели на его номера строки и столбца, также мы можем
получить индекс, представляющий данный элемент:
index = model.index(row, column, ...)
Строки и столбцы
Диаграмма выше показывает представление базовой модели для таблицы, в
которой каждый элемент расположен в соответствии с парой чисел — номером
строки и номером столбца. Можно получить индекс модели, ссылающийся на
элемент данных, передав соответствующие номера в модель.
Родительские элементы
При представлении данных в виде таблицы или списка идеальным является
табличный или схожий с табличным интерфейс элементов данных — система
пронумерованных строк и столбцов точно отображает способ, которым
представление отображает данные и их элементы для пользователя. В то же
время, представления, схожие по структуре с деревьями, требуют от модели
более гибкого интерфейса для доступа к хранящимся внутри неё элементам.
Следовательно, каждый элемент может быть родителем другой таблицы
элементов, примерно так же, как элемент самого верхнего уровня дерева может
содержать список элементов.
Роли элементов
Элементы модели могут играть различные роли для других компонентов
программы, позволяя в различных ситуациях предоставлять данные различных
типов. Например, DisplayRole используется при необходимости получить доступ
к строке, которая может отображаться представлением как текст. Обычно
элементы содержат данные для нескольких ролей, стандартные роли же
определяется ItemDataRole .
Роли элементов
Роль показывает модели, к какому типу данных обращаются. Представления могут
отображать роли различными способами, поэтому важно предоставлять
достаточную информацию о каждой роли. В секции Создание новых моделей
особенности использования ролей описаны более детально.
Наиболее частые способы использования элементов данных покрываются
стандартными ролями, определёнными в ItemDataRole . Передавая подходящие
элементы данных для каждой роли, модели могут предоставлять
представлениями и делегатам подсказки о том, как элементы требуется
отображать пользователю. Различные типы представлений могут как использовать
эту информацию (так, как это требуется представлению), так и игнорировать её.
Также, если это требуется в конкретном приложении, можно определить
собственные дополнительные роли.
Итоги раздела
Индексы моделей дают представлениям и делегатам информацию о
местонахождении элементов модели, причём эта информация независима от
конкретной организации данных внутри модели.
К элементам модели можно обратиться по сочетанию их номера столбца,
номера строки и индексу их родительского элемента.
Индексы создаются моделями по запросу других компонентов, таких как
представления и делегаты.
Если при запросе индекса в index() передан валидный индекс элемента-
родителя, то полученный индекс указывает на элемент, находящийся в модели
под данным родительским элементом, т.е. полученный индекс указывает на
потомка элемента, индекс которого передаётся.
Если же переданный индекс не валиден, то полученный индекс указывает на
элемент верхнего уровня модели.
Роль позволяет различать различные типы данных, связанные с конкретным
элементом модели.