Layers on QGraphicsView? - c++

Hi I'm making an application that pulls data from a WFS and then displays those layers of data on a QGraphicsView on a widget. At the moment all layers are rendered and added to the same view meaning if I want to turn a layer of it means re-rendering all of it except that layer.
At the moment im adding a QGraphicsScene with Ellipse Items and Polygon Items added to it, to the graphics scene. I'm wondering if its possible to add multiple scenes to a graphics view or layers to a scene or something that would allow me to just hide/show certain points/polygons from a check box or something that simply hides a layer?
I know this is kind of vague but I'd appreciate any help.
Thanks.

You only need one QGraphicsScene, but the key here is that all QGraphicsItems and QGraphicsObjects can be parented.
If you create a single QGraphicsItem or QGraphicsObject as a parent object, it doesn't need to draw anything, but can be used as the root for a layer's items.
Therefore, subclass from QGraphicsItem to create a QGraphicsItemLayer class that doesn't render anything and add all the ellipses, polygons etc that are required in the same layer as children of that QGraphicsItemLayer.
When you want to hide a layer, just hide the parent QGraphicsItemLayer object and all its children will be hidden too.
-------- Edited --------------
Inherit from QGraphicsItem: -
class QGraphicsItemLayer : public QGraphicsItem
{
public:
virtual QRectF boundingRect()
{
return QRectF(0,0,0,0);
}
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
{
}
};
Create a layer item:
QGraphicsItemLayer* pLayer = new QGraphicsItemLayer;
Add the objects you want to the layer, note that pLayer is passed as the parent
QGraphicsEllipseItem = new QGraphicsEllipseItem(pLayer);
Assuming you've created the QGraphicsScene with a pointer to it called pScene: -
pScene->addItem(pLayer);
Then when you want to hide the layer
pLayer->hide();
Or display the layer: -
pLayer->show();

Another way to go is QGraphicsItemGroup
Something like:
// Group all selected items together
QGraphicsItemGroup *group = scene->createItemGroup(scene->selecteditems());
...
// Destroy the group, and delete the group item
scene->destroyItemGroup(group);
So you can treat group as a layer and since group is also QGraphicsItem have all features like show()/hide() etc.
UPDATE: Changing Z-val for a group will allow you to implement things like 'move layer to top/bottom'

I think you could try to partition your objects according to z value: see setZValue.
Then introduce a mapping between layer id and indexing. A simple QStringList could do.
Of course, there are many details and variations that a practical solution will need to account for.

Related

Qt Application with QTabWidget, QGraphicsScene and QPushButton

I want to create a Qt application with a QTabWidget, where each tab contains a QGraphicsView and a QGraphicsScene to draw a polygon on a Cartesian plane. example: first tab for a triangle, second for square, etc...
So, I create my qt application and then:
- inside my class derived from QDialog, I create a QVBoxLayout, I've added my widget to this, and I've inserted it into my QTabWidget, created with QDesigner: I repeat this operation for each tab of QTabWidget.
I want to know if it's a bad practice to add a QGraphicsScene into a tab in this way or to redefine all the classes from qwidget, view, and scene.
out of the QTabwidget, i have some QPushButtons that do some operations on the QGraphicsScene's polygons: when i click on a polygon, i want to do some operations through the QpushButton, like sum the angles (only on the polygon that I've clicked).
How do I communicate to the QPushButton (out of the QTabWidget) that I've clicked a polygon and which one I've clicked(inside QTabWidget)?
the private data field of my_scene contains a QList of triangles (derived from QGraphicsPolygonItem)
https://i.stack.imgur.com/Pu329.jpg
I want to know if it's a bad practice
Generally speaking no, it isn't.
If you are ok with the functionality of some class you should use this class. If you need to extend it in any way you should subclass from this class and add everything you need.
It's true not only for Qt classes but for any other classes written by you or somebody else. It's one of the basic principles of inheritance.

Qt QGraphicsScene dynamic GUI elements like containers

I'm using QGraphicsScene and I want to create gui elements.
How do I/should I create a list container i.e. inventory in a game context.
See HERE
For an example of what I mean. There is an inventory widget at the bottom that can be dynamically populated.
Are there any container classes like the standard list widgets but for QGraphicsScene instead?
Right now the only way I can see is to draw a 'rectangle' and manually draw and manage 'squares' on top of it... which wouldn't be a container.
Every QGraphicItem is effectively a container because every item can be optionally parented to a QGraphicsItem. When you do that, the parent affects all its children...if the parent is moved, the children move with it. If a transform, visibility, or opacity are set on the parent, those changes apply to the children as well.
Basically, your understanding is correct; you would create a parent QGraphicsItem that draws a container outline, and then you would have children that each draw their icons or whatever is appropriate.
Alternately, you can use widgets in a QGraphicsScene, so all of the widget-based containers are available. If that's the route you want to go, then look at the QGraphicsWidget class.
This is a complex subject, but hopefully this will get you started.

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.

Select multiple QGraphicsItems without holding down key

I'm using a QGraphincsView that holds several elements which inherit from QGraphicsItem. The whole thing works fine, I can select them as desired. And when I hold down the Ctrl-key I can select several of them.
Now I want to implement an optional multi-selection without the need to hold down Ctrl-key. I already tried to set the related modifier in mouse-press-event by calling
evt->setModifiers(Qt::ControlModifier);
before the event is handed over to it's base-class QGraphicsItem but this does not work.
So my question: what has to be done to get multiple selection functionality by default and without holding down a key?
Thanks!
This is controlled by the QGraphicsScene. You stated: -
I'm using a QGraphincsView that holds several elements which inherit from QGraphicsItem
This is not actually the case. A QGraphicsView is a window into an area of the scene; it is a QGraphicsScene which holds items derived from QGraphicsItem.
You can see in the documentation that the QGraphicsScene has functions such as selectedItems(), selectionArea() and setSelectionArea(). While a QGraphicsItem can be selected with QGraphicsItem::setSelected, the control of what happens when you click an item is governed by the QGraphicsScene, with the event having been passed from the QGraphicsView.
If you inherit from QGraphicsScene, you can override the mouse methods; mousePressEvent, mouseMoveEvent, mouseReleaseEvent. This will allow you to monitor when the user selects consecutive items by clicking on them and react by calling their QGraphicsItem::setSelected function.
Alternatively, depending upon your design, you can allow the user to draw an area on the scene and call QGraphicsScene::setSelectionArea, which will set all the items surrounded by the given QPainterPath.

qt create simple rectangular board

I want to create a rectangular board using QT. This board will be updated when a step is performed. For example on step x, the text in cell 5,6 updates from "not explored" to "explored".
I have looked through the QT documentation and found the class QGraphicsView. How can i use QGraphicsItem to simulate a cell where text can be written?
I am also open for alternatives.
Technically this could also be done by customizing a QTableView/Widget, but QGraphicsScene is more robust for custom graphics and performance.
From a high level view, you can either create a composite object representing a "Cell" item, or you can subclass a QGraphicsItem and do all the custom painting yourself.
When creating a composite object, that would just be a QGraphicsItem "Cell" subclass which contains maybe a QGraphicsRectItem and a QGraphicsTextItem as members, set to the cell instance as a parent. This will keep the child items translating with the parent cell item.
When creating a completely custom QGraphicsItem, you would define all the painting inside of the paint() method, which would draw a rectangle, and text taken from a value set on the instance.