Implement Scrolling for QQuickView containing multiple QQuickPaintedItems - c++

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.

Related

Qt resizeEvent(), setGeometry() and update() - what order are they called and what calls what, when?

I can't seem to find a guide on Qt's widget / layout system that explains how it all fits together. Only examples of what to do in typical use-cases.
Slightly more specifically:
When my widget (which has a QGridLayout of child widgets) is resized, I want to:
layout the children, so obtain their sizes,
do some stuff before everything gets repainted
have the children painted, presumably by calling update()
How do I slot number 2 between 1 and 3? If the answer is, "Your design is bad, don't do this", then this is my specific problem:
I have a PlotFrame class that shows Cartesian graphs. It houses a PlotPane (the main area of the graph) and a dynamic number of axes. The PlotPane and Axis classes are QWidgets laid out with a QGridLayout and I'm using explicit double buffering so I can display transient mouse-driven features on top of the base buffered image.
When the PlotFrame is resized, the number 2 in the list above is this:
Create the buffered image of the axes whose ticks and labels dynamically depend on the length of the axis on the screen, this also calculates positions of grid lines for display in the main PlotPane.
Get the grid line positions just calculated and pass them to the PlotPane.
Create the buffered image of the PlotPane.
Once this is done, I'm happy to have Qt do its asynchronous stuff, and to deal with any transient graphical features in each of the widgets' paintEvent() functions.
Thanks in advance. I'm want to get a better understanding of how this all works but don't really want to be digging in the source code if I can help it. I find that Qt's own docs lack depth / are confusing to navigate. There are some great guides I've found (eg. flylib.com) but they are well out of date, therefore misleading. I'm currently using Qt6.
EDIT: After further experimentation, it seems that the parent widget (PlotFrame) resizeEvent() gets called after its children have been laid out and before they are painted. Is this so? Can I rely on this?

Can we create a graph in QLabel? If not, what's the best way to create graphs in a dialog box?

I need to create a graph in my dialog box which displays as I've shown in the image. I need some guidance to move forward with this. Example graph is shown here
So I am developing an application in Qt creator, and I need to display a graph in a dialog box that would give better understanding of different stores and comparisons among them.
Firstly I need some guidance to know what type of Qt widget should be used and then how to create those blocks as a graph.
I am not that familiar with qt grasp, can someone help me out with this?
Qt's designated widget to render images is indeed QLabel. Just render your graph, load it up on a QPixmap by calling for example QPixmap::fromImage, and then update your QLabel by calling QLabel::setPixmap.

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.

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 :)

Efficient method for finding object in map based on coordinates

I am building an editor using C++/Qt which has a click-and-drag feel to it. The behavour is similar to schematic editors (Eagle, KiCAD, etc), Microsoft Visio, or other programs where you drag objects from a toolbar into a central editing area.
My problem is that when the user clicks inside the custom widget I want to be able to select the instance of the box-like object and manipulate it. There will also be lines connecting the boxes together. However, I can't decide on an efficient method for selecting those objects.
I have two main thoughts on how to do the programming for this: The first is that the widget which is drawing the entire editor would simply encapsulate every one of the instances of the box. The other is to have each instance of the box (which is in my Model) carry with it an instance of a QWidget which would handle rendering the box (which would be in my View...but it would end up being strongly attached to the model). As for the lines connecting them, since they don't have a square bounding boxes they will have to be rendered by the containing widget.
So here is the summary of how I see this being done:
The editor widget turns into a container which holds the widgets and the widgets process their own click events. The potential issues here are that I don't know how to make the custom widget turn into a layout which lets click-and-drag functionality.
The editor widget takes care of all the rendering and processes the mouse clicks (the easier way in that I don't have to worry about layout...its just selecting the instances efficiently that I don't know what would be best).
So, now that there is a bit of background, for the 2nd method I plan on having each box-like instance having a bounding rectangle and the lines being represented by 3-4 pixel wide bounding rectangle segments (they are at 90 degree angles). I could iterate through every single box and line, but that seems really inefficient.
The big question: Is there some sort of data structure I can hold rectangles in and link them to widgets (or anything else for that matter) and then give it two coordinates (such as mouse coordinates) and have it spit me out the bounding box or linked object that those coordinates are inside of?
It sounds like your real question is about finding a good way to implement your editor, not the specifics of rectangle intersection performance.
You may be interested in Qt's "Diagram Scene" example project, which demonstrates the QGraphicsScene API. It sounds like a good fit for the scenario you describe. (The full source for the example ships with Qt.)
The best part is that you still don't have to implement hit testing yourself, because the API already provides what you are looking for (e.g., QGraphicsScene::itemAt()).
It's worth noting that internally, QGraphicsScene uses a simple iterative method to perform hit tests. As others have pointed out, this isn't going to be a serious bottleneck unless your scenes have a lot of individual items.