Программист C++/Qt и QML
0,0
рейтинг
1 июня 2013 в 00:51

Разработка → Model-View в QML. Часть нулевая, вводная

Одним из наиболее распространенных и эффективных приемов проектирования программ является использование шаблона программирования MVC (Model-View-Controller) — Модель-Представление-Контроль. MVC позволяет разделить части программы, отвечающие за хранение и доступ к данным, отображение данных и за взаимодействие с пользователем на отдельные слабо связанные модули. Подобное разделение ответственности упрощает структуру программы и позволяет вносить изменения в одну из этих частей не затрагивая остальные.

Такой подход активно применяется в Qt, а в QML вообще является краеугольным камнем. Так что тем, кто изучает QML понимание принципов MVC будет совсем не лишним.

Model-View в QML:
  1. Model-View в QML. Часть нулевая, вводная
  2. Model-View в QML. Часть первая: Представления на основе готовых компонентов
  3. Model-View в QML. Часть вторая: Кастомные представления
  4. Model-View в QML. Часть третья: Модели в QML и JavaScript
  5. Model-View в QML. Часть четвертая: C++-модели


Что такое MVC

Разделение ответственности является одним из основополагающих принципов программирования. Суть его состоит в том, чтобы разделять программу на функциональные блоки, каждый из которых отвечает за свою часть работы. Этими блоками могут быть и функции, и объекты, и модули и т.д. Блоки между собой связываются через определенные интерфейсы. Это дает некоторую независимость блоков относительно друг друга и снижает сложность системы.

Основные концепции графический интерфейс пользователя (GUI) были разработаны в лаборатории Xerox в 70-х годах двадцатого века. В их число входит и шаблон проектирования MVC, призванный снизить сложность и упростить архитектуру программ с графическим интерфейсом при помощи того самого разделения ответственности.

Итак, суть этого шаблона состоит в разделении программы на три компонента.

  • Модель. Отвечает за данные и обеспечивает доступ к ним.
  • Представление. Отвечает за отображение данных, полученных из модели.
  • Контроль. Отвечает за взаимодействие с пользователем. Может изменять данные в модели.


Схематически модель можно изобразить так:



Между этими компонентами есть определенные интерфейсы, позволяющие развязать их между собой. В идеале эти модули вообще должны независимы друг от друга и позволять вносить изменения или даже полностью заменить какой-либо модуль без переделки остальных.
С развитием графических интерфейсов и появлением новых видов программ, таких, например, как веб-приложения, такой вариант разделения стал подходить не везде. Поэтому появились его разновидности, такие как MVVC, MVP и т.д. Я буду дальше все разновидности этой модели обобщенно называть MVC.

Немало современных фреймворков используют MVC и содержат в себе средства для разделения программы в соответствии с этим шаблоном. Поскольку одним из основных предназначений Qt является разработка графических интерфейсов пользователя, то в нем без MVC тоже не обошлось и Qt включает в себя свою вариацию — Model-View фреймворк.

MVC в Qt и QML

Основная идея применения данного шаблона в Qt — разделение данных и их отображения.



Здесь модель отвечает за данные и доступ к ним. Поскольку в графическом интерфейсе за отображение элементов и получение ввода от пользователя нередко отвечают одни и те же элементы, то вполне логична идея объединить представление и контроль. Именно так и сделано в Qt: представление не только отображает данные, но и выполняет функции контроля и отвечает за взаимодействие с пользователем. Но для того, чтобы из-за такого объединения не терять гибкость, было введено понятие делегата. Делегат позволяет определять, как эти данные будут отображаться и как пользователь может их изменять. Представление же, по сути, теперь является контейнером для экземпляров делегата.

В QML этот принцип тоже применяется, даже в гораздо большей степени. Как я уже говорил, Model-View является одной из основополагающих концепций QML. Одно из основных предназначений QML — это выделение интерфейса программы в отдельную часть, которая может быть очень гибкой и легко адаптироваться для разных нужд. Например, для десктопного приложение может быть один вариант интерфейса, а для мобильного другой. При этом ядро программы (модель) может быть написано на C++ и оставаться неизменным.

Также стоит отметить, что для некоторых моделей в QML, делегаты могут только отображать данные, но не редактировать их.
Несмотря на то, что интерфейсная часть является основным местом для применения QML, модель на нем тоже можно можно реализовать, есть для этого соответствующие инструменты. При желании, программу можно сделать практически полностью на QML/JavaScript и свести количество кода на C++ к минимуму. Именно с такой программы мы и начнем.

Простой пример MVC в QML

Рассмотрим программу, которая выводит пару цветных прямоугольников с текстом посередине.

import QtQuick 2.0

Rectangle {
    width: 360
    height: 360

    ListModel {
        id: dataModel

        ListElement {
            color: "orange"
            text: "first"
        }
        ListElement {
            color: "skyblue"
            text: "second"
        }
    }

    ListView {
        id: view

        anchors.margins: 10
        anchors.fill: parent
        spacing: 10
        model: dataModel

        delegate: Rectangle {
            width: view.width
            height: 100
            color: model.color

            Text {
                anchors.centerIn: parent
                renderType: Text.NativeRendering
                text: model.text
            }
        }
    }
}

Моделью здесь является объект типа ListModel. В качестве элементов модели используются два дочерних объекта специального типа ListElement, в которых мы определили два свойства: color и text.

Для отображения мы используем объект типа ListView, которому в качестве модели мы указываем id нашего объекта типа ListModel. Само по себе представление не знает, какие отображать наши данные, поэтому нужно задать ему еще и делегат. Делегатом в QML является тип объекта, экземпляр которого создается на каждый элемент модели. А ListView, в свою очередь, создает эти объекты, отображает их в виде списка и обеспечивает навигацию по ним.

В качестве делегата выступает прямоугольник с текстом посредине. Цвет прямоугольника и текст берутся из модели. Внутри делегата видны все свойства элементов модели — т.н. «роли». Они доступны через специальный объект model, а также и напрямую, т.е. можно писать просто color. Я предпочитаю писать полностью, так нагляднее, откуда берутся данные и не будет никаких конфликтов, если у делегата есть свойства с такими же именами.

Результат выполнения выглядит примерно так:



Динамика в Model-View

В MVC модель может быть как пассивной, так и активной.

Пассивной является модель, которая просто хранит данные и не воздействует никак контроллер и представление. Соответственно, при изменении данных в модели, она не может никак сообщить представлению об этом. В таком случае, все изменения модели отслеживает контроллер, он же говорит представлению о том, что надо обновить информацию.

Активная же модель может оповещать представления о том, что данные изменились. В свою очередь, представление может отобразить эти изменения. Именно такой вариант считается классической реализацией MVC.

В Qt и QML применяется активная модель. Это дает нам возможность изменять данные в модели и не беспокоится об отображении обновленных данных — представление все сделает за нас само.

Намного доработаем предыдущий пример: добавим кнопку, которая будет добавлять элементы в модель при нажатии.

import QtQuick 2.0

Rectangle {
    width: 360
    height: 360

    ListModel {
        id: dataModel
    }

    Column {
        anchors.margins: 10
        anchors.fill: parent
        spacing: 10

        ListView {
            id: view

            width: parent.width
            height: parent.height - button.height - parent.spacing
            spacing: 10
            model: dataModel
            clip: true

            delegate: Rectangle {
                width: view.width
                height: 40
                color: "skyblue"

                Text {
                    anchors.centerIn: parent
                    renderType: Text.NativeRendering
                    text: model.index
                }
            }
        }

        Rectangle {
            id: button

            width: 100
            height: 40
            anchors.horizontalCenter: parent.horizontalCenter
            border {
                color: "black"
                width: 1
            }

            Text {
                anchors.centerIn: parent
                renderType: Text.NativeRendering
                text: "Add"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: dataModel.append({})
            }
        }
    }
}

Изначально модель не содержит никаких элементов. При нажатии на кнопку в модель добавляется пустой элемент без свойств. Модель информирует представление о том, что добавился новый элемент. На каждый новый элемент представление создает объект, тип которого мы указали в качестве делегата. Помимо тех свойств, которые мы описываем в элементах модели, в делегате будет доступно еще одно — index (или model.index), содержащее индекс элемента в модели и, соответственно, в отображении. Так что, несмотря на то, что мы добавляем пустой элемент в модель, одно свойство там все-таки есть и именно индекс мы используем в качестве текста.

Запустив программу и понажимав на кнопку получим примерно такой вид:



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

Небольшой вывод

Разработанная более 30 лет назад, технология MVC ничуть не утратила своей актуальности. И по сей день она является основой для проектирования программ с графическим интерфейсом; сейчас сложно представить себе такую программу, не использующую MVC.
Как технология, разработанная для создания графических интерфейсов, Qt и, особенно, QML имеют полную поддержку MVC. Необходимые инструменты входят в состав библиотек, более того, в самом Qt, достаточно четко прослеживается идеология этого шаблона.
Соответственно применение MVC здесь является естественным и именно так задумано разработчиками и именно с таким видением был спроектирован фреймворк.

На этом вводную часть можно считать законченной, а в следующей статье мы рассмотрим подробнее представления.
Никита Крупенько @BlackRaven86
карма
36,0
рейтинг 0,0
Программист C++/Qt и QML
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (3)

  • +1
    Ну вообще-то модель как-правило будет всё-таки C++ классом, который будет свои данные брать из базы\сети\файлов. В таком виде и QML код будет проще и нагляднее (никаких там захардкоженных массивов данных) и С++ код не будет нагружен логикой вьюхи. Самое в этом деле хорошее — что даже не появляется соблазна запороть красоту MVC смешивая модель и представление, поскольку они сразу в разных файлах и на разных языках.

    P.S. Неделя Qt на Хабре?
    • +2
      Ну я, например, обычно так и поступаю: логика на C++, отображение на QML. Но как поступать, если приложение пишется целиком на QML+JS?

      P.S. Вы имеете что-то против недели Qt?
    • +1
      Если данные поступают из сети прямо в QML, например при помощи XMLHttpRequest, то и модель удобнее держать здесь же.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.