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
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-transitionsI 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!
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 niceWell 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!