Использование плагина FileIO

Добавлено 15 мая 2022 в 09:14

Теперь мы можем использовать наш новый плагин для доступа к каким-либо данным. В этом примере мы получим некоторые данные о городах в формате 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 – это способ сообщить представлению об изменении данных.

Готовое приложение
Готовое приложение

Теги

C++ / CppGUI / Графический интерфейс пользователяMVD / model-view-delegate / модель-представление-делегатQMLQtQtQuickQtQuick ControlsTableViewПлагин / PluginПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.