Использование плагина FileIO
Теперь мы можем использовать наш новый плагин для доступа к каким-либо данным. В этом примере мы получим некоторые данные о городах в формате JSON и отобразим их в таблице. Мы построим это как два проекта: один для плагина расширения (называемого fileio
), который предоставляет нам способ чтения и записи текста из файла, и другой, который отображает данные в таблице (CityUI
). CityUI
использует расширение fileio
для чтения и записи файлов.
Данные JSON – это просто текст, отформатированный таким образом, что его можно преобразовать в допустимый объект/массив JS и обратно в текст. Мы используем наш FileIO
для чтения данных в формате JSON и преобразования их в объект JS с помощью встроенной функции Javascript JSON.parse()
. Данные позже используются в качестве модели для табличного представления. Это реализовано в функциях чтения и записи документа, показанных ниже.
FileIO {
id: io
}
function readDocument() {
io.source = openDialog.fileUrl
io.read()
view.model = JSON.parse(io.text)
}
function saveDocument() {
var data = view.model
io.text = JSON.stringify(data, null, 4)
io.write()
}
Данные JSON, используемые в этом примере, находятся в файле citys.json. Он содержит список записей данных о городах, где каждая запись содержит интересные данные о городе (пример данных показан ниже).
[
{
"area": "1928",
"city": "Shanghai",
"country": "China",
"flag": "22px-Flag_of_the_People's_Republic_of_China.svg.png",
"population": "13831900"
},
...
]
Окно приложения
Для создания приложения на основе Qt Quick Controls 2 мы используем мастер Qt Creator QtQuick Application (Приложение QtQuick). Мы не будем использовать новые формы QML, так как это сложно объяснить в книге, хотя новый подход к формам с файлом ui.qml намного удобнее, чем предыдущий. Таким образом, на данный момент вы можете удалить файл формы.
Базовая установка – это ApplicationWindow
, который может содержать панель инструментов, строку меню и строку состояния. Мы будем использовать строку меню только для создания некоторых стандартных пунктов меню для открытия и сохранения документа. Базовая настройка просто отобразит пустое окно.
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: root
title: qsTr("City UI")
width: 640
height: 480
visible: true
}
Использование действий
Чтобы лучше использовать/повторно использовать наши команды, мы используем тип QML Action
. Это позволит нам позже использовать то же самое действие и для потенциальной панели инструментов. Действия открытия, сохранения и выхода вполне стандартны. Действия открытия и сохранения пока не содержат никакой логики, об этом мы поговорим позже. Строка меню начинается с меню File и тремя записями этих действий. Дополнительно мы готовим уже диалоговое окно выбора файла, которое позже позволит нам выбрать наш документ с городами. Диалог не отображается при объявлении; чтобы показать его, вам нужно использовать метод open()
.
Action {
id: save
text: qsTr("&Save")
shortcut: StandardKey.Save
onTriggered: {
saveDocument()
}
}
Action {
id: open
text: qsTr("&Open")
shortcut: StandardKey.Open
onTriggered: openDialog.open()
}
Action {
id: exit
text: qsTr("E&xit")
onTriggered: Qt.quit();
}
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem { action: open }
MenuItem { action: save }
MenuSeparator {}
MenuItem { action: exit }
}
}
FileDialog {
id: openDialog
onAccepted: {
root.readDocument()
}
}
Форматирование таблицы
Содержимое данных о городах должно отображаться в таблице. Для этого используем элемент управления TableView
и объявляем 4 столбца: city
(город), country
(страна), area
(площадь), population
(население). Каждый столбец является стандартным TableViewColumn
. Позже мы добавим столбцы для флага и операции удаления, для которых потребуется пользовательский делегат столбца.
TableView {
id: view
anchors.fill: parent
TableViewColumn {
role: 'city'
title: "City"
width: 120
}
TableViewColumn {
role: 'country'
title: "Country"
width: 120
}
TableViewColumn {
role: 'area'
title: "Area"
width: 80
}
TableViewColumn {
role: 'population'
title: "Population"
width: 80
}
}
Теперь приложение должно показать вам строку меню с меню File и пустую таблицу с 4 заголовками таблиц. Следующим шагом будет заполнение таблицы полезными данными с помощью нашего расширения FileIO
.
Документ city.json представляет собой массив записей городов. Пример такой записи показан ниже.
[
{
"area": "1928",
"city": "Shanghai",
"country": "China",
"flag": "22px-Flag_of_the_People's_Republic_of_China.svg.png",
"population": "13831900"
},
...
]
Наша работа заключается в том, чтобы позволить пользователю выбрать файл, прочитать его, преобразовать и задать полученные данные для табличного представления.
Чтение данных
Для этого мы позволяем действию открытия открыть диалоговое окно выбора файла. Когда пользователь выбрал файл, в диалоге вызывается метод onAccepted
. Там мы вызываем функцию readDocument()
. Функция readDocument()
устанавливает URL из диалога выбора файла в наш объект FileIO
и вызывает метод read()
. Загруженный текст из FileIO
затем анализируется с помощью метода JSON.parse()
, и полученный объект напрямую устанавливается в табличное представление в качестве модели. Удобно же?
Action {
id: open
...
onTriggered: {
openDialog.open()
}
}
...
FileDialog {
id: openDialog
onAccepted: {
root.readDocument()
}
}
function readDocument() {
io.source = openDialog.fileUrl
io.read()
view.model = JSON.parse(io.text)
}
FileIO {
id: io
}
Запись данных
Для сохранения документа мы подключаем действие save
к функции saveDocument()
. Функция сохранения документа берет модель из представления, которая является объектом JS, и преобразует ее в строку с помощью функции JSON.stringify()
. Результирующая строка устанавливается в свойство text
нашего объекта FileIO
, и мы вызываем write()
для сохранения данных на диск. Параметры «null» и «4» в функции stringify
отформатируют полученные данные JSON, используя отступ с 4 пробелами. Это просто для лучшей читабельности сохраненного документа.
Action {
id: save
...
onTriggered: {
saveDocument()
}
}
function saveDocument() {
var data = view.model
io.text = JSON.stringify(data, null, 4)
io.write()
}
FileIO {
id: io
}
Это основное приложение с чтением, записью и отображением документа JSON. Подумайте обо всем, что было бы необходимо для написания программы для чтения и записи XML. С JSON всё, что вам нужно, – это способ чтения и записи текстового файла или отправки и получения текстового буфера.
Завершающий штрих
Приложение еще не полностью готово. Мы по-прежнему хотим показывать флаги и позволять пользователю изменять документ, удаляя города из модели.
В этом примере файлы флагов хранятся в папке flags относительно документа main.qml. Чтобы иметь возможность отображать их, в столбце таблицы необходимо определить пользовательский делегат для рендеринга изображения флага.
TableViewColumn {
delegate: Item {
Image {
anchors.centerIn: parent
source: 'flags/' + styleData.value
}
}
role: 'flag'
title: "Flag"
width: 40
}
Это всё, что нужно, чтобы показать флаг. Это предоставляет делегату свойство flag
из модели JS как styleData.value
. Затем делегат задает путь к изображению, добавляя к нему flags/, и отображает его как элемент изображения.
Для удаления мы используем аналогичную технику для отображения кнопки удаления.
TableViewColumn {
delegate: Button {
iconSource: "remove.png"
onClicked: {
var data = view.model
data.splice(styleData.row, 1)
view.model = data
}
}
width: 40
}
Для операции удаления данных мы получаем модель представления, а затем удаляем одну запись, используя функцию JS splice
. Этот метод доступен нам, так как модель из массива JS. Метод splice
изменяет содержимое массива, удаляя существующие элементы и/или добавляя новые элементы.
Массив JS, к сожалению, не так умен, как модель Qt, такая как QAbstractItemModel
, которая будет уведомлять представление об изменениях строк или данных. К настоящему времени в представлении не будут отображаться обновленные данные, поскольку оно никогда не получает уведомлений о каких-либо изменениях. Только когда мы устанавливаем данные обратно в представление, представление распознает наличие новых данных и обновляет свое содержимое. Повторная установка модели с помощью view.model = data
– это способ сообщить представлению об изменении данных.