Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

QML Qt / Qml / 3_QML для интерфейса

.doc
Скачиваний:
89
Добавлен:
28.03.2016
Размер:
667.14 Кб
Скачать
  1. В MenuBar.qml

  2.      gradient: Gradient {

  3.          GradientStop { position: 0.0; color: "#8C8F8C" }

  4.          GradientStop { position: 0.17; color: "#6A6D6A" }

  5.          GradientStop { position: 0.98;color: "#3F3F3F" }

  6.          GradientStop { position: 1.0; color: "#0e1B20" }

  7.      }

Этот градиент используется для придания объема панели меню. Первый цвет начинается с 0.0 и последний заканчивается на 1.0

Что делать дальше?

Мы закончили разработку пользовательского интерфейса очень простого текстового редактора. Продолжаем двигаться дальше, пользовательский интерфейс готов, и мы можем разрабатывать логику приложения, используя обычный Qt и C++. QML хорошо подходит как инструмент для построения прототипов и отделения логики приложения от графического пользовательского интерфейса.

Расширяем возможности QML с помощью C++

Теперь, когда мы имеем макет нашего приложения, мы можем приступить к реализации возможностей текстового редактора на C++. Использование QML вместе с C++ позволяет нам реализовать логику приложения с помощью Qt. Мы можем создать QML-контекст в приложении C++ используя классы Qt Declarative [doc.qt.nokia.com] и отображать элементы QML при помощи QDeclarativeView [doc.qt.nokia.com], который основан на Graphics View Framework [doc.qt.nokia.com]. Или же, мы можем экспортировать C++ код в плагин, который сможет использоваться в qmlviewer. Для нашего приложения мы разработаем функции загрузки и сохранения файла на C++ и экспортируем их как плагин. Это позволит запускать QML-приложение непосредственно в qmlviewer, а также полноценно работать с дизайнером QML в Qt Creator.

Делаем C++ классы доступными в QML

Мы будем реализовывать загрузку и сохранение файла используя Qt и C++. C++ классы и функции могут использоваться в QML только после того, как будут зарегистрированы в нем. Класс должен быть собран как Qt-плагин, а QML-приложение должно знать месторасположение плагина.

Для нашего приложения, нам потребуется создать следующие элементы:

  1. Класс Directory, который хранит список файлов (обьекты типа File) и отвечает за операции с каталогами

  2. Класс File, наследник QObject [doc.qt.nokia.com], который хранит имя файла

  3. Класс плагина, который будет регистрироваться QML-контексте

  4. Файл проекта Qt (с расширением .pro), для описания настроек сборки плагина

  5. Файл qmldir, который сообщает qmlviewer о том, где искать наш плагин

Сборка плагина

Чтобы собрать плагин, нам необходимо добавить указания на исходники, заголовки, и модули Qt в наш файл проекта. Весь код на C++ и файлы проекта находятся в директории filedialog.

  1.  

  2. В cppPlugins.pro:

  3.  

  4.      TEMPLATE = lib

  5.      CONFIG += qt plugin

  6.      QT += declarative

  7.  

  8.      DESTDIR +=  ../plugins

  9.      OBJECTS_DIR = tmp

  10.      MOC_DIR = tmp

  11.  

  12.      TARGET = FileDialog

  13.  

  14.      HEADERS +=     directory.h \

  15.              file.h \

  16.              dialogPlugin.h

  17.  

  18.      SOURCES +=    directory.cpp \

  19.              file.cpp \

  20.              dialogPlugin.cpp

Мы включаем в сборку модуль declarative и указываем, что мы хотим собрать плагин (TEMPLATE = lib, CONFIG = plugin ). Также мы указываем, что собранный плагин должен быть помещен в директорию plugins, расположенную в родительском каталоге.

Регистрация класса в QML

  1.  В dialogPlugin.h:

  2.  

  3.      #include <QtDeclarative/QDeclarativeExtensionPlugin>

  4.  

  5.      class DialogPlugin : public QDeclarativeExtensionPlugin

  6.      {

  7.          Q_OBJECT

  8.  

  9.          public:

  10.          void registerTypes(const char *uri);

  11.  

  12.      };

Наш класс плагина DialogPlugin является наследником QDeclarativeExtensionPlugin [doc.qt.nokia.com]. Нам необходимо переопределить виртуальную функцию registerTypes() [doc.qt.nokia.com]. Файл dialogPlugin.cpp выглядит следующим образом:

  1. DialogPlugin.cpp:

  2.  

  3.      #include "dialogPlugin.h"

  4.      #include "directory.h"

  5.      #include "file.h"

  6.      #include <QtDeclarative/qdeclarative.h>

  7.  

  8.      void DialogPlugin::registerTypes(const char *uri){

  9.  

  10.          qmlRegisterType<Directory>(uri, 1, 0, "Directory");

  11.          qmlRegisterType<File>(uri, 1, 0,"File");

  12.      }

  13.  

  14.      Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);

Функция registerTypes() [doc.qt.nokia.com] регистрирует наши классы File И Directory в QML. Эта функция требует названия классов для их прототипов, номера старшей и младшей версий классов и их имена.

Нам необходимо экспортировать плагин с помощью макроса Q_EXPORT_PLUGIN2 [doc.qt.nokia.com]. Обратите внимание, что в файле dialogPlugin.h мы имеем макрос Q_OBJECT [doc.qt.nokia.com] в начале нашего класса. Также нам надо запустить qmake для генерации мета-информации о наших классах.

Создание QML-свойств в C++ классе

Мы можем создавать QML-элементы и определять их свойства, используя C ++ и систему мета-информации Qt (Meta-Object System [doc.qt.nokia.com]). Мы можем сообщить Qt о свойствах наших объектов, используя для этого сигналы и слоты, тогда эти свойства будут доступны в QML.

Текстовый редактор должен иметь возможность загружать и сохранять файлы. Как правило, эти возможности содержатся в файловом диалоге. К счастью, мы можем использовать QDir [doc.qt.nokia.com], QFile [doc.qt.nokia.com] и QTextStream [doc.qt.nokia.com] для реализации чтения директории и операций ввода/вывода.

  1.      class Directory : public QObject{

  2.  

  3.          Q_OBJECT

  4.  

  5.          Q_PROPERTY(int filesCount READ filesCount CONSTANT)

  6.          Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)

  7.          Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)

  8.          Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

  9.  

  10.          ...

Класс Directory использует систему мета-информации Qt для регистрации свойств, необходимых для работы с файлами. Класс Directory экспортируется как плагин и может использоваться как элемент в контексте QML. Каждое из перечисленных свойств, использующих макрос Q_PROPERTY [doc.qt.nokia.com], является свойством QML.

Q_PROPERTY [doc.qt.nokia.com] объявляет свойство, а также его функции чтения и записи. Например, свойство filename, имеющее тип QString [doc.qt.nokia.com], читается при помощи функции filename() и устанавливается при помощи функции setFilename(). А каждый раз когда значение этого свойства меняется, генерируется сигнал filenameChanged(). Функции чтения и записи свойств обьявлены как public в файле заголовка.

Точно так же у нас есть другие свойства, объявленные в соответствии с их использованием. Свойство filesCount указывает количество файлов в директории. Свойство filename содержит имя текущего выбранного файла. Содержимое файла для чтения и записи хранится в свойстве fileContent.

  1. Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

Свойство files содержит список всех отфильтрованных файлов в директории. Класс Directory реализован так, чтобы отображать только корректные текстовые файлы; в данном случае, корректными считаются только файлы с расширением “.txt”. Объекты класса QList [doc.qt.nokia.com] могут использоваться в QML после объявления их с ключевым словом QDeclarativeListProperty в коде на C++. Заметим, что класс, указанный в качестве параметр-класса, должен быть потомком QObject, то есть класс File также должен наследовать класс QObject. В классе Directory список объектов типа File хранится в переменной m_fileList типа QList [doc.qt.nokia.com].

  1.      class File : public QObject{

  2.  

  3.          Q_OBJECT

  4.          Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

  5.  

  6.          ...

  7.      };

Свойства могут использоваться в QML как свойства элементов Directory. Заметим, что нам не следует создавать свойство id для объектов на языке C++.

  1.      Directory{

  2.          id: directory

  3.  

  4.          filesCount

  5.          filename

  6.          fileContent

  7.          files

  8.  

  9.          files[0].name

  10.      }

Поскольку QML использует синтаксис и структуру языка Javascript, мы можем пройти по списку файлов как по массиву и узнать их свойства. Например, чтобы получить имя первого файла, нам достаточно обратиться к свойству files0.name.

Обычные функции C++ также доступны из QML. Функции загрузки и сохранения файлов реализованы на языке C++ и определены с макросом Q_INVOKABLE. Также функцию можно объявить как слот (slot) и тогда она тоже будет доступна в QML.

  1.  В файле Directory.h:

  2.  

  3.      Q_INVOKABLE void saveFile();

  4.      Q_INVOKABLE void loadFile();

Класс Directory также должен сообщать другим объектам о том, что содержимое каталога изменилось. Для этого объект данного класса генерирует определенный сигнал (signal). Как уже отмечалось ранее, сигналы в QML имеют соответствующие обработчики, начинающиеся с приставки on. В данном случае, сигнал называется directoryChanged и генерируется каждый раз при обновлении содержимого каталога. При обновлении заново загружается список файлов в каталоге и создается список корректных текстовых файлов. Для того, чтобы объекты QML могли получать этот сигнал, в них необходимо реализовать обработчик сигнала onDirectoryChanged.

Более подробно стоит рассмотреть свойства объектов на C++. Свойства списка используют обратный вызов (callback) для получения и изменения содержимого списка. Свойства списка имеют тип QDeclarativeListProperty<File>. Каждый раз при обращении к списку, функция доступа должна возвращать QDeclarativeListProperty<File>. Так как параметр-класс File является наследником QObject, то при создании свойств QDeclarativeListProperty необходимо передать в конструктор ссылки на функции доступа и модификаторы. Также класс списка (в нашем случае QList) должен быть списком ссылок на объекты File.

Ниже приведено определение конструктора QDeclarativeListProperty из класса Directory:

  1.      QDeclarativeListProperty  ( QObject * object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )

  2.      QDeclarativeListProperty<File>( this, &m_fileList, &appendFiles, &filesSize, &fileAt,  &clearFilesPtr );

В качестве аргументов, конструктору передаются ссылки на функции, которые позволяют добавлять элементы в список, узнавать количество элементов, получать элемент по индексу и очищать список. Однако, обязательной является только функция добавления элементов в список. Стоит заметить, что указатели должны ссылаться на функции, соответствующие описанию функций AppendFunction [doc.qt.nokia.com], CountFunction [doc.qt.nokia.com], AtFunction [doc.qt.nokia.com] или ClearFunction [doc.qt.nokia.com].

  1.      void appendFiles(QDeclarativeListProperty<File> * property, File * file)

  2.      File* fileAt(QDeclarativeListProperty<File> * property, int index)

  3.      int filesSize(QDeclarativeListProperty<File> * property)

  4.      void clearFilesPtr(QDeclarativeListProperty<File> *property)

Чтобы упростить нашу диалоговую форму, класс Directory отфильтровывает некорректные файлы, имеющие расширение, отличное от .txt. То есть пользователь в списке увидит только файлы с расширением .txt. Также проверяется, чтобы сохраняемый файл также имел расширение .txt. Directory использует класс QTextStream [doc.qt.nokia.com] для чтения данных и записи в файл.

Используя наш элемент Directory, мы можем получить список файлов, определить количество текстовых файлов в каталоге приложения, получить имя файла и его содержимое в виде строки QString [doc.qt.nokia.com], а также получить информацию об изменении содержимого каталога.

Для создания плагина необходимо выполнить команду qmake для файла проекта cppPlugins.pro. Чтобы собрать бинарный файл и поместить его в каталог с плагинами (plugins), необходимо выполнить команду _make.

Импорт плагина в QML

Утилита qmlviewer импортирует файлы, расположенные в той же директории, что и разрабатываемое QML-приложение. Мы также можем создать файл qmldir содержащий пути до QML-файлов, которые мы хотим импортировать. Также в файле qmldir мы можем хранить информацию о расположении плагинов и других ресурсов.

  1.  В qmldir:

  2.  

  3.      Button ./Button.qml

  4.      FileDialog ./FileDialog.qml

  5.      TextArea ./TextArea.qml

  6.      TextEditor ./TextEditor.qml

  7.      EditMenu ./EditMenu.qml

  8.  

  9.      plugin FileDialog plugins

Плагин, который мы создали, называется FileDialog, что указывается в поле TARGET в файле описания проекта. Скомпилированный плагин располагается в каталоге plugins.

Интеграция файлового диалога в меню

Наш элемент FileMenu должен отображать элемент FileDialog, содержащий список текстовых файлов в директории, что позволяет пользователю выбрать файл, просто кликнув на него в списке. Также необходимо назначить действия для кнопок сохранения, загрузки и создания нового документа. FileMenu содержит поле ввода, позволяющее пользователю ввести имя файла с помощью клавиатуры.

Элемент Directory, используемый в файле FileMenu.qml, уведомляет элемент FileDialog о том, что необходимо обновить список отображаемых файлов. Этот сигнал обрабатывается в функции onDirectoryChanged.

  1. In FileMenu.qml:

  2.  

  3.      Directory{

  4.          id:directory

  5.          filename: textInput.text

  6.          onDirectoryChanged: fileDialog.notifyRefresh()

  7.      }

Чтобы упростить разработку нашего приложения, мы не будем скрывать наш файловый диалог. И как отмечалось ранее, наш файловый диалог будет отображать в списке только текстовые файлы с расширением .txt.

  1. В FileDialog.qml:

  2.  

  3.      signal notifyRefresh()

  4.      onNotifyRefresh: dirView.model = directory.files

Компонент FileDialog будет отображать содержимое текущего каталога, используя список под названием files. Для отображения элементов этот список использует компонент(представление) GridView, который отображает данные в виде таблицы с использованием делегатов. Делегат отвечает за внешний вид модели, и наш файловый диалог просто отобразит текстовую таблицу, с расположенными в центре именами файлов. При клике по имени файла, появится прямоугольник, обрамляющий выбранный элемент. Также наш FileDialog будет обновлять список файлов при получении соответствующего сигнала.

  1. В FileMenu.qml:

  2.  

  3.      Button{

  4.          id: newButton

  5.          label: "New"

  6.          onButtonClick:{

  7.              textArea.textContent = ""

  8.          }

  9.      }

  10.      Button{

  11.          id: loadButton

  12.          label: "Load"

  13.          onButtonClick:{

  14.              directory.filename = textInput.text

  15.              directory.loadFile()

  16.              textArea.textContent = directory.fileContent

  17.          }

  18.      }

  19.      Button{

  20.          id: saveButton

  21.          label: "Save"

  22.          onButtonClick:{

  23.              directory.fileContent = textArea.textContent

  24.              directory.filename = textInput.text

  25.              directory.saveFile()

  26.          }

  27.      }

  28.      Button{

  29.          id: exitButton

  30.          label: "Exit"

  31.          onButtonClick:{

  32.              Qt.quit()

  33.          }

  34.      }

Теперь можно соединить элементы меню FileMenu с соответствующими действиями. При нажатии на кнопку saveButton, текст будет передан из TextEdit в свойство fileContent элемента directory, а имя редактируемого файла будет скопировано из поля ввода. И после этого будет вызвана функция saveFile() для сохраниения файла. Кнопка loadButton похожа по функциональности. Кнопка newButton очищает содержимое TextEdit.

Аналогично, кнопки EditMenu будут связаны с функциями TextEdit, выполняющими копирование, вставку и выбор всего текста в редакторе.

Текстовый редактор завершен

Итак, созданное приложение может использоваться как простой текстовый редактор, способное редактировать текст и сохранять его в файл.

Исходный код

Полный исходный код этого приложения можно взять здесь [qt.gitorious.org].

Соседние файлы в папке Qml