Прежде всего, я хотел бы извиниться за то, что еще не прочитал всю документацию по QML. Но я должен спросить здесь, потому что это не дает мне спать, так что давайте перейдем к делу:
У меня есть элемент GUI, который повторяется 9 раз. Это прямоугольники, содержащие Image и MouseArea (каждый). Единственным свойством, которое отличает их друг от друга, является их положение в картезианской системе. Остальные свойства абсолютно одинаковы для каждого элемента.
Я нашел это: https://doc.qt.io/qt-6/qml-qtquick-repeater.html
Но тем временем я начал думать, есть ли в QML возможность объявления пользовательских классов, а затем вызывать объекты этого класса с помощью конструктора?
На данный момент мой код состоит из нескольких сотен строк, хотя приложение кажется очень простым. Я даже не представляю, как это будет выглядеть, если я начну что-то более сложное и не найду способа сделать это короче.
Итак, вопрос как в заголовке: Как избежать повторения кода при создании похожих элементов GUI в QML?





Поскольку вы упомянули Rectangle, MouseArea и Image, я хотел убедиться, что вы проверяете существующие компоненты, такие как ItemDelegate и Button, которые поддерживают свойства background, icon, pressed и имеют сигнал onClicked.
С точки зрения рендеринга, есть не только Repeater, но и ListView и GridView, которые также поддерживают прокручиваемые полосы прокрутки.
В следующем примере я создал MyDelegate.qml, который содержит интерактивный делегат с настраиваемым прямоугольным фоном и настраиваемой иконкой и текстом, 9 экземпляров которого контролируются ListModel:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
ListModel {
id: listModel
ListElement { txt: "one"; ico: "frown.svg"; icol: "red" }
ListElement { txt: "two"; ico: "smile.svg"; icol: "green" }
ListElement { txt: "three"; ico: "smile.svg"; icol: "green" }
ListElement { txt: "four"; ico: "frown.svg"; icol: "red" }
ListElement { txt: "five"; ico: "smile.svg"; icol: "green" }
ListElement { txt: "six"; ico: "frown.svg"; icol: "red" }
ListElement { txt: "seven"; ico: "smile.svg"; icol: "green" }
ListElement { txt: "eight"; ico: "frown.svg"; icol: "red" }
ListElement { txt: "nine"; ico: "frown.svg"; icol: "red" }
}
ListView {
anchors.fill: parent
model: listModel
delegate: MyDelegate { }
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
}
}
// MyDelegate.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ItemDelegate {
width: ListView.view.width - 20
background: Rectangle {
color: pressed ? "orange" : index & 1 ? "#eee" : "#ddd"
}
text: txt
icon.source: ico
icon.color: icol
}
// smile.svg
<svg xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 32 32"><path d = "M16 29.8A13.8 13.8 0 1 1 29.8 16 13.815 13.815 0 0 1 16 29.8zm0-26.6A12.8 12.8 0 1 0 28.8 16 12.815 12.815 0 0 0 16 3.2zm-4.5 10.6a1.2 1.2 0 0 0 .608-.168 1.52 1.52 0 0 0 .464-.43 1.927 1.927 0 0 0 .278-.572 2.234 2.234 0 0 0 0-1.26 1.927 1.927 0 0 0-.278-.571 1.52 1.52 0 0 0-.464-.431 1.185 1.185 0 0 0-1.216 0 1.52 1.52 0 0 0-.464.43 1.927 1.927 0 0 0-.277.572 2.234 2.234 0 0 0 0 1.26 1.927 1.927 0 0 0 .277.571 1.52 1.52 0 0 0 .464.431 1.2 1.2 0 0 0 .608.168zm9.608-.168a1.52 1.52 0 0 0 .464-.43 1.927 1.927 0 0 0 .278-.572 2.234 2.234 0 0 0 0-1.26 1.927 1.927 0 0 0-.278-.571 1.52 1.52 0 0 0-.464-.431 1.185 1.185 0 0 0-1.216 0 1.52 1.52 0 0 0-.464.43 1.927 1.927 0 0 0-.277.572 2.234 2.234 0 0 0 0 1.26 1.927 1.927 0 0 0 .277.571 1.52 1.52 0 0 0 .464.431 1.185 1.185 0 0 0 1.216 0zm3.223 5.743l-.926-.379a7.863 7.863 0 0 1-7.39 4.976.166.166 0 0 0-.032 0 7.863 7.863 0 0 1-7.388-4.976l-.926.379a8.846 8.846 0 0 0 8.313 5.597.21.21 0 0 0 .035 0 8.848 8.848 0 0 0 8.314-5.597z"/><path fill = "none" d = "M0 0h32v32H0z"/></svg>
// frown.svg
<svg xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 32 32"><path d = "M16 29.8A13.8 13.8 0 1 1 29.8 16 13.815 13.815 0 0 1 16 29.8zm0-26.6A12.8 12.8 0 1 0 28.8 16 12.815 12.815 0 0 0 16 3.2zm6.318 20.026l.692-.723c-3.604-3.451-10.418-3.452-14.02 0l.692.723c3.19-3.057 9.448-3.055 12.636 0zM11.5 13.8a1.2 1.2 0 0 0 .608-.168 1.52 1.52 0 0 0 .464-.43 1.927 1.927 0 0 0 .278-.572 2.234 2.234 0 0 0 0-1.26 1.927 1.927 0 0 0-.278-.571 1.52 1.52 0 0 0-.464-.431 1.185 1.185 0 0 0-1.216 0 1.52 1.52 0 0 0-.464.43 1.927 1.927 0 0 0-.277.572 2.234 2.234 0 0 0 0 1.26 1.927 1.927 0 0 0 .277.571 1.52 1.52 0 0 0 .464.431 1.2 1.2 0 0 0 .608.168zm9.608-.168a1.52 1.52 0 0 0 .464-.43 1.927 1.927 0 0 0 .278-.572 2.234 2.234 0 0 0 0-1.26 1.927 1.927 0 0 0-.278-.571 1.52 1.52 0 0 0-.464-.431 1.185 1.185 0 0 0-1.216 0 1.52 1.52 0 0 0-.464.43 1.927 1.927 0 0 0-.277.572 2.234 2.234 0 0 0 0 1.26 1.927 1.927 0 0 0 .277.571 1.52 1.52 0 0 0 .464.431 1.185 1.185 0 0 0 1.216 0z"/><path fill = "none" d = "M0 0h32v32H0z"/></svg>
Вы можете Попробовать онлайн!
Конечно же, есть и подобные существующие объекты! В любом случае стоит приступить к созданию пользовательских типов. Честно говоря, в последние несколько дней я начал всерьез задумываться о том, стоит ли специализироваться на Qt, хотя он выглядит огромным. Гигантская библиотека + изучение нового языка
У вас есть несколько вариантов определения типов объектов QML, посмотрите документацию.
Для создания типа объекта документ QML должен быть помещен в текстовый файл с именем TypeName.qml, где TypeName — желаемое имя тип.
// ImageButton.qml
import QtQuick 2.0
Item {
width: 100
height: 100
Image {
anchors.fill: parent
source: "test.png"
}
MouseArea {
anchors.fill: parent
onClicked: console.info("Button clicked!")
}
}
Поскольку файл называется ImageButton.qml, теперь его можно использовать как тип с именем ImageButton в любом другом файле QML в том же каталоге.
// myapplication.qml
import QtQuick 2.0
ImageButton {}
Вы можете использовать встроенные компоненты для объявления нового компонента внутри файла.
// myapplication.qml
import QtQuick 2.0
component ImageButton : Item {
width: 100
height: 100
Image {
anchors.fill: parent
source: "test.png"
}
MouseArea {
anchors.fill: parent
onClicked: console.info("Button clicked!")
}
}
ImageButton {}
Я бы предпочел использовать один из приведенных выше вариантов, потому что этот вариант имеет некоторые ограничения на использование ваших пользовательских компонентов. Я бы использовал тип Component QML, если компонент будет использоваться только в качестве делегата. Вот цитата из документации:
Иногда бывает неудобно создавать новый файл для типа, т.к. instance при повторном использовании небольшого делегата в нескольких представлениях. если вы не на самом деле нужно выставить тип, но нужно только создать экземпляр, Компонент является опцией. Но если вы хотите объявить свойства с помощью типы компонентов, или если вы хотите использовать его в нескольких файлах, Component это не вариант. В этом случае вы можете использовать встроенные компоненты.
Вы можете создать новый файл qml, а затем инкапсулировать элемент управления (например, класс), который вы хотите