Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. A basic Question
QtWS25 Last Chance

A basic Question

Scheduled Pinned Locked Moved Solved QML and Qt Quick
qmlstyesheet
30 Posts 5 Posters 10.9k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J.HilkJ Offline
    J.HilkJ Offline
    J.Hilk
    Moderators
    wrote on last edited by
    #1

    Hello everyone,
    after years of QWidget only creation I'm finally dabbling in QML.

    My goal is it to create a "Button" item that changes it's displayed img depending on 2 things.
    If the mosuecurser is hoveriung over it and if a state ist set or not.

    In QWidgets I would use a Stylesheet for that, but that doesn't seem to be a thing for QML so I set different sources:

    Item {
        id: root
        signal activated(int id)
    
        property int m_ID: 0
        property bool m_connected: false
    
        property string imgDefault: ""
        property string imgConnected: ""
        property string imgHoverD: ""
        property string imgHoverC: ""
    
        function setConnected(connected){
            m_connected = connected
            if(m_connected)
                mainImg.source = imgConnected
            else
                mainImg.source = imgDefault
        }
    
        Image {
            id: mainImg
            source: imgDefault
            anchors.fill: parent
        }
    
        MouseArea{
            anchors.fill: parent
            hoverEnabled: true
    
            onClicked: root.activated(m_ID)
    
            onEntered: {
                if(!m_connected){
                    mainImg.source = imgHoverD
                }else{
                    mainImg.source = imgHoverC
                }
            }
    
            onExited: {
                if(!m_connected){
                    mainImg.source = imgDefault
                }else{
                    mainImg.source = imgConnected
                }
            }
        }
    }
    

    Is this the right way to do it?
    It feels wrong :(


    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


    Q: What's that?
    A: It's blue light.
    Q: What does it do?
    A: It turns blue.

    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      It's OK, although the general "philosophy" behind QML is for it to be declarative: you define what you want, and Qt does it for you. Your implementation is imperative: you state exactly what you want.

      Here is an alternative, more declarative solution:

      Item {
        id: root
        property bool isConnected: false
      
      Image {
        id: mainImg
        source: {
          if (isConnected) {
            if (mouseArea.containsMouse)
              return imgHoverC
            else
              return imgConnected
          } else {
            if (mouseArea.containsMouse)
              return imgHoverD
            else
              return imcDefault
          }  
        }
        anchors.fill: parent
        }
      
        MouseArea {
          id: mouseArea
          hoverEnabled: true
        }
      }
      

      With that (you can remove other functions you created), the Image element will automatically respond to any changes in both mouse area and isConnected property.

      (Z(:^

      J.HilkJ 1 Reply Last reply
      5
      • mrjjM Offline
        mrjjM Offline
        mrjj
        Lifetime Qt Champion
        wrote on last edited by
        #3

        Hi
        Cool. How does it know what code to run when
        property bool isConnected changes?
        the source: {} is aware it uses isConnected inside and hence reacts?

        sierdzioS 1 Reply Last reply
        1
        • mrjjM mrjj

          Hi
          Cool. How does it know what code to run when
          property bool isConnected changes?
          the source: {} is aware it uses isConnected inside and hence reacts?

          sierdzioS Offline
          sierdzioS Offline
          sierdzio
          Moderators
          wrote on last edited by
          #4

          @mrjj said in A basic Question:

          the source: {} is aware it uses isConnected inside and hence reacts?

          Is that a question to me?

          Yes, it is aware. That's how QML engine works, it builds up an "understanding" of which property update should trigger which bindings to be recalculated. In this case, "source" will be recalculated each time root.isConnected is changed, and each time mouseArea.containsMouse changes.

          Ah, I just noticed the OP has property bool m_connected: false, I should have used that instead of adding my isConnected. Anyway, that's a small change to make.

          (Z(:^

          mrjjM 1 Reply Last reply
          2
          • sierdzioS sierdzio

            @mrjj said in A basic Question:

            the source: {} is aware it uses isConnected inside and hence reacts?

            Is that a question to me?

            Yes, it is aware. That's how QML engine works, it builds up an "understanding" of which property update should trigger which bindings to be recalculated. In this case, "source" will be recalculated each time root.isConnected is changed, and each time mouseArea.containsMouse changes.

            Ah, I just noticed the OP has property bool m_connected: false, I should have used that instead of adding my isConnected. Anyway, that's a small change to make.

            mrjjM Offline
            mrjjM Offline
            mrjj
            Lifetime Qt Champion
            wrote on last edited by mrjj
            #5

            @sierdzio
            Yes it was as you seem to really know QML and i was wondering how it works.
            Sorry for intruding a little :)
            I had no idea that binding was that effective so it would know inside code it uses some
            property and hence should execute.
            Very cool.

            sierdzioS 1 Reply Last reply
            1
            • sierdzioS sierdzio

              It's OK, although the general "philosophy" behind QML is for it to be declarative: you define what you want, and Qt does it for you. Your implementation is imperative: you state exactly what you want.

              Here is an alternative, more declarative solution:

              Item {
                id: root
                property bool isConnected: false
              
              Image {
                id: mainImg
                source: {
                  if (isConnected) {
                    if (mouseArea.containsMouse)
                      return imgHoverC
                    else
                      return imgConnected
                  } else {
                    if (mouseArea.containsMouse)
                      return imgHoverD
                    else
                      return imcDefault
                  }  
                }
                anchors.fill: parent
                }
              
                MouseArea {
                  id: mouseArea
                  hoverEnabled: true
                }
              }
              

              With that (you can remove other functions you created), the Image element will automatically respond to any changes in both mouse area and isConnected property.

              J.HilkJ Offline
              J.HilkJ Offline
              J.Hilk
              Moderators
              wrote on last edited by
              #6

              @sierdzio wow, thanks.

              I remade that item(class?) about 4 times already, each time it has less code in it and becomes faster.

              Your example works splendently!
              I technically don't even need the setConnected function.

              Will take a while to get my head around this different style of QML ...

              Thanks again, time to dig back in!


              Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


              Q: What's that?
              A: It's blue light.
              Q: What does it do?
              A: It turns blue.

              sierdzioS 1 Reply Last reply
              0
              • mrjjM mrjj

                @sierdzio
                Yes it was as you seem to really know QML and i was wondering how it works.
                Sorry for intruding a little :)
                I had no idea that binding was that effective so it would know inside code it uses some
                property and hence should execute.
                Very cool.

                sierdzioS Offline
                sierdzioS Offline
                sierdzio
                Moderators
                wrote on last edited by
                #7

                @mrjj said in A basic Question:

                @sierdzio
                Yes it was as you seem to really know QML and i was wondering how it works.
                Sorry for intruding a little :)
                I had no idea that binding was that effective so it would know inside code it uses some
                property and hence should execute.
                Very cool.

                Hey, no problem, I'm happy to explain :-)

                (Z(:^

                mrjjM 1 Reply Last reply
                0
                • J.HilkJ J.Hilk

                  @sierdzio wow, thanks.

                  I remade that item(class?) about 4 times already, each time it has less code in it and becomes faster.

                  Your example works splendently!
                  I technically don't even need the setConnected function.

                  Will take a while to get my head around this different style of QML ...

                  Thanks again, time to dig back in!

                  sierdzioS Offline
                  sierdzioS Offline
                  sierdzio
                  Moderators
                  wrote on last edited by
                  #8

                  @J.Hilk said in A basic Question:

                  @sierdzio wow, thanks.

                  I remade that item(class?) about 4 times already, each time it has less code in it and becomes faster.

                  Your example works splendently!

                  Great, good to hear.

                  I technically don't even need the setConnected function.

                  Yes, it should not be necessary. If you need to modify the value of m_connected, even from other file where your button is added, it will be enough to modify it via dot syntax. The change signal is emitted automatically. So, assuming your button is saved in MyButton.qml file, you can do this:

                  // some other QML file, for example main.qml
                  MyButton {
                    id: myButton
                    m_connected: true
                  }
                  

                  Thanks again, time to dig back in!

                  Happy coding! :-)

                  (Z(:^

                  1 Reply Last reply
                  2
                  • sierdzioS sierdzio

                    @mrjj said in A basic Question:

                    @sierdzio
                    Yes it was as you seem to really know QML and i was wondering how it works.
                    Sorry for intruding a little :)
                    I had no idea that binding was that effective so it would know inside code it uses some
                    property and hence should execute.
                    Very cool.

                    Hey, no problem, I'm happy to explain :-)

                    mrjjM Offline
                    mrjjM Offline
                    mrjj
                    Lifetime Qt Champion
                    wrote on last edited by
                    #9

                    @sierdzio
                    Super
                    Have a tiny little one extra
                    It knows to recalc source when MouseArea changes simply because its inside its scope?
                    I have same issue as J.Hilk trying to apply widget logic to QML and its really not. :))

                    sierdzioS 1 Reply Last reply
                    1
                    • mrjjM mrjj

                      @sierdzio
                      Super
                      Have a tiny little one extra
                      It knows to recalc source when MouseArea changes simply because its inside its scope?
                      I have same issue as J.Hilk trying to apply widget logic to QML and its really not. :))

                      sierdzioS Offline
                      sierdzioS Offline
                      sierdzio
                      Moderators
                      wrote on last edited by sierdzio
                      #10

                      @mrjj said in A basic Question:

                      It knows to recalc source when MouseArea changes simply because its inside its scope?

                      Now, how scopes work in QML is a bit complicated, I'm sure you'll encounter lots of WTF? moments :-)

                      Yes, in this case the mouse area is in scope (the Image can access it's properties by calling it by ID, in my example the id is mouseArea). But in general, all QML engine needs is to get the onPropertyChanged signal - it does not matter from where it is coming from, it will simply register that signal as "hey, Property changed it's value, so I need to update the value here, too". It can be some global context property, QML singleton, other QML component, or even some C++ QObject that was exposed/ connected (via context property, or Connections element for example) and is visible to Image component.

                      Some things to be aware of here:

                      • the binding will be recalculated each time some (relevant) property changes. This can sometimes mean a lot of updates per second, for example if you bind to mouse.x (one tends to move the mouse quite a lot :-))
                      • thus, it is important not to overdo it (for example, if you create a Q_PROPERTY in c++, remember not to emit changed() signal when the property value has not changed: if (newValue == oldValue) return;. Qt Creator automatically generates good code for properties, thankfully)
                      • if you (at some point) assign a value to property in JavaScript, the binding is broken. It won't update anymore. Here's a short example:
                      Item { id: obj1; height: obj2.height * 2 }
                      Item { id: obj2 }
                      MouseArea {
                        onClicked: obj1.height = obj2.height * 3 // Boom!
                        // The binding is broken when you click the mouse area.
                        // Why? You tell obj1 that the height should be set
                        // to a new value, right here right now. To QML, it is
                        // the same as if you set it to obj1.height = 150.
                        // Constant value
                      }
                      

                      In the example, if you want to change the binding to obj1.height: obj2.height * 3 and keep it updating when obj2.height changes, you can use Binding element.

                      (Z(:^

                      1 Reply Last reply
                      1
                      • mrjjM Offline
                        mrjjM Offline
                        mrjj
                        Lifetime Qt Champion
                        wrote on last edited by
                        #11

                        Oh yes lots of those moments :)
                        Aha, so if u set to a constant value it wont auto update.
                        What if multiple objects are using the same binding?
                        Is it then disabled for all or only for that mouse area or is it globally?

                        sierdzioS 1 Reply Last reply
                        0
                        • mrjjM mrjj

                          Oh yes lots of those moments :)
                          Aha, so if u set to a constant value it wont auto update.
                          What if multiple objects are using the same binding?
                          Is it then disabled for all or only for that mouse area or is it globally?

                          sierdzioS Offline
                          sierdzioS Offline
                          sierdzio
                          Moderators
                          wrote on last edited by
                          #12

                          @mrjj said in A basic Question:

                          What if multiple objects are using the same binding?

                          Each binding is used by single object. They are declared on the "receiving end", so to speak. Example:

                          Item { height: someObj.height + 15 }
                          Item { height: someObj.height + 15 }
                          Item { height: someObj.height + someObj.height }
                          

                          These are 3 separate bindings. If you overwrite the height value in first Item with some constant, remaining 2 will still work and update automatically.

                          (Z(:^

                          mrjjM 1 Reply Last reply
                          1
                          • sierdzioS sierdzio

                            @mrjj said in A basic Question:

                            What if multiple objects are using the same binding?

                            Each binding is used by single object. They are declared on the "receiving end", so to speak. Example:

                            Item { height: someObj.height + 15 }
                            Item { height: someObj.height + 15 }
                            Item { height: someObj.height + someObj.height }
                            

                            These are 3 separate bindings. If you overwrite the height value in first Item with some constant, remaining 2 will still work and update automatically.

                            mrjjM Offline
                            mrjjM Offline
                            mrjj
                            Lifetime Qt Champion
                            wrote on last edited by
                            #13

                            @sierdzio
                            Super. Then its all clear.
                            Also the global nature of it was escaping me.
                            like you can use
                            MyButton {
                            id: myButton
                            m_connected: true
                            }
                            with out any extern/include/add to scope extras.

                            Thank you.

                            sierdzioS 1 Reply Last reply
                            0
                            • mrjjM mrjj

                              @sierdzio
                              Super. Then its all clear.
                              Also the global nature of it was escaping me.
                              like you can use
                              MyButton {
                              id: myButton
                              m_connected: true
                              }
                              with out any extern/include/add to scope extras.

                              Thank you.

                              sierdzioS Offline
                              sierdzioS Offline
                              sierdzio
                              Moderators
                              wrote on last edited by
                              #14

                              @mrjj said in A basic Question:

                              with out any extern/include/add to scope extras.

                              Yes, although there are some rules here. Only top-level properties (defined in root element of any given QML file) are visible outside of the component. Also, no IDs are accessible outside of current QML file (with a few tiny exceptions). So:

                              /// Some other qml file
                              MyButton {
                                m_connected: true // Works fine
                                mouseArea.hoverEnabled: false // Error. The ID 'mouseArea' is not visible outside of MyButton.qml file,
                                // and additionally hoverEnabled is not a top-level property
                              }
                              

                              (Z(:^

                              mrjjM 1 Reply Last reply
                              0
                              • sierdzioS sierdzio

                                @mrjj said in A basic Question:

                                with out any extern/include/add to scope extras.

                                Yes, although there are some rules here. Only top-level properties (defined in root element of any given QML file) are visible outside of the component. Also, no IDs are accessible outside of current QML file (with a few tiny exceptions). So:

                                /// Some other qml file
                                MyButton {
                                  m_connected: true // Works fine
                                  mouseArea.hoverEnabled: false // Error. The ID 'mouseArea' is not visible outside of MyButton.qml file,
                                  // and additionally hoverEnabled is not a top-level property
                                }
                                
                                mrjjM Offline
                                mrjjM Offline
                                mrjj
                                Lifetime Qt Champion
                                wrote on last edited by mrjj
                                #15

                                @sierdzio
                                oh
                                so only first level of scope ?
                                Item {
                                can_be_seen
                                Item2 {
                                all here is private?
                                }
                                }

                                well maybe its good IDs are not global visible or one could make some crazy spaghetti code very easy.

                                sierdzioS 1 Reply Last reply
                                0
                                • mrjjM mrjj

                                  @sierdzio
                                  oh
                                  so only first level of scope ?
                                  Item {
                                  can_be_seen
                                  Item2 {
                                  all here is private?
                                  }
                                  }

                                  well maybe its good IDs are not global visible or one could make some crazy spaghetti code very easy.

                                  sierdzioS Offline
                                  sierdzioS Offline
                                  sierdzio
                                  Moderators
                                  wrote on last edited by
                                  #16

                                  @mrjj said in A basic Question:

                                  @sierdzio
                                  oh
                                  so only first level of scope ?

                                  Yes, only first level, unless I am mistaken ;-) Writing from memory now. And this applies to using the component somewhere else (in a different QML file). Within single file, there are no such strict visibility restrictions.

                                  well maybe its good IDs are not global visible or one could make some crazy spaghetti code very easy.

                                  Yea, it can be a bit annoying in the beginning, but enforces some rather good practices in the long run.

                                  (Z(:^

                                  E 1 Reply Last reply
                                  0
                                  • sierdzioS sierdzio

                                    @mrjj said in A basic Question:

                                    @sierdzio
                                    oh
                                    so only first level of scope ?

                                    Yes, only first level, unless I am mistaken ;-) Writing from memory now. And this applies to using the component somewhere else (in a different QML file). Within single file, there are no such strict visibility restrictions.

                                    well maybe its good IDs are not global visible or one could make some crazy spaghetti code very easy.

                                    Yea, it can be a bit annoying in the beginning, but enforces some rather good practices in the long run.

                                    E Offline
                                    E Offline
                                    Eeli K
                                    wrote on last edited by
                                    #17

                                    @sierdzio said in A basic Question:

                                    well maybe its good IDs are not global visible or one could make some crazy spaghetti code very easy.

                                    Yea, it can be a bit annoying in the beginning, but enforces some rather good practices in the long run.

                                    I agree. Otherwise there's no private/public distinction in QML (and you can bypass even this visibility restriction runtime if you really want to) but I think it's reasonable to hide those inside IDs because otherwise it would encourage messy programming style with no real components. Now we at least have a possibility to have real "implementation details", some kind of data hiding. So sometimes it feels annoying but in the long run it's better.

                                    About the original problem, here's another possible solution. Not as nice and tidy as @sierdzio's but in some cases might it be clearer not to use nested if-elses, and if you have to change several properties based on the same conditions you would have to duplicate those conditions. Here you can just add another property to PropertyChanges.
                                    (changed image to rect to save some work...)

                                    import QtQuick 2.6
                                    import QtQuick.Controls 2.2
                                    import QtQuick.Layouts 1.1
                                    
                                    ApplicationWindow {
                                        visible: true
                                        width: 640
                                        height: 480
                                    
                                        ColumnLayout {
                                            id: columnLayout
                                            anchors.fill: parent
                                            Button{onClicked: root.isConnected=!root.isConnected}
                                            Item {
                                                id: root
                                                property bool isConnected: false
                                                Layout.fillHeight: true
                                                Layout.fillWidth: true
                                                Rectangle {
                                                    id: mainImg
                                                    anchors.fill:parent
                                                    
                                                    states:[
                                                        State{
                                                            name:"conn_mouse"
                                                            when:root.isConnected && mouseArea.containsMouse
                                                            PropertyChanges {
                                                                target:mainImg
                                                                color:"red"
                                                            }
                                                        },
                                                        State{
                                                            name:"conn_no_mouse"
                                                            when:root.isConnected&&!mouseArea.containsMouse
                                                            PropertyChanges {
                                                                target:mainImg
                                                                color:"green"
                                                            }
                                                        },
                                                        State{
                                                            name:"noconn_mouse"
                                                            when:!root.isConnected&&mouseArea.containsMouse
                                                            PropertyChanges {
                                                                target:mainImg
                                                                color:"blue"
                                                            }
                                                        },
                                                        State{
                                                            name:"noconn_nomouse"
                                                            when:!root.isConnected&&!mouseArea.containsMouse
                                                            PropertyChanges {
                                                                target:mainImg
                                                                color:"yellow"
                                                            }
                                                        }
                                                    ]
                                                }
                                    
                                                MouseArea {
                                                    id: mouseArea
                                                    hoverEnabled: true
                                                    anchors.fill:parent
                                                    onClicked: console.log("clicked")
                                                }
                                            }
                                        }
                                    }
                                    
                                    1 Reply Last reply
                                    3
                                    • mrjjM Offline
                                      mrjjM Offline
                                      mrjj
                                      Lifetime Qt Champion
                                      wrote on last edited by
                                      #18

                                      hi
                                      yes it might be annoying at first. Like UI of widgets being private but
                                      save you from pain down the road.

                                      states

                                      Oh that is a nice class. So that would be better if m_connected state were more complex
                                      or more than source property we wanted to changed on the clicked etc.

                                      ¨Thank you for sharing.

                                      1 Reply Last reply
                                      0
                                      • E Offline
                                        E Offline
                                        Eeli K
                                        wrote on last edited by
                                        #19

                                        Just one stylistic note... Quick Controls 2 standard library qml code uses this extensively so it may be at least good to know even if you don't want to use it. It's alternative syntax for nested if-else. Modifying my own code, just set the rectangle color (or in sierzio's code the image source):

                                        color: root.isConnected ? (mouseArea.containsMouse ? "red" : "green") :
                                               (mouseArea.containsMouse ? "blue" : "yellow")
                                        

                                        This is from the Material style's Button.qml:

                                        color: !control.enabled ? control.Material.hintTextColor :
                                                    control.flat && control.highlighted ? control.Material.accentColor :
                                                    control.highlighted ? control.Material.primaryHighlightedTextColor : control.Material.foreground
                                        
                                        1 Reply Last reply
                                        1
                                        • sierdzioS Offline
                                          sierdzioS Offline
                                          sierdzio
                                          Moderators
                                          wrote on last edited by
                                          #20

                                          Heh, actually the first version of my snipped used the question mark notation, but I changed it to if-else because I thought it would be more readable.

                                          It is definitely a good approach, and for simple cases I would recommend it - QML engine can optimize the question mark operator more heavily than if-else.

                                          (Z(:^

                                          1 Reply Last reply
                                          1

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved