Providing a text property with a Rectangle in a Qt Quick Application
-
Hi all,
In this simple example I've tried to provide a text with a Rectangle (as a Rectangle hasn't a text property):
main.qml:
... GreenSquare { BlueSquare { width: 12 anchors.fill: parent anchors.margins: 8 text: "(1)" } } ...
GreenSquare.qml:
import QtQuick 2.8 Rectangle { width: 80 height: 80 color: "green" border.color: Qt.lighter(color) }
BlueSquare.qml:
import QtQuick 2.8 Rectangle { width: 80 height: 80 color: "blue" border.color: Qt.lighter(color) Text { x: 25; y: 25 text: "X" } }
But I get an error saying that 'text" is a non-existing property!
How would you solve the issue please? -
Rectangle { id: root width: 80 height: 80 color: "blue" border.color: Qt.lighter(color) propety string text: "X" Text { x: 25; y: 25 text: root.text } }
OR
Rectangle { width: 80 height: 80 color: "blue" border.color: Qt.lighter(color) property alias text: innerText.text Text { id: innerText x: 25; y: 25 text: "X" } }
The stock Rectangle doesn't have a 'text' property and so doesn't your own Rectangle. You have to declare the new properties you want. The object of the Text type doesn't automatically become a property called 'text' of its parent. Nor can you reach the 'text' property of the Text inner object when you instantiate your BlueSquare (in this case in main.qml). You must either propagate the value from outside to the inner object as in the first solution, or expose the inner object's property as an alias in the top level object as in the second solution. These are the two basic ways to reach a component's inner level objects' properties and they are very important to understand - you will need them constantly.
Notice that in the first solution you can only set the value but not get it; you will get only the root item's property value. In the second solution you have full acces the the inner item's property value so that if it's for example a user editable component which will be edited by the user you can get the new value. In that kind of situation, if you wanted to use the first solution, you would have to make another property:
Rectangle { id: root width: 80 height: 80 color: "blue" border.color: Qt.lighter(color) propety string text: "X" property string updatedText: innerText.text Text { id: innerText x: 25; y: 25 text: root.text } }
which looks quite uneffient compared to the proerty alias solution. And indeed, using an alias is more efficient because it just makes the original property visible and doesn't use an intermediate property.
As a side note, you try to set width and height explicitly in many places where it would be better to do something else. For your own components which might get instantiated in several places, here BlueSquare and GreenSquare, give implicitW/H instead. In main.qml you try to set a BlueSquare width and at the same time fill the parent. "anchors.fill: parent" means that the whenever the parent is sized or resized so is the child. Therefore the explicit "width: 12" doesn't have effect.
-
@Eeli-K
Thanks for your useful explanation.If we state the matter in a nutshell, we can say, using the second solution is like referencing in C++. That is, when we use a reference for a variable (here a property), we have it and also can change it. Right?
About the side note, for example, BlueSquare.qml:
I think the usage of implicitWidth or width (explicit width) has no difference, because, when for example, BlueSquare's width is explicitly initialised, and then is instantiated in another component and will be given a width there, that new width will override the initialised value (80). The same happens if we set the properties implicitly as well.
Another side note: As you consider the screenshot, and I reread your previous comments on that, I have a couple of entry points in the project, but since in the main.cpp we load main.qml, the program starts from there and other entry components in this project, are useless.
How would you use one of them in a simple way in this project? -
we can say, using the second solution is like referencing in C++.
In a way, yes. Alias gives an alias name to a property inside a component file and makes it available as if it were a property of the type where it is declared. Declaring it in a component's top level object makes it visible to other files.
I think the usage of implicitWidth or width (explicit width) has no difference, because, when for example, BlueSquare's width is explicitly initialised, and then is instantiated in another component and will be given a width there, that new width will override the initialised value (80). The same happens if we set the properties implicitly as well.
Yes, in this case. But they are still conceptually different and may make difference in some situations. One real world case is using layout types - implicit size is strongly recommended in the documentation if you put an item into a layout. Personally I prefer a conceptually clean solution but it's not wrong to do "whatever works" here.
How would you use one of them in a simple way in this project?
Just rename them: row.qml -> RowExample.qml (or MyRow.qml or whatever you like). Then they can be instantiated in other files://main.qml RowExample { }
Of course they work only if there are no other problem factors involved, but from what I saw in them they should work.
-
Declaring it in a component's top level object makes it visible to other files.
You're likely depicting something called 'global declarations' in C++, I got the subject, but here I mixed up the names "component", "object" and "file"! I'm toying with the three as one thing.
Do you agree I declare the width and height of the "all" components which will be used/instantiated in some other component implicitly, "always"?
//main.qml
RowExample {
}I know and you've told me that. I meant the way they are, without renaming them.
In this case I think the only use of them is to, say, start the program by having the C++ file load them. -
You're likely depicting something called 'global declarations' in C++
I would rather talk about public interface and private implementation. In a component the top level object's properties are public but everything else, i.e. children and their properties, is private. You can make a child's property public by declaring an alias.
And here we come to terminology... I'm struggling with that myself and unfortunately haven't been able to be consistent. See http://doc.qt.io/qt-5/qtqml-syntax-basics.html and http://doc.qt.io/qt-5/qtqml-documents-topic.html and http://doc.qt.io/qt-5/qml-qtqml-component.html to see how Qt uses those terms. Basically component, type and file could be loosely interchangeable but "object" is a bit more difficult. Is it a run-time object only or is a declared entity in a qml file always an object even if it's not instantiated yet by the engine? Seeing how the Qt docs use that I would say that it's the latter. In C++ world there's a strict difference between class and object. A QML type is kind of a class.
In this case I think the only use of them is to, say, start the program by having the C++ file load them.
I'm not sure if it's strictly the only possible use for them but in general you're right.