Im Artikel über dynamische Erstellung von Widgets in Qt habe ich bereits beschrieben, wie man Schaltflächen dynamisch erstellt und löscht, sowie wie man mit ihnen interagiert. Und dort wurde Vertical Layout verwendet, und im Fall von Qml können wir ListView Qml verwenden, wie es ähnlich verwendet wird, wenn in Java unter Android programmiert wird. Was übrigens auch beim Programmieren in Qt unter Android** gilt.
Im Artikel über die dynamische Erstellung von Widgets wurden exemplarisch Objekte der QButton-Klasse verwendet In diesem Artikel werden Button Qml-Objekte verwendet, deren Anpassung im next Artikel . Aber die Schaltflächen werden in ListView Qml. platziert.
Projektstruktur für die Arbeit mit ListView Qml
Diesmal kommen wir mit einem standardmäßig angelegten Projekt und sogar ohne Interface-Designer aus. Außerdem konnten zum Zeitpunkt des Schreibens nicht alle Parameter im Designer eingestellt werden. Und die Projektstruktur ist wie folgt:
- QmlDynamic.pro - Projektprofil;
- deployment.pri - Datei mit Bereitstellungsregeln für die Zielplattform;
- main.cpp - Startdatei der Hauptanwendung;
- main.qml - qml-Datei mit Programm-Quellcodes
main.qml
Da alle anderen Dateien für uns uninteressant sind und standardmäßig erstellt werden, gehen wir gleich mit der Datei main.qml. ins Debriefing.
Der Algorithmus des Programms ist wie folgt. Oben im Anwendungsfenster befindet sich ein Row -Objekt, das ein Textfeld und zwei Schaltflächen enthält. Der Rest des Anwendungsfensters wird vom ListView Qml-Objekt belegt. Wenn auf eine der Schaltflächen in der Zeile geklickt wird, wird ein dynamisches Objekt in der ListView Qml erstellt, das die Schaltfläche enthält . Beim Klick auf einen dynamischen Button wird der Index ListElement , der den angeklickten Button enthält, in das Textfeld in Row übertragen. Und dann, durch Drücken der zweiten Schaltfläche im Objekt Row , wird das Element aus der ListView Qml per Index aus dem Textfeld entfernt, und der Wert im Textfeld gelöscht.
Bemerkenswert ist, dass beim Entfernen eines Elements, das sich beispielsweise in der Mitte der Liste befindet, die Indizes aller Elemente, die diesem Element folgen, um eins reduziert werden. Das heißt, die Indizes der Elemente werden automatisch neu berechnet.
import QtQuick 2.5 import QtQuick.Controls 1.4 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") /* Номер создаваемой кнопки, для её визуальной идентификации * при демонстрации проекта */ property int number: 0 /* Строка с полем, где отображается индекс нажатой динамической кнопки, * кнопкой для создания динамических кнопок, * и кнопкой для удаления динамических кнопок по индексу * */ Row { id: row // Задаём размеры строки и прибиваем к верхней части окна приложения height: 50 anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right // Задаём размещение поля с индексом кнопки Rectangle { width: (parent.width / 5) height: 50 // Устанавливаем текстовое поле для размещения индекса кнопки Text { id: textIndex anchors.fill: parent text: "" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } } // Кнопка для создания динамических кнопок Button { id: button1 text: qsTr("Create Button") width: (parent.width / 5)*2 height: 50 /* По клику по кнопке добавляем в model ListView * объект, с заданными параметрами * */ onClicked: { listModel.append({idshnik: "Button " + (++number)}) } } // Кнопка для удаления динамических кнопок Button { id: button2 text: qsTr("Delete Button") width: (parent.width / 5)*2 height: 50 // Удаляем кнопку по её индексу в ListView onClicked: { if(textIndex.text != ""){ listModel.remove(textIndex.text) textIndex.text = "" // Обнуляем текстовое поле с индексом } } } } // ListView для представления данных в виде списка ListView { id: listView1 // Размещаем его в оставшейся части окна приложения anchors.top: row.bottom anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right /* в данном свойстве задаём вёрстку одного объекта * который будем отображать в списке в качестве одного элемента списка * */ delegate: Item { id: item anchors.left: parent.left anchors.right: parent.right height: 40 // В данном элементе будет находиться одна кнопка Button { anchors.fill: parent anchors.margins: 5 /* самое интересное в данном объекте * задаём свойству text переменную, по имени которой будем задавать * свойства элемента * */ text: idshnik // По клику по кнопке отдаём в текстовое поле индекс элемента в ListView onClicked: { textIndex.text = index } } } // Сама модель, в которой будут содержаться все элементы model: ListModel { id: listModel // задаём ей id для обращения } } }
Insgesamt
Das Ergebnis ist eine Anwendung, die Schaltflächenelemente dynamisch erstellt und löscht, die wie in der Abbildung gezeigt aussehen. Sie können sich auch eine Demonstration der Anwendung im Video-Tutorial ansehen.
Здравствуйте,
Не хотелось бы повторяться. В статье по сигналам и слотам в QML есть вариант использования C++ объекта. Там используется тип Connections , который можно настроить на сигнал, который передаёт некое значение, тот же самый ID.
Большое спасибо за ответ.
А я не понимаю, как представление ссылается на модель? Нигде ведь не указано, что представление использует именно модель с id : listModel.
ps.
Ни JavaScript ни JSON не знаю, проблема в этом ? :)
Сама модель устанавливается в качестве property в представление.
Точно.
Я почему-то подумал, что парент у ListModel главное окно, а не представление.
Три закрывающие скобки сбили с толка :)
Спасибо.
Можно и окно парентом сделать, то есть написать код модели под представлением. Но тогда в property model потребуется передать id этой модели.
Не могу понять, как мы добавляем новый элемент в listModel на строчке 53.
Как я понял из прочитанного: delegate дополняет listModel своими данными, чтобы было удобно ее отображать. В данном случае этими данными является idshnik. Сама listModel не хранит idshnik.
Возникает вопрос, как мы можем писать следующее listModel.append({idshnik: "Button " + (++number)}).
Ведь что получается, что добавляя новый элемент в listModel мы передаем данные idshnik, а listModel их не хранит. Получается, они передаются сразу делегату? Или я запутался?
idshnik - это роль в модели данных.
Когда в QML в модель добавляются данные таким способом
строчки 111-114
Создается модель без каких-либо еще ролей, это так?При первом добавлении элемента задается роль?
Но ведь это всего лишь текст кнопки.
Роли здесь определяются автоматически. В самой модели в данном случае роль не определяется, как например сделано в этой статье , где имена ролей указываются в методе roleNames. Так что да, в строка 111-114 создаётся модель без предопределённых ролей.
Как я понимаю, когда делегат пытается получить данные из модели, он отправляет в модель имя роли, по которому модель отыскивает нужное свойство в объекте, который был добавлен в строке 53.
Отлично, теперь понял.
И изменить набор ролей уже будет нельзя, если мы их уже задали.
Что говорится в "Note that when creating content dynamically the set of available properties cannot be changed once set. Whatever properties are first added to the model are the only permitted properties in the model." на http://doc.qt.io/Qt-5/qml-qtqml-models-listmodel.html#append-method .
Благодарю, Евгений.
Приветствую. Подскажите пожалуйста, как сделать так что бы на элементе ListView изначально был activeFocus. Ни как не получается это сделать. Даже если добавить
То все равно
выдает сначала true, а потом сразу же false. Если же нажать Tab то актив фокус сразу же устанавливается на элементе ListView. Вроде доходчиво объяснил проблему. Помогите кто знает решение.
Наверное, у вас что-то ещё не успело загрузиться в тот момент, и это что-то забирает фокус на себя. Попробуйте найти тот виджет, который забирает фокус и после его инициализации верните фокус на ListView.
Дело в том, что на данный момент на странице нет ничего кроме этого ListView, а в нем лишь модель и делегат.
Не помню порядок инициализации в QML и вызова метода onCompleted, возможно, что объекты в делегате будут созданы последними в итоге.
Можете попробовать сделать задержку таймером установить фокус по сработке таймера.
Я нашел в чем проблема. Страница у меня эта не первая, и не единственная. И проблемы прилетали с основного файла где крутился StackView с моими страницами. И еще я реализовал разные страницы как Item, а не Page. То есть общий фон и на нем сменялись эти самые Item'ы. Перевел все на Page - и все проблемы исчезли.
Ну видите )) Было же ещё что-то кроме того ListView ))
Добрый день.
Очень полезная статья. Спасибо.
Вопрос такой: 1) нужно "взять" кнопку 2 пальцем (прикаснулись пальцем к кнопке 2, держим, через 2-3 кнопка оторвалась от ListView) и перетащить её между 7 и 8. Как такое в Qml реализовать? куда копать?
2) можно удалить кнопку "викинув" её? Т.е. горизонтально чиркнули по кнопке слева направо, кнопка визуально улетела и соседние кнопки сомкнулись. Наподобе как в андроиде удаляются уведомления.
Давно уже не работал с QML. Qt поддерживает во вторых котролах Material Design, но не знаю, насколько он реализует данный функционал. Felgo - QML based фреймворк реализует подобные вещи.
Но что касается drag and drop фунционала, то тут нужно создавать плавающий rectangle, который будет летать за пальцем, правильно высчитывать все позиции всех элементов и т.д. В общем достаточное количество кода будет.