Home > Enterprise >  Qt Quick: How to avoid code-repeating during similar GUI elements creation in QML?
Qt Quick: How to avoid code-repeating during similar GUI elements creation in QML?

Time:12-20

First of all I would like to apologize for not having read all the QML documentation yet. But I have to ask here, cause it makes me sleepless, so let's get to the point:

I have GUI element that repeats 9 times. These are rectangles containing Image and MouseArea (each one). The only property that distinguishes them from each other is their position in the Cartesian system. The rest of properties are exactly the same for each item.

I have found this: https://doc.qt.io/qt-6/qml-qtquick-repeater.html

But in meantime I have started thinking, is there in QML possibility of declaration user classes and then calling this class objects using constructor?

At the moment my code is a few hundred lines long, althought my application seems to be very simply. I cannot even imagine, how it could look like if I'll start something more complicated and won't find method to make that shorter.

So the question is like in the title: How to avoid code-repeating during similar GUI elements creation in QML?

CodePudding user response:

Because you mentioned Rectangle, MouseArea, and Image, I wanted to make sure you check existing components, such as ItemDelegate and Button that support background, icon, pressed properties and have an onClicked signal.

In terms of render, not only is there Repeater, but there are ListView and GridView which also have support for flickable scrollbars.

In the following example, I created a MyDelegate.qml which contains a clickable delegate with a custom rectangular background and customizable icon and text whose 9 instances as controlled by the 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>

You can Try it Online!

CodePudding user response:

You have multiple options to define QML object types, have a look at the documentation.

Defining an Object Type with a QML File

To create an object type, a QML document should be placed into a text file named as .qml where is the desired name of the type.

// ImageButton.qml
import QtQuick 2.0

Item {
    width: 100
    height: 100

    Image {
        anchors.fill: parent
        source: "test.png"
    }

    MouseArea {
        anchors.fill: parent
        onClicked: console.log("Button clicked!")
    }
}

Since the file is named ImageButton.qml, this can now be used as a type named ImageButton by any other QML file within the same directory.

// myapplication.qml
import QtQuick 2.0

ImageButton {}

Defining an Object Type inline

You can use inline components to declare a new component inside of a file.

// 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.log("Button clicked!")
    }
}

ImageButton {}

Use the Component QML Type

I would rather use one of the above options, because this option has some limitation in the usage of your custom components. I would use the Component QML Type if the component will only be used as a delegate. Here is a quote from the documentation:

Sometimes, it can be inconvenient to create a new file for a type, for instance when reusing a small delegate in multiple views. If you don't actually need to expose the type, but only need to create an instance, Component is an option. But if you want to declare properties with the component types, or if you want to use it in multiple files, Component is not an option. In that case, you can use inline components.

  • Related