UI Widget drawing in order - c++

I have a custom UI Widget library I've created for a game I'm working on. I need to consider drawing order and retro fit it into what I currently have and not sure of the best way to do so. I have a UI class which loads the widgets from an xml file. It stores all widgets in a map where the key is the string name of the widget.
Widgets can hold other Widgets in a parent child relationship (so the Widget class has a map of Widgets where the name is the key again). When I draw I loop through the UI widget map and only call the Widget's Draw() function's that don't have parents (most top level). Within each Widget's Draw() it loops through it's children and calls their Draw() functions.
The question is what would be a good way to have a sort order variable for each widget work with drawing in the order of the sort order variable while keeping the maps, since I like the ease of finding widgets by the string key name?
Any ideas?

When you need multiple ways to order the same set of items, you have two choices:
Store both orderings, or
Store one ordering, and figure out the other one on the fly
Storing the second set of orderings is a way to pay with memory to save CPU cycles.
In your case, you have two orderings: the one implemented by the name-to-widget map, and the one for the drawing order. You have three implementation choices:
Store widgets in the map (i.e. ordered by name), and run a topological sort each time you need to figure out the drawing order - I assume here that the reason to order for drawing is to make sure that parents are drawn before children, implying the topological sort.
Store widgets in a list by the drawing order, and run linear lookups to get widgets by their name - This may be a good option when you draw widgets a lot, but look up by name very infrequently.
Store widgets in the map (i.e. ordered by name), and make a separate list of either widgets or widget names, arranged according to the drawing order - This double-accounting spends memory to buy back the CPU cycles that you would otherwise waste for re-ordering in the drawing order.

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?

Two qGraphicsScenes Vs. one qGraphicsScene + duplicate items

I have a QGraphicsScene and a QGraphiscView. I have many items in the scene, with the behavior defined for each item sets. I call this normal mode. So in normal mode I can interact and see the items in the scene from the view.
I want to create a X-ray mode, which basically means the position of all items in the scene is the same, but items and behaviors are different.
The question is which of the following is the better approach (or there is even better one) to switch between modes:
Create a new scene, add items to the new scene on the same position as the old scene. To switch to x-ray mode, put view(scene) to the new scene.
Keep only one scene, add items to the normal scene, but for switching to X-ray mode, only hide/show items.
From ease of implementation, efficiency and speed, which option is better? Is there any built-in options in Qt to do this?

Is it not possible to have multiple objects in Qt being painted simultaneously?

I read the following in the Qt Documentation.
Qt documentation on QPainter
The original question on SO, that I looked into.
So, I had two classes, with their own paint() functions. The paint functions would be called upon receiving their respective paint events, that were triggered on different and independent actions by the user. This worked fine.
Now for some reason, I need to show and update both the objects at the same time.
So simply, adding both of the items to the scene does not work. Only one of them is shown and updated. Refactoring the code is not an issue for me. I can re-arrange the two classes so that they are both drawn from one paint().
But this really makes me wonder then, and this is my question (for which I've googled a bit too), how are scenes with many dozens of objects then painted simultaneously (at least they give an illusion of concurrency)? Using threads somehow or through some time-based interleaving?
Maybe it's a silly question. I dunno.
It is indeed a silly question about an imaginary problem that doesn't not exist in reality. The graphics view will schedule consecutive draws for the items in the order needed to produce the desired result. Now if your code doesn't implement the desired result, that's a whole different subject. There is no concurrency, those are consecutive operations that only take place in the main thread.
If your drawing is very complex, draw using a secondary thread on a QImage and use the QImage as cache to draw your items in their respective paint functions.
Now for some reason, I need to show and update both the objects at the
same time.
What might that reason be? What does "at the same time" mean? In a single frame? Is a millisecond apart too much to qualify for "at the same time"?
Re QWidget painting: The paint events are delivered to individual widgets by the widget compositor. The way it works with the default raster back end is as follows: The topmost widget in the hierarchy is backed by a QImage. When any of the sub-widgets are to be repainted, the compositor delivers composite paint events to the widgets that overlay the area to be repainted. This is done sequentially as the compositor traverses the widget graph.
Re QGraphicsItem painting: The paint "events" are delivered to individual items by the scene. The items to be painted are selected basing on what area needs updating, what items were explicitly marked for update, etc. The painter is set up to correctly composite the item with the rest of the scene. The calls to paint are done sequentially as the scene traverses the item graph.
It would be, in general, impossible to do these in parallel due to data dependencies, and the fact that there are no requirements for the paintEvent or paint to be thread-safe.
Your problem is not directly related to this at all, you need to show a complete code example that reproduces your issue. Most likely your implementation of the item ignores some of the requirements for the item's behavior.

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

Question regarding image tiling in a QGraphicsView

This is related to one of my other questions.
If I am tiling a large image by creating a separate QGraphicsItem (with the raster data as its pixmap), how do I keep track of the QGraphicsItem's position within the scene? Obviously for raster data, it is important to keep all the tiles "touching" to make a continuous image and they also have to be in the right place so the image doesnt look jumbled.
Does each tile have to have positioning methods that move it in relation to it's neighbors on the top/left/bottom/right? This seems kind of clunky. Is there a better way to make them all move together?
In other words, if I pan the scene with scroll bars, or pick up the image and drag/move it around in the scene, I want all the tiles to also move and stay in the right position relative to each other.
What is the best approach for controlling the layout, which tiles need to be rendered (i.e. only the visible ones), and populating the data only once it is needed? Also, once a tile has been rendered, is the data from it ever dropped, and repopulated from the image file, say if it stays out of view for a while, then comes back later if someone pans to that section?
There are (more than) 2 ways of doing this:
Use QGraphicsItemGroup which
handles grouping of your tile items
for you. It moves, selects, updates
it's group members as if they are
one. I've never used it but from the
doc, it seems to work with typical
applications.
Paint the tiles yourself in the
paint() of one custom item. This
gives you total control on how to
place and draw the tiles while the
item truly acts as one item since it
is, well, one item. This is what I
do.