PDA

View Full Version : Architecture Question on QGraphicsView, QGraphicsScene, QGraphicsItems



Caolan O'Domhnaill
28th March 2016, 18:56
Hello,

I am still relatively new to QT and when I initially started writing my application I based it off of examples that did not use the QT Creator, or Model/View architecture etc. ... I have since gone back and refactored my code to utilise these tools because of so many problems I was having and now it is working as I wanted it to. All except for the graphics part of it so I wanted to see if the problem is the way I have built it.

I have created as the primary graphics window a class derived from the QGraphicsView. I do all of my coordinate transforms, viewport updates, creation of the QGraphicsScene, handle drawing of the background, handle keypress events, zoom in zoom out, create the QGraphicsItems, etc. ...

The QGraphicsItems are added to the scene and do their own painting.

As I work through examples I have seen where the QGraphicsScene has a much more active role than I am giving it. It has been subclassed, and handles painting among other things.

What I am wondering is whether my set up is good, or is there a "best practices" way to do it that is hould be using as well. Like I said I have seen it done many different ways just like the UI was however the way I changed it to seems to be the "Best Practices" way of doing things.

Cheers!
-caolan.

d_stranz
28th March 2016, 20:40
I find it more common usually to derive from QGraphicsView than the derive from QGraphicsScene. However, deriving from QGraphicsScene allows you to implement member functions that give convenient access (by name, so to speak) to specific scene elements. I did this for a 2-D data plotting widget (similar to QwtPlot) so that I could retrieve titles, axes, etc. by name: getAxis( yLeft ) for example. The scene managed the layout of these scene elements depending on their visibility and appearance attributes.

More recently I reimplemented this widget as a pure QWidget containing QLayouts to manage the size and positioning of titles, axes, etc. The central plotting canvas is now derived from QGraphicsView but uses a plain vanilla QGraphicsScene to hold the things to be plotted - the x-y data items mainly. The plot decorations (axes, titles, etc.) are self-contained QWidgets and do their own drawing independently of the scene.

It's a tradeoff - if you have entities that you would like to organize, layout, and access by name, then deriving from QGraphicsScene allows you to let your custom scene do the organizing and provide access methods instead of using some external data structure for the same thing. If the organizing is better done through an external data structure, then using a plain QGraphicsScene will generally suffice.

Caolan O'Domhnaill
28th March 2016, 21:49
Okay thank you that makes sense. What you did with a 2D data plotting sounds almost like what i am trying to do. The objects need direct interaction with each other and with the user, so the view is just a passive displayer of the interactions. While waiting I went ahead and created an alternative structure by making a generic QGraphicsView and a derived QGraphicsScene and started to test with it and it seems to be what I want for this example. Basically everything being drawn: polygons, vector arrows, text, etc. ... need to function both by themselves as entities, and be able to form groups and have interaction with other objects.

Thank you for the insight!


I find it more common usually to derive from QGraphicsView than the derive from QGraphicsScene. However, deriving from QGraphicsScene allows you to implement member functions that give convenient access (by name, so to speak) to specific scene elements. I did this for a 2-D data plotting widget (similar to QwtPlot) so that I could retrieve titles, axes, etc. by name: getAxis( yLeft ) for example. The scene managed the layout of these scene elements depending on their visibility and appearance attributes.

More recently I reimplemented this widget as a pure QWidget containing QLayouts to manage the size and positioning of titles, axes, etc. The central plotting canvas is now derived from QGraphicsView but uses a plain vanilla QGraphicsScene to hold the things to be plotted - the x-y data items mainly. The plot decorations (axes, titles, etc.) are self-contained QWidgets and do their own drawing independently of the scene.

It's a tradeoff - if you have entities that you would like to organize, layout, and access by name, then deriving from QGraphicsScene allows you to let your custom scene do the organizing and provide access methods instead of using some external data structure for the same thing. If the organizing is better done through an external data structure, then using a plain QGraphicsScene will generally suffice.

d_stranz
29th March 2016, 00:22
Basically everything being drawn: polygons, vector arrows, text, etc. ... need to function both by themselves as entities, and be able to form groups and have interaction with other objects.

This sounds like it might be one of those ugly cases where you need an external (topological) graph-like data structure to maintain the internal representation of the entities and their relationships, and a "parallel universe" that represents the visualization of those entities on screen and which manages the user interaction. The pragmatist in me says, "No, you could implement the entire thing in QGraphicsScene and put a serialization scheme on top of that to allow you to save and restore it" whereas the purist says "Yes, but what if you abandon Qt or Qt replaces the Graphics / View architecture with something much more flashy? Then you have to rewrite the whole thing. Better to keep the two parts of the functionality separate so your data representation can live independently of how you display it".

I guess in my case the purist wins out most often, because I usually have a clear line between my data model and algorithms that manipulate it (written in as close to pure C++ with STL as I can manage - we've recently given the boot to boost, since most of what we use is in C++11 STL now) and the visualization and editing of the model (in Qt).

Caolan O'Domhnaill
29th March 2016, 01:12
hmmm. That is something to think about for sure. I am doing this with a pseudo-spreadsheet portion of the app which is already done. I have a parallel data grid that mirrors the UI grid.

Although I understand the purist route, I am going to take the easy way out for now in order to keep the deadline :)

I'll have to take a look at the C++11 STL and see if it provides what we need as well. In our math-equation parser we use the following:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>


This sounds like it might be one of those ugly cases where you need an external (topological) graph-like data structure to maintain the internal representation of the entities and their relationships, and a "parallel universe" that represents the visualization of those entities on screen and which manages the user interaction. The pragmatist in me says, "No, you could implement the entire thing in QGraphicsScene and put a serialization scheme on top of that to allow you to save and restore it" whereas the purist says "Yes, but what if you abandon Qt or Qt replaces the Graphics / View architecture with something much more flashy? Then you have to rewrite the whole thing. Better to keep the two parts of the functionality separate so your data representation can live independently of how you display it".

I guess in my case the purist wins out most often, because I usually have a clear line between my data model and algorithms that manipulate it (written in as close to pure C++ with STL as I can manage - we've recently given the boot to boost, since most of what we use is in C++11 STL now) and the visualization and editing of the model (in Qt).

d_stranz
29th March 2016, 04:01
I was able to replace lexical_cast, but without going back deep into the code repository I can't really tell you how. std::to_string() and std::to_wstring() work well for going the other way. lexical_cast gets many hits for being very slow because of all of the checking it does.

You'll have trouble with multi_index, as I did with boost::bimap and boost::variant. There aren't any C++11 STL counterparts.

There is nothing wrong with boost per se. Since we used so little of it in our data processing toolkit libraries, and the parts we used had been added to the C++11 standard, we decided to eliminate that dependency. We also have to make our libraries portable among multiple android, linux, Mac OS/X, and Windows platforms, so the less external code we have to manage the better. We also use our libraries in writing both Qt and MFC apps on Windows, so we can't use Qt's containers and algorithms there either.

The templates I mention above are in an app built on top of our libraries. I got rid of bimap by implementing my own simple paired std::map<> template with some methods which keep the maps synchronized. boost::variant will be a lot harder.


math-equation parser

On my to do list is a similar spreadsheet and parser, with the addition that it also needs to understand how to parse chemical formulas and do chemical arithmetic , so that you can do things like put the molecular weight of the formula from cell A! into the cell at B1. Or add the formula from A1 to the formula from A2 and place the result in B1. Or count the number of carbon atoms in A1 and put that in B1.

Caolan O'Domhnaill
29th March 2016, 18:05
Thank you for the pointers. I'll look to it. The code I am working on is only Windows/Linux cross compatible so I do not have the issues that you're encountering with android, Mac OS, etc. ...

Our app is similar idea but with physics. It's a sandbox, tutorial application for 1st year Engineering Physics courses. Our "spreadsheet" contains particles and the vectors on each row, that act on them. Each column is a time slice defineable by the user. Each cell in the spreadshee contains the calculation defined by the equation assigned to the vector. Each vector can be given an equation or a constant and it will then recursively search the rest of the spreadsheet looking for variables, which in turn recursively tries to resolve those as well. if it cannot resolve an equation (e.g.: a variable is undefined) it'll highlight it as red. It's pretty simple concept but implementation was complex, yet straight-forward. The particles and vectors added show up in the main screen (hence the question about graphics) and interact either by themselves or with each other.




I was able to replace lexical_cast, but without going back deep into the code repository I can't really tell you how. std::to_string() and std::to_wstring() work well for going the other way. lexical_cast gets many hits for being very slow because of all of the checking it does.

You'll have trouble with multi_index, as I did with boost::bimap and boost::variant. There aren't any C++11 STL counterparts.

There is nothing wrong with boost per se. Since we used so little of it in our data processing toolkit libraries, and the parts we used had been added to the C++11 standard, we decided to eliminate that dependency. We also have to make our libraries portable among multiple android, linux, Mac OS/X, and Windows platforms, so the less external code we have to manage the better. We also use our libraries in writing both Qt and MFC apps on Windows, so we can't use Qt's containers and algorithms there either.

The templates I mention above are in an app built on top of our libraries. I got rid of bimap by implementing my own simple paired std::map<> template with some methods which keep the maps synchronized. boost::variant will be a lot harder.



On my to do list is a similar spreadsheet and parser, with the addition that it also needs to understand how to parse chemical formulas and do chemical arithmetic , so that you can do things like put the molecular weight of the formula from cell A! into the cell at B1. Or add the formula from A1 to the formula from A2 and place the result in B1. Or count the number of carbon atoms in A1 and put that in B1.

d_stranz
31st March 2016, 21:21
Cool. Sounds like a fun project.