QML TreeView - how to apply delegate to certain items in the tree?



  • Hello guys,

    I'm a newbie at QML and QT so please bear with me,
    I've created a model in C++ for a TreeView QML type through which I can view the model and apply a suitable delegate for my items.

    so what I need to do is to have 2 icons in front of each child item except for "Objects detected count" and "Objects in pallet count" and the parent items (i.e. communication status,...etc.) instead of having the icons in front of each item. The tree is shown below

    alt text

    Here is the delegate file

    MyTreeDelegate.qml

    import QtQuick 2.0
    import QtQuick.Controls 1.4
    
    Component   //must wrap rectangle inside a component QML type to be called from a different file
    {
        Rectangle
        {
            id:baseRec
            color: "transparent"
            //    color: ( styleData.row % 2 == 1 ) ? "white" : "#60e6dd"
            height: 5
            //    border.color: "black"
            //    border.width: 1
            
            MouseArea
            {
                anchors.fill: parent
    
                onClicked:
                {
                    console.log("row: ",styleData.row)
                    console.log("column: ",styleData.column)
                    console.log("index: ",styleData.index)
                }
            }
            
            Text
            {
                anchors.verticalCenter: parent.verticalCenter
                text: styleData.value === undefined ? "" : styleData.value
                // The branches don't have a description_role so styleData.value will be undefined
            }
    
            Image
            {
                height: baseRec.height
                width: height
                id:rightImage
                source: "images/RedOff.png"
                anchors
                {
                    verticalCenter:baseRec.verticalCenter
                    right:baseRec.right
                }
            }
    
            Image
            {
                id:leftImage
                height: baseRec.height
                width: height
                source: "images/GreenOff.png"
                anchors
                {
                    verticalCenter:baseRec.verticalCenter
                    right:rightImage.left
                }
            }
        }
    }
    
    

    main.qml

    import QtQuick 2.0
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Window 2.2
    import MyTreeModel 1.0  //from qmlRegister in C++ code
    
    Rectangle
    {
        id:mainRec
        MyTreeModel
        {
            id: theModel
        }
    
        MyTreeDelegate
        {
            id: theDelegate
        }
    
        TreeView
        {
            id: treeView
            style: TreeViewStyle    //for styling the tree
            {
                backgroundColor: "white"
                alternateBackgroundColor:"white"
                //branch delegates are used for delegating the arrow on the left
            }
            //think of what to do on a double click or single click
            //think of how to delegate only specific children
    
            headerVisible: false    //to hide the header
            anchors.fill: mainRec    //to fill rectangle
    
            model: theModel
    
            itemDelegate: theDelegate
    
            onItemDelegateChanged:
            {
    //        console.log("current index: ", index)
            }
    
            TableViewColumn
            {
                role: "name_role"
                title: "Status"
            }
        }
    }
    
    

    I've tried using styleData.row and styleData.index but I can't figure out how to use them to my advantage.

    Thanks for reading through all of this.



  • Make your delegate react to states. This might help get you moving again:
    https://qmlbook.github.io/en/ch05/index.html#states-and-transitions

    I also use ternary expressions (not states) to do things such as:

    Rectangle{
            color               : PathView.isCurrentItem ? color : currentColor; //"black" : "red"
    

    I don't see why you couldn't add a property:

    TreeView { 
    id: treeView;
    property bool activeComms: true;
    

    and in your delegate:

    source: parent.activeComms === false ? "images/GreenOff.png" : "images/RedOff.png";
    

    or

    source: parent.activeComms ? "images/GreenOn.png" : "images/RedOn.png";
    


  • @Haitham I haven't used Controls 1 but it looks like you can simply add the visible property value to Image elements:

    Image
    {
        visible: !(styleData.value === "Objects detected count" || styleData.value === "Palletizing Status" || [etc...])
    

    Or instead of text values try those styleData.row, styleData.index or something else which might work.



  • @Eeli-K That's what I wrote at the end of the post bro

    The problem with styleData.row as far as I can tell that it depends if the parent items are expanded or not.

    for instance, "Vision status" might return 1 and might return 3 depending if "Communication status" is expanded or not.

    Unfortunately, I don't know how to benefit from styleData.index though it would come in handy later on.

    I will keep you posted if I've figured out a solution to this problem. Thanks for your help

    Update:

    using visible with styleData.depth===0 made the icons disappear next to the items

    styleData.row, as I mentioned earlier, depends if the parent items are expanded so it's pretty useless and I've tested it.



  • @6thC
    So I've followed your advice and checked states and they are pretty nice.

    I've made a very dummy program to test them and they worked well!

    alt text

    the application consisted of the following code (not the whole code)

            Image
            {
                id: red
                source: "images/RedOff.png"
                anchors
                {
                    centerIn: root
                }
            }
    
            Image
            {
                id: green
                source: "images/GreenOff.png"
    
                anchors
                {
                    left:red.right
                    verticalCenter:root.verticalCenter
                }
            }
    
            states:
                [
                State
                {
                    name: "GreenOn"
                    PropertyChanges { target: green; source:"images/GreenOn.png"}
                    PropertyChanges { target: red; source:"images/RedOff.png" }
                },
    
                State
                {
                    name: "RedOn"
                    PropertyChanges { target: green; source:"images/GreenOff.png"}
                    PropertyChanges { target: red; source:"images/RedOn.png" }
                },
    
                State
                {
                    name: "BothOff"
                    PropertyChanges { target: green; source:"images/GreenOff.png"}
                    PropertyChanges { target: red; source:"images/RedOff.png" }
                }
            ]
    
            //Control buttons
            Row
            {
                spacing: 5
    
                Button
                {
                    id: btnOn
                    text: "On"
                    onClicked: root.state = "GreenOn"
                }
    
                Button
                {
                    id: btnOff
                    text: "Off"
                    onClicked: root.state = "RedOn"
                }
    
                Button
                {
                    id: btnReset
                    text: "Reset"
                    onClicked: root.state = "BothOff"
                }
    

    the next challenge would be to control the states from C++ side and choose specific items to edit.

    as I wrote above, I've checked on styleData.row and styleData.index but still can't figure out how to use them to my advantage, if you've got any other tip I would be grateful!



  • Oh hey. Cool, great to hear.

    I was using states and transitions mostly for GUI animations, as per my example below, I use transitions to animate the control color (in this case from State"inSpec" to "maximum" and "warning" to "maximum" states, I want an endless flashing red animation:

    Transition {
              from: "inSpec"
              to: "maximum"
              SequentialAnimation {
                  loops: Animation.Infinite;
                  ColorAnimation { from: "transparent"; to: "red"; duration: 300 }
                  ColorAnimation { from: "red"; to: "transparent";  duration: 300 }
              }
          },
          Transition {
              from: "warning"
              to: "maximum"
              SequentialAnimation {
                  loops: Animation.Infinite;
                  ColorAnimation { from: "transparent"; to: "red";  duration: 300;        }
                  ColorAnimation { from: "red"; to: "transparent"; duration: 300;         }
                  }
          }
    

    So I do control my data via c++ and the state of that data is how I control my GUI. The effect is c++ data is controlling the fronted but it's just the frontend reacting to the data inputs that happen to come from c++... I could have just as easily connected onto QML data etc.



  • @6thC
    pretty nice

    Well as I am using static images (4 images only) I don't think I would need that in my application

    I would be so grateful if you would check my other post here

    I am trying to do something similar if I understood you correctly, I want the state of images to react to what happens in the back-end (C++ Obviously) but to do that I need to know how to address each item individually, so I thought QModelIndex holds the answer but I am stuck as shown in the post above.

    The big picture:

    I want to chage the state of the images of one item according to a condition met in the back-end (e.g. if the communication is established it would change the icon next to "Connected" from GreenOff to GreenOn) and so on for the rest of the code.

    Thanks a lot for your time, I owe you big time bro!



  • No problems, I'm no expert but I think I can bail you out :-) - keep in mind this is just how I've solved this for myself. There are other resources for "QML States and Transitions" and "QML to C++" or "C++ to QML" around that are quite better than myself... saying that.

    All you really need from there is to control the state based on some condition/expression.
    So from there... a bit of scripting to set the object state either from within the control or externally doesn't really matter, but Connect is your friend.

    I have my logic in another class for this example. My state is controlled via qml script:
    example (QML):

    function adjustState(control, incomingData){
    ...
      if( <incomingData evaluation for ok data>){
                        // aok
                        control.state =  "inSpec";
                    }
    ...
      if( <incomingData evaluation for data entering close to max limit>){
                        control.state =  "warning";
    }
    ...
    
      if( <incomingData evaluation for data value breached max limit>){
                        control.state =  "maximum";
    }
    

    Nothing hard to process - I do try to limit the processing work I throw to JavaScript engine but some is inevitable and this is pretty simple stuff for it and necessary.

    My actual code there is a bit more complicated but I can probably only give you that as an example and hopefully you see it's setting a control's state property on some condition.

    So then it's just connect the object to the c++ data.

    I use C++ object instances. Lets pretend we have an C++ object that is referenced in the QML context such as: (c++)

    context->setContextProperty(QStringLiteral("ObjectInstanceCpp"), &m_MyObject);
    

    now QML has that context, I can use m_MyObject from QML by using "ObjectInstanceCpp".

    So let's take a peek at this c++ class ... I'll let you fill in the rest but this gives you a basic idea.

    class SomethingSomethingName: public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int SimpleInt READ getSimpleIntProperty WRITE setSimpleIntProperty NOTIFY simpleIntPropertyChanged)
    

    in somewhere c++ we've declared SomethingSomethingName and it's now stored in m_MyObject, and as above I've made QML aware of this via setContextProperty.

    From QML script I can then do things like:

    Connections { target: ObjectInstanceCpp;
    onSimpleIntPropertyChanged:{ adjustState(qmlControl, ObjectInstanceCpp.SimpleInt); }
    

    or even:

    onSimpleIntPropertyChanged:{ 
    var myLimit=20;
    if(ObjectInstanceCpp.SimpleInt >myLimit ){
    qmlControl.state = "maximum";
    } 
    }
    

    Hope this helps, this is not tested as I've had to cut a lot of my own application context out and rename things but hopefully helpful.



  • @6thC did your application consist of a tree and you could apply a delegate to specific items? because that's where I'm stuck now. I am still researching on how to connect QML with C++ and as usual if I figured out a way of applying delegates to specific items based on a logical condition (like your warning and maximum states), I will let you know.

    Thanks for your time bro!


Log in to reply
 

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