Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt QML: in-file definition of reusable objects

I have a Qml component that is reasonably large so that I want to make it a reusable component but it is too small/non-generic such that I want to avoid creating its own .qml file. It seems like Components are the right method to define reusable objects in the same file, but when I do that I don't know how to access and change properties of the contained objects.

More specifically, imagine having an in-file definition like this

Component {
id: myReusableComponent
// This cannot be set because component does not allow properties
// (Would work fine in a separate file myReusableComponent.qml)
// property alias string innerText: innerText.text
Rectangle {
    id: rect
    width: 200
    height: 200
    color: "red"
    Text {
        id: innerText
        text: "Want to set text later"
    }
}

How can I reuse this component later, while changing some properties of it? I know the following is not valid syntax, but I want to use it similarly to this:

Loader {
id: hello
sourceComponent: myReusableComponent
item.innerText: "Hello" }

Text { text: "Some other stuff in between" }

Loader {
id: world
sourceComponent: myReusableComponent
item.anchors.left: hello.right
item.rect.width: 100
item.rect.color: "blue"
item.innerText: "World" }

Loader {
id: excl
sourceComponent: myReusableComponent
item.rect.color: "green"
item.innerText: "!!!" }
etc...

Any ideas? Or is there a fundamentally different way of doing this?
What I essentially want is reusability of QML objects that are defined in place while still being able to change the properties of them.

This seems to be related but does not solve the problem of creating multiple objects. Repeaters seem to be useful but don't give me the flexibility I want.


NOTE: I will add some remarks about the answers here since they might get overlooked in the comments. I like all three answers from Blabbouze, derM and ddriver! I accepted Blabbouze's answer because it provides a concrete solution that is closest to what I was looking for. However, I also didn't know about the overhead of Loaders and might consider using a different approach after reading derM's answer. Finally, dynamic object creation as suggested by ddriver is not exactly what I was looking for but might be useful for others. Thanks everyone!

like image 405
mattu Avatar asked Nov 30 '16 14:11

mattu


People also ask

What is component onCompleted in QML?

This can be used to execute script code at startup, once the full QML environment has been established. The onCompleted signal handler can be declared on any object. The order of running the handlers is undefined. Rectangle { Component. onCompleted: console.

What is difference between Qt and QML?

QML is the language; its JavaScript runtime is the custom V4 engine, since Qt 5.2; and Qt Quick is the 2D scene graph and the UI framework based on it. These are all part of the Qt Declarative module, while the technology is no longer called Qt Declarative.

What is Qtqml?

The Qt QML module provides a framework for developing applications and libraries with the QML language. It defines and implements the language and engine infrastructure, and provides an API to enable application developers to extend the QML language with custom types and integrate QML code with JavaScript and C++.

What are QML components?

A component provides a way of defining a new type that we can re-use in other QML files. A QML component is like a black-box and interacts with the outside world through properties, signals and functions and is generally defined in its own QML file. (For more details, see the Component documentation).


2 Answers

I think you are looking for onLoaded() signal. It is emitted when the Loader have successfully created the Component.

You can then access your loaded type properties with item.

import QtQuick 2.7
import QtQuick.Controls 2.0


ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")


    Component {
        id: myReusableComponent
        Rectangle {
            id: rect
            property alias innerText: innerText.text

            width: 200
            height: 200
            color: "red"
            Text {
                id: innerText
                text: "Want to set text later"
            }
        }
    }


    Loader {
        id: hello
        sourceComponent: myReusableComponent
        onLoaded: {
            item.innerText = "hello"
        }
    }

    Loader {
        id: world
        sourceComponent: myReusableComponent
        anchors.left: hello.right
        onLoaded: {
            item.width =  100
            item.color =  "blue"
            item.innerText =  "World"
        }
    }
}
like image 91
Blabdouze Avatar answered Oct 23 '22 04:10

Blabdouze


At first I will try to explain why you do not want to do it

If the Component is reused - even in one file and not only by the means of Repeaters, ListViews e.t.c - you should consider creating a seperate file, to keep your file clean and readable.

If you create inline-components, and then create the instances by the means of a Loader just to be able to create them, they come with an overhead, that can be tolerated if the component is really enormous, or you really need the Loader for the possibility to change the source dynamically. Otherwise, it helps you only to make everything complex, and raise the ressource consumption.

There is almost no penalty for outlaying the code into multiple files. So do it, when ever you have a reasonable, logically enclosed unit - and especially when you are going to reuse it multiple times in a file.

Use the Component when you are planning to use this as a delegate. Some perfere to directly declare the delegate where it is used. I do so, if there are only few properties to set.
Especially if you have multiple Views that share the same delegate-prototype, it is a good idea to use the Component.

If you really need to use a Loader (for the reasons you need a Loader and not for non-dynamic object creation) then you can use a Component to avoid the need of the onLoaded-event. It enables you to preconfigure the Component, and set the event-handlers without the need of a Connections-Object.

Ok - now a short answer to your question:
You can create instances of a Component by many means:

  • Loader
  • Views (ListView, GridView, Repeater ...)
  • Dynamic Object Creation with JS (componentID.createObject(parent)) at any point where you can execute JS code.

Read: http://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html#creating-objects-dynamically

like image 31
derM Avatar answered Oct 23 '22 05:10

derM