Some questions on a QML transformation project



  • Hi all,

    Here is a simple transformation program that I employ it as in the example.

    It works as expected and I read the example's comments many times and it's fine but doesn't provide all details well, so I have some questions:

    In ClickableImage.qml we have:

    import QtQuick 2.8
    
    Image {
        id: root
        signal clicked
    
        MouseArea
        {
            anchors.fill: parent
            onClicked: root.clicked()
        }
    }
    

    1- What is that Image used for in the code? I don't think it's a built-in type; it looks like a class. But don't we have a built-in type named Image? If it's like a class name so we can change the name to whatever we like, not?

    2- Is that signal clicked like a slot and root.clicked() a signal to it?

    3- What if we use anchors.fill: root instead of anchors.fill: parent?

    Thanks.



  • @tomy You should use Qt's own documentation. For example in Creator's Help view there's an Index in which you can type Image and it shows some hits.

    http://doc.qt.io/qt-5/qml-qtquick-image.html

    You must read Qt's own documentation, not just the qmlbook. You don't need to understand all at once, just read the general parts of it once and search for details later. Sometimes re-reading the general parts is good for experienced developers, too. Continual use of class/type documentation is necessary for everyone.

    Q2: signal is a signal, root.clicked() calls that signal. Remember that in C++ Qt signals are just functions at the low level. When you emit the signal it's like calling a function which calls the connected slots. Does it help you to understand if you had code like this:

    Image {
        id: root
        signal clicked
        onClicked: console.log("signal 'clicked' was sent and this function caught it")
    
        MouseArea
        {
            anchors.fill: parent
            onClicked: root.clicked()
        }
    }
    

    What happens, do you understand it?

    Q3: nothing. 'parent' is a special property of each object in a QML tree. Naturally it's the, well, parent of that object. The image called 'root' is here the parent of the mouse area. See Item's parent property in the documentation and http://doc.qt.io/qt-5/qtquick-visualcanvas-visualparent.html.

    About 'anchors.fill' - you can again search in the documentation index. 'anchors.fill' will lead you to "anchors group" of Item which says: "Note: You can only anchor an item to siblings or a parent. For more information see Anchor Layouts." And Anchor Layouts is a link which will lead to another document which you should read if you use anchors.



  • @Eeli-K

    You should use Qt's own documentation. For example in Creator's Help view there's an Index in which you can type Image and it shows some hits.

    http://doc.qt.io/qt-5/qml-qtquick-image.html

    Yes, thanks.
    I got from this that, Image is a type in Qt and it can be used to load many different types of images. specifying width and height are optional.

    You must read Qt's own documentation, not just the qmlbook. You don't need to understand all at once, just read the general parts of it once and search for details later. Sometimes re-reading the general parts is good for experienced developers, too. Continual use of class/type documentation is necessary for everyone.

    I understand.

    Image {
        id: root
        signal clicked
        onClicked: console.log("signal 'clicked' was sent and this function caught it")
    
        MouseArea
        {
            anchors.fill: parent
            onClicked: root.clicked()
        }
    }
    

    What happens, do you understand it?

    Yes. I didn't run it but l say my thought:
    When a click on the MouseArea occurs the signal clicked() in the root will be triggered by the code onClicked: root.clicked() in MouseArea body. Correct?

    Q3: nothing. 'parent' is a special property of each object in a QML tree. Naturally it's the, well, parent of that object. The image called 'root' is here the parent of the mouse area. See Item's parent property in the documentation and http://doc.qt.io/qt-5/qtquick-visualcanvas-visualparent.html.

    I understood some things from that.

    We have some basic types (like int, double) that are used in code I think. And also some object types like: Image, Rectangle, MouseArea and Text.

    If we have objects/items called X, Y, Z and S, (E.g., one of the objects above), in this code:

    X {
        width: 320
        height: 480  // No coordinates, so both x and y are 0
    
        Y {
            y: 64   // x is 0
            width: 256
            height: 256
    
            Z {
                x: 192
                y: 64   
                width: 128
                height: 128
            }
        }
    
        S {
            x: 64
            y: 192
            width: 256
            height: 256
        }
    }
    

    Is it right:
    Here the parent of Y and S is X and Y and S are siblings. The parent of Z is Y but we can change its parent. (by?)



  • @tomy

    When a click on the MouseArea occurs the signal clicked() in the root will be triggered by the code onClicked: root.clicked() in MouseArea body. Correct?

    Yes, and because the root has 'clicked' signal it also has the correponding signal handler 'onClicked' automatically. Now we set the handler; it prints to the log output. When the mouse area is clicked the text is printed.

    Here the parent of Y and S is X and Y and S are siblings. The parent of Z is Y but we can change its parent. (by?)

    Yes. It's not usually necessary or reasonable to change a parent manually.



  • Thanks for your info.



  • Hi,

    In the Transformation.qml file we have:

    import QtQuick 2.8
    
    Item {
        width: bg.width
        height: bg.height
    
        Image {
            id: bg
            source: "/background.png"
     }
    
        MouseArea {
            id: backgroundClicker
            anchors.fill: parent
            onClicked: {
                circle.x = 84
                box.rotation = 0
                triangle.rotation = 0
                triangle.scale = 1.0
            }
        }
    
       ...
    } 
    

    First the part

    Item {
        width: bg.width
        height: bg.height
    

    seems useless because if I remove these and make the Image as the first item, the program runs with no changes. So what need is there for that please?

    Neither id: bg nor id: backgroundClicker in the code seems to be needed as well.

    Also I modified the main.qml code to:

    import QtQuick 2.8
    import QtQuick.Window 2.2
    import QtQuick.Controls 2.0
    import QtQuick.Controls.Styles 1.4
    
    Window {
        visible: true
        width: 700
        height: 600
        title: qsTr("Transformation")
    
        Column {
             spacing: 20
    
         Transformation {
                 id: transformation
               }
    
         Button{
             Text: "test_overlap"
             style: ButtonStyle
             onClicked: transformation._test_overlap()
              }
    
         Button {
           Text: "test_transformed"
            style: ButtonStyle
            onClicked: transformation._test_transformed()
          }
    
         Button {
            // Text: "Quit"
             onClicked: close()
         }
       }
    }
    

    Using only

    Transformation {
                id: transformation
              }
    

    the program runs through a process, but what are its steps briefly, please?
    I also tried to add a text and style to the buttons declared above. But got errors!



  • @tomy

    width: bg.width
    height: bg.height
    seems useless

    In this case they may be useless and in general when you write a component (a qml file) you shouldn't explicitly give the top level item's height & width. It's a good habit to give implicit size instead. Unfortunately those things are a bit complicated but in short, width and height may be calculated by the engine and may differ from what you explicitly give. Implicit width and height are some sensible size which the visible component could have if it's not for example stretched to fill available space or if there is no explicit size given.

    IDs are necessary only if the object must be known by name somewhere, but it may still be good for self-documentation. Come up with a descriptive name for an object and you may thank yourself later when you read your own code because you will know without further thinking why you have made the object.

    import QtQuick.Controls 2.0
    

    means that Controls 2 are used, not Controls 1. Change it to

    import QtQuick.Controls 1.4
    

    and you can use styles. I think that the qmlbook uses only Controls 1. I tend to use Controls 2.

    Button{
             Text: "test_overlap"
    

    must be

    Button{
             text: "test_overlap" // property names must always begin with lower case, Text is a type name!
    


  • @Eeli-K
    Thank you.

    you shouldn't explicitly give the top level item's height & width.

    Did you mean "the top level item height & width", please?

    IDs are necessary only if the object must be known by name somewhere, but it may still be good for self-documentation. Come up with a descriptive name for an object and you may thank yourself later when you read your own code because you will know without further thinking why you have made the object.

    Yes, you're right. Thanks.

    import QtQuick.Controls 2.0
    

    means that Controls 2 are used, not Controls 1. Change it to

    import QtQuick.Controls 1.4
    

    What does it mean please, I can't understand well. Isn't Controls 2 used? Where is it stated?

    // property names must always begin with lower case, Text is a type name!

    Good info.
    And when I use that style for the buttons, they won't be appeared on output!



  • @tomy

    Did you mean "the top level item height & width", please?

    I was a bit unclear. When you write a component (i.e. a qml file which can be used from other qml files) you've got the top level item first. For that item you can give implicitWidth/Height if there are reasonable default values for the size of that component. But you shouldn't give width/height properties explicitly.

    //MyItem.qml
    import QtQuick...//whatever you need to import
    Item{ // top level item, rectangle, button or whatever
        id: control
        //width: 100 No!
        implicitWidth: 100 // Yes
        Rectangle{ // inner items go here
    }
    

    When you use that component in another file you can there give w/h explicitly.

    //main.qml
    ...
    MyItem {
        width: 150 //yes
    }
    

    In many situations it doesn't matter whether you give explicit or implicit size and where you give it. In some situations it matters. It's better to use implicit in components themselves and explicit when you instantiate a component. However, look at what is done in the Material theme's Button.qml in Components 2:

    T.Button {
        id: control
        implicitWidth: Math.max(background ? background.implicitWidth : 0,
                                contentItem.implicitWidth + leftPadding + rightPadding)
        implicitHeight: Math.max(background ? background.implicitHeight : 0,
                                 contentItem.implicitHeight + topPadding + bottomPadding)
    ...
    contentItem: Text {
    ...
    }
    background: Rectangle {
            implicitWidth: 64
            implicitHeight: 48
            ...
            width: parent.width
            height: parent.height - 12
            ...
    }
    ...
    }
    

    The background rectangle has both implicit and explicit size! The control itself has only implicit size which is max of background's implicit size and contentItem's implicit size (which is the text's size by default). When you instantiate the Button and don't give explicit size the implicit size is used. The engine gives the actual width and height values based on implicit size. The background will get the same size which by default will be the same as control's implicit size. If, on the other hand, the developer gives explicit size (width&height) when the Button is instantiated, implicit size of the control is bypassed and both the component itself and the background will get that explicit size. The Text element will be elided if the explicit width is smaller than text's implicit width.



  • @tomy

    What does it mean please, I can't understand well. Isn't Controls 2 used? Where is it stated?

    There are two sets of UI controls in Qt Quick, Controls 1 and Controls 2. Usually only one of them is selected for a given application, although they can be used together. Under the hood they work differently and they have different look&feel. You must import the one you are using (or both if you are using both in the same component). In Qt 5.9 the version of Controls 1 is 1.4 and the version of Controls 2 is 2.2. You can import an older version (at least for Controls 2), too, if you don't use features which require a newer version.

    I used Button from Controls 2. Both Controls 2 and Controls 1 have a type named Button which both have a property named text. Therefore a simple button works if you change the import statement from version 1.4 to 2.2 or vice versa. However, only Controls 1 use the 'style' property for styling. Therefore it didn't work when only Controls 2 was imported.



  • Thank you.
    Here is main.qml and styles don't work yet! That is, when I use them this way no button will be shown on output.

    import QtQuick 2.8
    import QtQuick.Window 2.2
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    
    Window {
        visible: true
        width: 700
        height: 600
        title: qsTr("Transformation")
    
        Column {
             spacing: 10
    
         Transformation {
                 id: transformation
               }
    
        Button {
             id: t_o_button
             text: "test_overlap"
             style: ButtonStyle
             onClicked: transformation._test_overlap()
              }
    
         Button {
             id: t_t_button
             text: "test_transformed"
             style: ButtonStyle
             onClicked: transformation._test_transformed()
          }
    
         Button {
             id: quit_button
             text: "Quit"
             style: ButtonStyle
             onClicked: close()
         }
       }
    }
    

    In programs, for example like this, how should use the components in main.qml? It seems that every component has the instances of another component in itself and we instantiate the most top component, on a hierarchy when each has instances of the the lower one. Right?





  • @Eeli-K

    About Controls1 styles: http://doc.qt.io/qt-5/qtquick-usecase-styling.html

    Thank you, done.

    About using components: http://doc.qt.io/qt-5/qtqml-syntax-basics.html

    Thanks it was useful. But not exactly spotting what I meant!
    I didn't mean parent-child relationship.

    Look please, the program, when run, starts from somewhere and meets main.qml, ClickableImage.qml and Transformation.qml. The the output will be shown.
    I meant, where in the components above firstly the program is started, then where it goes and where again until it finishes and shows the result on output.



  • @tomy Maybe I now understand your question. Do you want to know how the objects and the object structure is created? In C++, javascript and many other languages there's a clear order of things which you can follow by reading the code. It starts with main.cpp and you can continue from there, read the code and tell what happens. But QML is declarative in nature. It means that in the code you tell what you want to get but how it's actually done is up to the QML engine. There's no predetermined initialization order which you can rely on. The code means for example:

    "I want to get a structure of visual items where there's a top level window which has this and this control elements inside it in these visual places. The color of this one will change according to the state of that one. When this button is pressed such and such should happen." There are several possible ways how this can be achieved and you don't need to know it. The engine could for example read the code and go through the object structure several times and do different initialization steps each time. What you can rely on is that after the UI is rendered and it starts listening to user interactions everything is initialized according to the intentions you expressed in the code.

    Logically and in practice initialization must begin somewhere; the starting point is usually main.qml but you give the name in (usually) main.cpp to the engine. The object tree will have the topmost object in main.qml as the root. In your main.qml the engine instantiates Window, inside it a Column, inside that a Transformation and Buttons. When it instantiates Transformation it must of course instantiate Item, Image and MouseArea which are inside that Transformation, and so on. But in which order everything actually is initialized and when everything is ready is up to the engine.

    Did that answer your question or did you have something else in mind?



  • @Eeli-K

    Did that answer your question or did you have something else in mind?

    It did, yes. I understood the initialisation and it's logical because when no, say, button is clicked why initialising it!

    For completing my understanding please also take a look at this:

    When I run that program, it firstly goes to main.cpp and loads main.qml from there.
    In main.qml we have a window, it will be set and an instance of Transformation which is created by:

    Transformation {
                id: transformation
              }
    

    (the id: transformation is not even mandatory for having an instance of the component Transformation, I suppose)

    When we created an instance this way, then the engine/system/even loop (whatever), goes to the definition/declaration of that component (Transformation).

    It sees that it has an Item as root, an Image, a MouseArea and a few ClickableImage "instances", so it therefore goes to the ClickableImage component (ClickableImage.qml) and sees an Image and MouseArea there too.

    After these the engine/system gets back to the main.qml and puts whatever Transformation component has on the screen followed by the three buttons declared below.

    Not very wrong?



  • @tomy

    I understood the initialisation and it's logical because when no, say, button is clicked why initialising it!
    By "initialization" I mean the phase before the object tree is fully constructed so that all objects which are reached by the declared tree are ready and all properties have expected values. So a button is initialized even if it's never clicked.

    Otherwise what you said is a logical way to see it. However, we must keep in mind that this may still be different from the actual execution order of initialization. For example it's undermined in which order a structure like

    Item{
        //properties...
        Rectangle{
            //properties...
            Rectangle{}
        }
        Button{
            //properties...
        }
    }
    

    is actually initialized: whether Item's all properties will be ready before the outer Rectangle's or whether the Button is ready before the Rectangles. The idea of the declarative nature of QML is that you don't need to know.



  • Thank you. I think I got all that was needed.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.