How should I use a QGraphicsScene with layouts and widgets - c++

I'm creating some graphic data displaying widget in Qt4 and I was tempted to use the QGraphicsScene for it, create QGraphicsItems for the data items etc.
However, I wanted to add some layer of controls (eg. scrollbars, zoom+other buttons - I want to make it in a similar style as eg. Google Maps, that is, the data would be displayed all over the widget, and the buttons would be shown atop of them) to the widget. So I thought it might be feasible to add them to the scene (perhaps as a child of a QGraphicsGroupItem that would be shown over the data). But I want them to move & resize when I resize the whole widget, so I should use a QGraphicsLayout for managing them. But at this point, I discovered things are pretty complicated.
The problem is that when using QGraphicsLayout, the following constraints hold:
Only a QGraphicsWidget can be managed by a layout
QGraphicsLayout can only be used to manage children of a QGraphicsWidget
Which means that I would have to create my controls as QGraphicsWidgets, add a top level QGraphicsWidget to the data widget, and manage the size of this top level widget myself.
So I want to ask:
Wouldn't a classic approach (ie. use plain old widgets for all controls, and use QGraphicsScene only for displaying the data) be more reasonable?
Is there any advantage in using QGraphicsScene in this case (performance or simplicity...)?
How should I use QGraphicsScene to exploit its strengths?

Since Qt 4.4 you can embed classic widgets in a QGraphicsScene by using QGraphicsProxyWidget :
QWidget *widget = new QWidget;
QGraphicsScene scene;
QGraphicsProxyWidget *proxy = scene.addWidget(widget);

If you think that QGraphicsScene (or whatever other widget you have) is appropriate for most of your display, use that. What we have done in the past for somewhat similar things is to make a custom widget that inherits (one way or another) from QWidget, and put the control widgets in a layout on top of that widget. This means that the whole widget is drawing whatever it is you want drawn, and the control widgets are on top of that, resizing as the whole widget is resized.
Alternatively, a couple of times we've had layouts that were just a bit too complicated for the layout widgets to easily handle. Rather than create a custom layout, we just positioned them with no layout, and moved them in code on the resize event. It works just as well.

Related

Use the same widget in two different layouts in Qt

I would like to use the same widget in two different layouts in Qt. Here is my code:
QWidget *myWidget = new QWidget;
QFormLayout *layout1 = new QFormLayout;
layout1->addWidget(myWidget);
QFormLayout *layout2 = new QFormLayout;
layout2->addWidget(myWidget);
The widget is as it should in layout2 but is not visible in layout1.
A workaround would be to create two different myWidget widgets, but I would like to know if there is a better way of doing.
Why does this happen and what is the correct way of doing this?
addWidget transfers the ownership from layout1 to layout2.
Object trees are the way Qt uses to organize objects. As an example, an item that has a parent is displayed in its parent's coordinate system and is graphically clipped by its parent's boundaries.
You can try to work around the limitation, but it is not how you should use Qt and I won't suggest it.
If you need two widgets, create two widgets. That's how Qt is designed and how it should be used.
See here for further details about the Qt's objects model.
You cannot have the same object in multiple places. There is only once instance of it and it lives in only one single location. You can only have multiple references. Granted, a layout doesn't take a widget instance, but a reference (pointer) to it, but Qt's design is such that adding a widget to a layout will transfer ownership to the layout's underlying widget. And it makes sense, the two layouts may call for a different widget geometry, and how does a single widget have two geometries in the same time? Even if possible theoretically, it is not possible without abstracting the geometry away from the widget, and in Qt's case the geometry is part of the widget, so it is not possible. And that's just one of the many aspects which make such reuse/sharing challenging and not really viable.
Depending on what you want to achieve you could:
reuse GUI elements - in that case roll out YourOwnWidget : public QWidget, then you can instantiate in as many times as you want
share the same data across multiple GUI elements, in addition to the previous step, what you really want to do is put the data in a separate, non-visible object, then you can create and bind as many types and instances of GUI elements to it as you want.
You can use QGraphicsView. Define one instance of QGraphicsView and add it to layout1:
QGraphicsView *gv1 = new QGraphicsView();
layout1->addWidget(gv1);
Define another one and add it to layout2:
QGraphicsView *gv2 = new QGraphicsView();
layout2->addWidget(gv2);
QGraphicsScene *qc = new QGraphicsScene();
qc->addWidget(myWidget);
Now set scene to your QGraphicsView objects
gv1->setScene(qc);
gv2->setScene(qc);
After that you have two views containing the same widget.

Scrollable background with resizable Qt widgets on it

I would like to create a Qt window that
can be scrolled/panned by dragging its background (scrollbars should not be shown)
contains multiple sub-widgets at defined locations (which scroll with the background)
the sub-widgets can be resized by pulling their border
What I managed so far is to create a QGraphicsView with setDragMode(QGraphicsView::ScrollHandDrag) http://doc.qt.io/qt-5/qgraphicsview.html#dragMode-prop. I then placed the sub-widgets on a QGraphicsScene, however, this didn't allow the sub-widgets to be resized by pulling their border.
I also tried to inherit my custom sub-widget class from QDialog, which allows setSizeGripEnabled(true). However, this doesn't resize their content, and QDialog is probably not meant to be part of a QGraphicsView.
Any suggestions? It would also be ok if the sub-widgets behave like sub-windows that can also be dragged at their title bar, as long as they cannot be closed and they move when the background is dragged.
You can look aside QMdiArea class (Qt documentation: QMdiArea). By problem description it is what you need.
Of course, you can use Graphics View Framework, but, i think, it will be more difficult. If you choose such approach, very useful will be class QGraphicsWidget (Qt documentation: QGraphicsWidget).

How to create QSplitter ui class via qt designer?

I am new to Qt and I need to implement a monitoring interface with the following considerations:
I have a main window, on which I should put multiple screens, qsplitter appears to be the best solution.
The interface provides user with the option of changing the number of cameras, so QSplitter should be created/re-created during run time.
The problem is that I have too many cameras to pre-define widgets for them, so I need to create QSplitter UI instances dynamically.
The problem is that I can't find QSplitter classes when using Qt Designer and creating QSplitter class programatically is not working as MainWindow has been created through Qt Designer (.ui).
I would like to hear any suggestions regarding this issue, if there are better approaches, please let me know.
In Qt Designer, the QSplitter is not a widget, but a Layout.
Select the widgets you want to include in the two splitter areas, then select Layout from the context menu (right mouse button) - you will find two entries Layout Horizontally in splitter and Layout Vertically in splitter to group the widgets in a vertical or in a horizontal splitter.
QSplitter isn't a strict UI element, it's essentially a parent element that controls child elements. If you want to do it through Designer you'd probably run into headaches, but the basic gist is you select a number of widgets to be controlled by it, and click the Layout Horizontally/Vertically in splitter button which is in the layout buttons group.
What you might be best doing is creating your child elements programmatically, creating your splitter programmatically, adding the child widgets with someSplitter->addWidget(...). In the Qt docs there's some sample code for this:
QSplitter *splitter = new QSplitter(parent);
QListView *listview = new QListView;
QTreeView *treeview = new QTreeView;
QTextEdit *textedit = new QTextEdit;
splitter->addWidget(listview);
splitter->addWidget(treeview);
splitter->addWidget(textedit);
http://doc.qt.io/qt-5/qsplitter.html#details
And if you really want to do it in Designer there's a guide here: http://www.bogotobogo.com/Qt/Qt5_Splitter.php
Although QSplitter is a widget, you can't create one directly in Qt Designer. It is only available for laying out pre-existing widgets - which does not fit your use-case, since you need to create child widgets dynamically.
However, you can work around this limitation by using widget promotion. This is a simple mechanism that allows you to add substitute classes to represent widgets that are not directly available in Qt Designer. The idea is that you add a widget that is most similar to the one you actually want (e.g. a QFrame is most similar to QSplitter) and then promote that to the substitute class which you have defined in your own header file. When uic finally generates the code, it will use your substitute class instead of the class of the widget added in Qt Designer (which just acts as a placeholder).
Note that when you create your substitute class in the Promoted Widgets dialog, the base class should be a QFrame, not a QSplitter. This is because you are extending a QFrame (i.e. your placeholder widget), rather than a QSplitter. Of course, you can define your substitute class to be anything you like.
Image is easier to understand.

How to hide/show a QLabel and QTextEdit at the same time in my Qt application?

I am working on a hide/show feature for my console in my Qt GUI application. The console consists of 2 widgets; QLabel and QTextEdit. Do I need to add the QLabel and QTextEdit to a QWidget in order to show/hide them, or is there a better way?
So basically I am looking for a container such as 'JPanel' in Java...
Do I need to add the QLabel and QTextEdit to a QWidget in order to show/hide them, or is there a better way?
Multiple methods are possible here. You can, as you suggest, create a parent QWidget and add the QLabel and QTextEdit to a QWidget. Calling show and hide on the parent widget will affect its children.
Another method would be to have a slot function, which when an action is called, the slot calls show / hide on the 2 widgets.
Neither is right or wrong and depends upon the overall design of your application.
Ok, I think you need to use a layout (horizontal / vertical):
The simplest way to arrange objects on a form is to place them in a
horizontal or vertical layout. Horizontal layouts ensure that the
widgets within are aligned horizontally; vertical layouts ensure that
they are aligned vertically.
Horizontal and vertical layouts can be combined and nested to any
depth. However, if you need more control over the placement of
objects, consider using the grid layout.

How to set the size/location of a central widget in QT

I have a central widget with several doc widgets surrounding it. When i hide it, the docs resizes to fill the space however, when i show the central widget back, it is show with its default size. I want to be able to resize and position it as it was before being hidden.
Before hiding do something like this:
QSize storedSize = mainwindow->getCentralWidget->size();
(assuming mainwindow is a * QMainWindow containing the central widget you are talking about)
When you want to resize it back to the old size:
mainwindow->getCentralWidget->resize(storedSize);