QML tree view from a model - c++

I'm quite new to QML and I'm struggling to find any information how to render a tree model in a simple way (although horizontally, rather than vertically):
Seems like QML only supports list data structures from models. Is there any way to do it?

Figured it out on my own. The trick is to use DelegateModel, Repeater and Row/Column layouts, not TreeView.
Create a component which will show your current node using DelegateModel.
Use a Repeater to create children - let the component dynamically create another instance of itself, assign the current node as the rootIndex of newly constructed DelegateModel, and set it as the model for the Repeater.
By properly using layouts, you can position your nodes exactly as in the attached image.

Related

Implement Scrolling for QQuickView containing multiple QQuickPaintedItems

So recently I started a project where I want to display a graph showing relations between different datasets.
The graph consists of Edges and Nodes, while both of these classes inherit QQuickPaintedItem and override the paint(QPainter* painter) method. I have another class which stores all Nodes and Edges in two QLists. When I want to display the Graph, the paint method for every Node and Edge is called and is being painted into a QQuickView. I do this by setting a Qml File for the QQuickView (important: The loaded qml File has nothing to do with the actual Graph, everything is painted with the paint-method from QQuickPaintedItem and i haven't exposed anything related to the Graph to Qml.
The Qml File is being used to display some controls for the graph) and getting the content item (QQuickItem* from method contentItem()) of the QQuickView and setting it as a parent for every Node and Edge. All of this works fine for me.
Now to my problem: Some of the graphs I wanna display are bigger than my actual view i want to display them in, so to solve this i want to implement horizontal and vertical Scrolling for my View. However I haven't found anything that seems to solve my problem due to the fact that i can't use predefined Qml-Layouts like ListView aso. for my Graph.
Does anyone have an idea how I could implement Scrolling? Is there a way to implement this for my QQuickView or is there are way to expose my two Lists of Nodes and Edges to Qml and implement Scrolling in there?
(If you need some code, feel free to ask. I don't think it makes sense to share some code right now since I'm searching for an idea what to do about this topic in the first place).
Thank you!
If you don't mind rendering everything, what you need is Flickable.
Set the viewable width and height of your Flickable (explicitely, with anchors, or with layouts) and nest your custom item in it and set the contentWidth and contentHeight of the flickable depending on the total size or your custom item.

Change the order of ColumnLayout or ListView and save / restore it

I'm working on a stream overlay that extracts information out of a game (flight simulator) and displays it on the screen. Right now I'm using Qt in conjunction with a *.html to render the overlay. It is all working really well, however I wanted to add some customization options for the users of my overlay software and I figured the best way would be to render the Overlay in QML.
The main part of the overlay is a row that contains around 8 "elements" that display the relevant data.
One thing that should be customized is the order of the elements in the row. But I really have no idea how to implement this feature. I've seen a few posts and tutorials on how to customize the order in a View using the DelegateModel. However right now it's not a view but QML Components inserted in a RowLayout due to the fact that they are all different components (e.g. some of the images are actually animated for which I'm using a component that uses Canvas2D to draw the images). I guess I could figure out a way to store those elements in a model using the Loader Component to display the content in QML. But even then I'm not entirely sure how to store and restore the order of the elements. As far as I can tell the DelegateModel only changes the View and not the underlying model.
Any suggestion or best practice to accomplish my goal would be highly appreciated.

Can I connect rectangle to C++ model?

I have connected QML's ListView to my C++ model and it updates when model changes which is cool. However I don't want to display my data in ListView bur rather in custom way in rectangle (ideally a plain view which doesn't exist).
How could I do this?
The issue I see obviously is rectangle is not a view and there is other plain view which allows custom drawing. Is there way around it?
Addon
Following up on the answer and comment, let me give context why I am doing it. I have various information and if I use list, I will have to use multiple lists on one screen which doesn't look good. What I want to implement is what I would call 'document view'. Header goes here, title goes there, data here and footer here. It is a custom presentation of my model's data.
#Folibis, I like your first point. It seems like if do something like:
Rectangle
{
Text { text: mySingleton.getFruitName() }
Text { text: mySingleton.getFruitPrice() }
}
Note I have intentionally not included anchors or geometry to keep focus on my question but assume price appears next to fruit name.
Does this mean if I update fruit name or price of the exact same object in model else where in GUI, the above will be updated automatically?
You have several ways to implement custom drawing. I can't really imagine what data can be supplied to Rectangle but anyway:
You can create custom Item in C++, for example singleton an fetch needed data from it.
Rectangle {
width: mySingleton.getWidth();
height: mySingleton.getHeight();
color: mySingleton.getColor();
}
You can create custom element derived from QQuickPaintedItem. All you need is reimplement QQuickPaintedItem::​paint(QPainter * painter) to draw your own rectangle. It's simpliest way to create ow element but not efficient since it uses QPainter.
Create custom element deriver from QQuickItem. You will need to reimplement QSGNode * QQuickItem::​updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData). That's fast and reliable way but requires OpenGL experience.
Also as (1), but painting on Canvas element

Possible to copy/duplicate a CCLayer?

I'm creating a scene that will have a list of items, like a shop menu. Ideally, I want to create the layout for a single shop item in CocosBuilder. Then, in code, I access that layout and make multiple copies for all my shop items, which can be added to the scene.
Is this possible in cocos2d-x?
if all of items you have are CCSprite, you can use this code for duplicating it:
CCSprite* copy=CCSprite::spriteWithTexture(source->getTexture());
you can duplicate all sprites in a layer then add them to a new layer.

Refresh of a QGraphicsScene / QGraphicsView

I am troubled by the following:
I am working with an interactive QGraphicsScene that needs to render the graphical representation of an SQL query, based on the users' operations, such as: add something to the query (a table, a new column, something else) or remove something from the query (a keyword, a table, a column ...). The changes of the scene must be displayed after the operation, and also the "logic layer" of the application needs to track the operations the user did, since the "rendering" of the query is done by the "logic" layer (ie: the "logic layer" creates all the QGraphicsItemGroup derived objects which at a later stage, after all the logic layer components were built, are being added to the graphics scene of the query and put on the window).
The problem that occurs is the following: right now I did not manage to find any usable solution to present a query after a change in the smoothest possible way.
Allow me to link in a screenshot for further explanation:
Let's suppose the user wants to remove the PERSON.NAME column from the query. What happens in the application:
the user clicks on the "remove" (small red X after the column name) button of the PERSON.NAME columns' graphic item
the Graphics View senses this operation, sends the REMOVE column from the graphic system to the "logic layer" (the "model")
the logic layer on its turn removes the corresponding "logic layer" object representing the PERSON.NAME column,
And here the trouble starts:
the entire graphic (yes, everything) is re-rendered by the logic layer creating the graphic items for the same query, without PERSON.NAME
then I have to create a new window which has a new QGraphicsScene object together with a QGraphicsView
insert the re-rendered objects' graphic items representing the query, (but now without the PERSON.NAME column) into the new QGraphicsScene with addItem()
and now replace the central widget of the application with the new window.
and now you can see, that indeed, in the query the PERSON.NAME is not there anymore and all the graphic elements that were below PERSON.NAME were moved up on the screen.
Obviously this is not a good solution, there is an ugly flickering when I change the window, but I simply did not find a better solution to this problem till now.
So I am asking for your help in order to identify what improvements can be done to this methodology of updating the screen upon removal (addition) of a new element knowing the background information above, without a new window. Obviously other, mroe generic graphic related comments are welcome too.
Thanks,f
Based on the information from the question and the comments, a couple of things you could consider:
First thing is that you need to get rid of creating a new Window and a new QGraphicsView when refreshing.
I suppose this is the main reason for flickering. Keep your UI structure unmodified and only modify the scene.
You could use one of these approaches:
Either create a new QGraphicsScene and set it as the view's scene, or call clear() on
the existing scene. Then recreate your QGraphicsItems from your native model and make sure that all your pointers and references are updated.
Another approach would be to have the QGraphicsScene update your native model when something changes, to avoid the need to recreate the whole scene from scratch. For example, let the QGraphicsScene handle the deletion of the QGraphicsItem when the user clicks the delete icon, and then let the scene update your native model to reflect this change.
Yet another approach would be to discard your native model, and use the QGraphicsScene with its QGraphicsItems as your model. Implement serialization etc. in the scene class. This avoids the need to synchronize the two models. The drawback is that your graphics independant logic is then much tighter coupled to the QGraphicsScene, which you might not want. Depending on your code size, this might also be a lot of work.
I would start with 1., since it seems to be the easiest way to go based on your existing approach. If you still come across weird issues with pointers and object ownership, try to isolate them and ask on SO :)