0

I am relatively new to QML and Qt Quick and I am looking for recommendations on how to display the UI here:

enter image description here

This UI was made with QGraphicWidgets using Json as a model:

If you notice, the outside "Carriage Strength" container is basically a tree structure. A treeview list seems like the way to go, but I'm unsure if I can customize the view to contain the progress bar and html view (QWebEngineView). I will also need to be able to update specific components like the progress bar at runtime as well.

Any input is helpful. If you have any examples to point to would be great as well. Thanks in advance.

As requested below here is a sample of JSON which would be used to construct the model:

{ 
 "SequenceHistory": [
        {
            "Definition": {
                "ID": "carriage_strength",
                "DisplayName": "Carriage Strength",
                "TestArray": [
                    {
                        "ID": "sequence_one_setup",
                        "DisplayName": "Sequence 1 Setup",
                        "TestArray": [
                            {
                                "ID": "psm1_carriage_strength",
                                "DisplayName": "PSM1 Carriage Strength",
                                "Progress":  100,
                                "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                                "Status":  "Finish Failed",
                            },
                            {
                                "ID": "psm2_carriage_strength",
                                "DisplayName": "PSM2 Carriage Strength",
                                "Progress":  43,
                                "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                                "Status":  "In Progress"
                            },
                            {
                                "ID": "psm3_carriage_strength",
                                "DisplayName": "PSM3 Carriage Strength",
                                "Progress":  0,
                                "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                                "Status":  "Not Started"
                            },
                            {
                                "ID": "psm4_carriage_strength",
                                "DisplayName": "PSM4 Carriage Strength",
                                "Progress":  0,
                                "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                                "Status":  "Not Started"
                            }
                        ],
                    },

                ],
            }

    }

    ]
}

The display name indicates the name, the progress indicates the progress bar percentage, the html indicates the html to be displayed in the WebEngineView, and the status indicates the status label. Disregard any timing info that is in the screenshot, but not in the JSON. The main difference between this JSON and the screenshot is that I added a container "Sequence 1 Setup" inside the Carriage Strength container to show that the containers can contain containers as well as items.

Michael Japzon
  • 201
  • 1
  • 2
  • 10
  • I think the TreeView is a bit too restricted for this, since the outer level looks different then the inner level nodes. But maybe you can get away with it by setting visible false for some of the items (if outer level). It would be handy since the TreeView has this notion of expanding which I didn't find in any other QML widgets. You can throw whatever you want in the [itemDelegate](https://doc.qt.io/qt-5/qml-qtquick-controls-treeview.html#itemDelegate-prop) – Amfasis Dec 20 '18 at 20:24

1 Answers1

2

I make extensive use of such GUI elements, and the approach is always to represent the tree as lists of lists, that is, depending on the use case, either a ListView or a plain Repeater with a row or column container inside a Flickable, and then it is just delegate nesting.

The stock TreeView may cut it too, if you don't need access for fine-tuning and customizing the view.

I've personally ran into too much stiffness with many of the stock controls, they just don't work the way I need them to or outright lack the desired functionality, and unless it is something really standard, I prefer to roll out my own implementations, which is actually quite easy in QML.

Update:

Ok, now that you have provided an example data source, I can do a basic mock-up for you. BTW you are missing some commas in the JSON, and even tho in my example it is defined in-line, you can simply use JSON.parse(text) to get the same object from a string. Also note that modelData in each nested model refers to a different data object, basically to the respective array element index. And since only one of your arrays has more than one element, the example skips one level for the sake of brevity:

Window {
  visible: true
  width: 640
  height: 480
  title: qsTr("Hello World")

  property var jsondata: {
    "SequenceHistory": [
          {
            "Definition": {
              "ID": "carriage_strength",
              "DisplayName": "Carriage Strength",
              "TestArray": [
                {
                  "ID": "sequence_one_setup",
                  "DisplayName": "Sequence 1 Setup",
                  "TestArray": [
                    {
                      "ID": "psm1_carriage_strength",
                      "DisplayName": "PSM1 Carriage Strength",
                      "Progress":  100,
                      "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                      "Status":  "Finish Failed"
                    },
                    {
                      "ID": "psm2_carriage_strength",
                      "DisplayName": "PSM2 Carriage Strength",
                      "Progress":  43,
                      "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                      "Status":  "In Progress"
                    },
                    {
                      "ID": "psm3_carriage_strength",
                      "DisplayName": "PSM3 Carriage Strength",
                      "Progress":  0,
                      "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                      "Status":  "Not Started"
                    },
                    {
                      "ID": "psm4_carriage_strength",
                      "DisplayName": "PSM4 Carriage Strength",
                      "Progress":  0,
                      "HtmlToDisplay": "<html><body>This is guide instruction</body></html>",
                      "Status":  "Not Started"
                    }
                  ],
                },                    
              ],
            }                
          }              
        ]
  }

  ListView {
    id: rv
    anchors.fill: parent
    model: jsondata.SequenceHistory
    delegate: Rectangle {
      width: rv.width
      height: childrenRect.height // fit the expanding part
      color: "grey"
      Column {
        spacing: 2
        Row {
          spacing: 10
          Button {
            id: exp
            checkable: true
            text: checked ? "+" : "-"
            implicitWidth: 20
            implicitHeight: 20
          }
          Text {
            text: modelData.Definition.DisplayName
            anchors.verticalCenter: parent.verticalCenter
          }
        }
        Column {
          x: 20
          spacing: 2
          Repeater {
            // if not expanded model is null else skip one level
            model: exp.checked ? 0 : modelData.Definition.TestArray[0].TestArray
            delegate: Rectangle {
              width: rv.width
              height: childrenRect.height
              color: "lightgrey"
              Column {
                Row {
                  spacing: 10
                  Button {
                    id: exp2
                    checkable: true
                    text: checked ? "+" : "-"
                    implicitWidth: 20
                    implicitHeight: 20
                  }
                  ProgressBar {
                    value: modelData.Progress
                    from: 1
                    to: 100
                    anchors.verticalCenter: parent.verticalCenter
                  }
                  Text {
                    text: modelData.DisplayName
                    anchors.verticalCenter: parent.verticalCenter
                  }
                }
                Column {
                  visible: exp2.checked // hide if not expanded
                  TextArea {
                    width: 300
                    height: 200
                    text: modelData.HtmlToDisplay
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

And the result:

enter image description here

Note that you can also compartmentalize things by spreading the delegates across different sources, because as you style them property, they will get bigger. I put it in a single source just for the sake of the example.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • thanks for the advice. If I were to roll out my own implementation, what would that look like? I liked the model/view/delegate structure because I could bind that with my C++ objects. Is this still possible without using the canned views? If you have any sample code to look at that would be super helpful. – Michael Japzon Dec 20 '18 at 20:52
  • Well, if it is standard JSON, you can simply parse it in QML and presumably have arrays of data in it, which can be directly used as models in QML. Maybe post an example JSON as a data source. – dtech Dec 20 '18 at 20:55
  • I went ahead and posted some sample JSON. Thanks. – Michael Japzon Dec 21 '18 at 10:26
  • Thank you, I will roll out my own implementation. The biggest problem I have is that the container items like "Carriage Strength" and "Sequence 1 Setup" are really listviews that can contain other listsviews like itself or an actual item like "PSM1 Carriage strength". I found what seem to be the solution to that problem may be the nested models approach you described here: https://stackoverflow.com/questions/31996279/nested-list-in-qml-data-models-in-models I know it is asking a lot but can you demonstrate a nested model approach to my example? – Michael Japzon Dec 23 '18 at 06:53
  • I'd like to move to a C++ model which is in line with you nested model approach. Thanks. – Michael Japzon Dec 23 '18 at 06:57
  • JSON can already facilitate a nested model. The downsides are that the entire view has to be redrawn on any and all changes. You can choose to use listviews instead of repeaters even if your data source is a JS object. But you can also use the approach you linked if it fits your needs better, either way. – dtech Dec 23 '18 at 11:00
  • my GUI could have tons of objects based on the Json I so I believe I'll need to go with the linked approach since I'll just need to update some items without the full redraw. Do you have any examples of nested C++ models? Perhaps I can write up a new question. Thanks for all the help. – Michael Japzon Dec 23 '18 at 15:55
  • Well, it is your call, you will have to manually parse the json in C++ and populate your models. I have this generic object model in which every object serves as a model for its children, and can be populated from c++ or qml, even declaratively https://stackoverflow.com/questions/35160909/how-to-create-a-generic-object-model-for-use-in-qml – dtech Dec 23 '18 at 17:09