Evgenii Legotckoi
Oct. 26, 2015, 11:25 p.m.

QML - Lesson 007. ListView. Dynamic creation and deletion of elements

I have already told in the article about the creation of dynamic widgets to Qt , how to create and delete buttons dynamically, as well as how to interact with them. And there used Vertical Layout, a case we can use QML ListView Qml, both similarly used when programming in Java for Android. That, incidentally, is also applicable when programming in Qt for Android.

The article about the dynamic creation of widgets used for example QButton class objects. This article will use the objects Button Qml, which customization was discussed in the following article . But the key will be placed in the ListView Qml.

Project structure for ListView Qml

This time dispense project created by default and even without the designer interface. Especially because at the time of this writing, not all parameters can be set in the designer. A following project structure:

  • QmlDynamic.pro - the profile of the project;
  • deployment.pri - deploy rules file on the target platform;
  • main.cpp - the main application startup file;
  • main.qml - qml file with the original program codes

main.qml

Since all of the other files do not represent any interest for us and created by default, then immediately move on to the the file main.qml.

The algorithm of the program is as follows. At the top of the application window has a Row object, which is a text box and two buttons. The remainder of the application window object takes ListView Qml . By pressing one of the buttons in the Row creates a dynamic object in the ListView Qml, which contains the button. By pressing the button, the dynamic in the text box on the Row passed ListElement index, which is the button on which the press was made. And then pressing the second button in the Row object on the index of the text box is removed an item from the ListView Qml, and the value in the text box is cleared.

Noteworthy is the fact that if you delete an item, which is located in the middle of the list, for example, indexes of all the elements following this reduced by one element. That is, the indices of the items are recalculated automatically.

  1. import QtQuick 2.5
  2. import QtQuick.Controls 1.4
  3.  
  4. ApplicationWindow {
  5. visible: true
  6. width: 640
  7. height: 480
  8. title: qsTr("Hello World")
  9.  
  10. /* Number created buttons for her visual identification of the demonstration project
  11. */
  12. property int number: 0
  13.  
  14. /* The string field that displays dynamically pressed the button index button
  15.   * to create dynamic buttons, and a button to delete the index of dynamic buttons
  16. * */
  17. Row {
  18. id: row
  19. // Set line size and nailed to the top of the application window
  20. height: 50
  21. anchors.top: parent.top
  22. anchors.left: parent.left
  23. anchors.right: parent.right
  24.  
  25. Rectangle {
  26. width: (parent.width / 5)
  27. height: 50
  28.  
  29. // Set the text box to accommodate the key index
  30. Text {
  31. id: textIndex
  32. anchors.fill: parent
  33. text: ""
  34. verticalAlignment: Text.AlignVCenter
  35. horizontalAlignment: Text.AlignHCenter
  36. }
  37. }
  38.  
  39. // Button to create dynamic buttons
  40. Button {
  41. id: button1
  42. text: qsTr("Create Button")
  43. width: (parent.width / 5)*2
  44. height: 50
  45.  
  46. /* By clicking on the button to add the model ListView object
  47.   * with the specified parameters
  48. * */
  49. onClicked: {
  50. listModel.append({idshnik: "Button " + (++number)})
  51.  
  52. }
  53. }
  54.  
  55. // Button to remove the dynamic buttons
  56. Button {
  57. id: button2
  58. text: qsTr("Delete Button")
  59. width: (parent.width / 5)*2
  60. height: 50
  61.  
  62. // Remove the button on its index in the ListView
  63. onClicked: {
  64. if(textIndex.text != ""){
  65. listModel.remove(textIndex.text)
  66. textIndex.text = "" // Null text box with index
  67. }
  68. }
  69. }
  70. }
  71.  
  72. // ListView to represent the data as a list
  73. ListView {
  74. id: listView1
  75. // We place it in the remainder of the application window
  76. anchors.top: row.bottom
  77. anchors.bottom: parent.bottom
  78. anchors.left: parent.left
  79. anchors.right: parent.right
  80.  
  81. /* in this property we specify the layout of the object
  82.   * that will be displayed in the list as a list item
  83. * */
  84. delegate: Item {
  85. id: item
  86. anchors.left: parent.left
  87. anchors.right: parent.right
  88. height: 40
  89.  
  90. Button {
  91. anchors.fill: parent
  92. anchors.margins: 5
  93.  
  94. /* The most interesting in this facility We set text property variable name,
  95.   * which will set the properties of the element
  96. * */
  97. text: idshnik
  98.  
  99. // By clicking on the button in the text box we return the index in the ListView
  100. onClicked: {
  101. textIndex.text = index
  102. }
  103. }
  104. }
  105.  
  106. model: ListModel {
  107. id: listModel
  108. }
  109. }
  110. }

Conclusion

As a result, you will receive an application in which dynamically created and deleted with buttons that will appear as shown in the figure.

Video

Do you like it? Share on social networks!

L
  • June 14, 2017, 3:09 p.m.

Здравствуйте,


Скажите, как реализовать следующее. Из объекта C++ посылается сигнал. Этот сигнал передает int ID - номер элемента.  В QML этот сигнал принимается и объект из ListModel с index == ID, например, меняет текст или свой  цвет.
Evgenii Legotckoi
  • June 15, 2017, 1:27 p.m.

Не хотелось бы повторяться. В статье по сигналам и слотам в QML есть вариант использования C++ объекта. Там используется тип Connections , который можно настроить на сигнал, который передаёт некое значение, тот же самый ID.

Connections {
    target: appCore 
    onSendToQml: {
        
    }
}
При этом сигнатура сигнала и обработчика не прописывается в этом соединении. Мы просто знаем, что в сигнале передаётся ID типа int, например с таким названием: sendedID.

Далее необходимо по этому ID изменить цвет. Тут есть такой момент, Изменения в свойствах делегата ListView необходимо делать через модель данных. То есть, модель данных должна содержать поле с указанием цвета. Можно сделать по умолчанию чёрный, а потом изменять его.
delegate: Text {
    anchors.left: parent.left
     anchors.right: parent.right
     height: 50
    horizontalAlignment: Text.AlignHCenter
    verticalAlignment: Text.AlignVCenter

    color: textColor // Роль с цветом
    text: textList // Роль с текстом
}
Тогда изменение цвета будет выглядеть так:
Connections {
    target: appCore 
    onSendToQml: {
          listModel.get(sendedID).textColor = "blue"
    }
}
L
  • June 15, 2017, 2:21 p.m.

Большое спасибо за ответ.

А я не понимаю, как представление ссылается на модель? Нигде ведь не указано, что представление использует именно модель с id : listModel.

ps.
Ни JavaScript ни JSON не знаю, проблема в этом ? :)

Сама модель устанавливается в качестве property в представление.

model: ListModel {
    id: listModel // задаём ей id для обращения
}
Это для представления достаточно. По факту, даже этот id можно не указывать. Указание данного id было необходимо, чтобы другие объекты интерфейса, которые не относятся к модели, могли бы к ней обратиться.

Точно.
Я почему-то подумал, что парент у ListModel  главное окно, а не представление.
Три закрывающие скобки сбили с толка :)
Спасибо.

Можно и окно парентом сделать, то есть написать код модели под представлением. Но тогда в property model потребуется передать id этой модели.

Не могу понять, как мы добавляем новый элемент в listModel на строчке 53.
Как я понял из прочитанного: delegate дополняет listModel своими данными, чтобы было удобно ее отображать. В данном случае этими данными является idshnik. Сама listModel не хранит idshnik.
Возникает вопрос, как мы можем писать следующее  listModel.append({idshnik: "Button " + (++number)}).
Ведь что получается, что добавляя новый элемент в listModel мы передаем данные idshnik, а listModel их не хранит. Получается, они передаются сразу делегату? Или я запутался?

idshnik - это роль в модели данных.
Когда в QML в модель добавляются данные таким способом

listModel.append({idshnik: "Button " + (++number)})
То при попытке забрать данные из модели в QML вызывается во внутренностях ListView метод модели data() , который присутствует во всех моделях в Qt. Как я понимаю, в качестве роли передаётся QString ("idshnik") из делегата, и если в текущем объекте такая роль была найдена
{idshnik: "Button " + (++number)}
то возвращается некоторое значение, которое там было задано.

строчки 111-114

        model: ListModel {
            id: listModel // задаём ей id для обращения
        }
Создается модель без каких-либо еще ролей, это так?

А где тот момент, когда роль задается?
Строчка 53?
listModel.append({idshnik: "Button " + (++number)})
При первом добавлении элемента задается роль?

Или на строчке 102 внутри создания делегата?
text: idshnik
Но ведь это всего лишь текст кнопки.

Не могу понять как это именно работает :)

Роли здесь определяются автоматически. В самой модели в данном случае роль не определяется, как например сделано в этой статье , где имена ролей указываются в методе roleNames. Так что да, в строка 111-114 создаётся модель без предопределённых ролей.


На строке 53 создаётся объект со свойствами, у которых есть имена. Эти имена и являются ролями.

На строке 102 по сути написано имя роли, которую нужно попытаться найти в объекте модели.

Как я понимаю, когда делегат пытается получить данные из модели, он отправляет в модель имя роли, по которому модель отыскивает нужное свойство в объекте, который был добавлен в строке 53.

Учитывая, что все кастомные типы данных в QML наследуются от QObject, то и создаваемый объект должен также наследоваться от этого класса. А QObject может иметь динамически создаваемые property, доступ к которым осуществляется через имя, в данном случае таким имененем выступает роль (idshnik).

Отлично, теперь понял.

И изменить набор ролей уже будет нельзя, если мы их уже задали.

Что говорится в "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 .

Благодарю, Евгений.

R
  • Nov. 27, 2018, 2:21 p.m.

Приветствую. Подскажите пожалуйста, как сделать так что бы на элементе ListView изначально был activeFocus. Ни как не получается это сделать. Даже если добавить

  1. Component.onCompleted:
  2. {
  3. listView.forceActiveFocus();
  4. }

То все равно

  1. onActiveFocusChanged: { console.log("activeFocusChanged", activeFocus) }

выдает сначала true, а потом сразу же false. Если же нажать Tab то актив фокус сразу же устанавливается на элементе ListView. Вроде доходчиво объяснил проблему. Помогите кто знает решение.

Evgenii Legotckoi
  • Nov. 27, 2018, 2:32 p.m.

Наверное, у вас что-то ещё не успело загрузиться в тот момент, и это что-то забирает фокус на себя. Попробуйте найти тот виджет, который забирает фокус и после его инициализации верните фокус на ListView.

R
  • Nov. 27, 2018, 4:26 p.m.

Дело в том, что на данный момент на странице нет ничего кроме этого ListView, а в нем лишь модель и делегат.

Evgenii Legotckoi
  • Nov. 28, 2018, 2:06 p.m.

Не помню порядок инициализации в QML и вызова метода onCompleted, возможно, что объекты в делегате будут созданы последними в итоге.

Можете попробовать сделать задержку таймером установить фокус по сработке таймера.

R
  • Nov. 29, 2018, 9:48 a.m.

Я нашел в чем проблема. Страница у меня эта не первая, и не единственная. И проблемы прилетали с основного файла где крутился StackView с моими страницами. И еще я реализовал разные страницы как Item, а не Page. То есть общий фон и на нем сменялись эти самые Item'ы. Перевел все на Page - и все проблемы исчезли.

Evgenii Legotckoi
  • Nov. 29, 2018, 2:47 p.m.

Ну видите )) Было же ещё что-то кроме того ListView ))

juvf
  • Nov. 3, 2022, 2:20 p.m.

Добрый день.
Очень полезная статья. Спасибо.

Вопрос такой: 1) нужно "взять" кнопку 2 пальцем (прикаснулись пальцем к кнопке 2, держим, через 2-3 кнопка оторвалась от ListView) и перетащить её между 7 и 8. Как такое в Qml реализовать? куда копать?
2) можно удалить кнопку "викинув" её? Т.е. горизонтально чиркнули по кнопке слева направо, кнопка визуально улетела и соседние кнопки сомкнулись. Наподобе как в андроиде удаляются уведомления.

Evgenii Legotckoi
  • Dec. 12, 2022, 2:51 p.m.

Давно уже не работал с QML. Qt поддерживает во вторых котролах Material Design, но не знаю, насколько он реализует данный функционал. Felgo - QML based фреймворк реализует подобные вещи.
Но что касается drag and drop фунционала, то тут нужно создавать плавающий rectangle, который будет летать за пальцем, правильно высчитывать все позиции всех элементов и т.д. В общем достаточное количество кода будет.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • AK
    April 1, 2025, 11:41 a.m.
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup