QGraphicsScene, error by accessing pointer in custom class - c++

I have a pretty complex problem... In Qt I have a custom class (named FotoGebouw) that inherits from QGraphicsItem, it also contains a pointer to another custom class (named Gebouw). If I want to acces the selected items from the scene, in other words the "FotoGebouw"items, I first have to cast them to QGraphicsItems. But this way, I seem to lose the pointer (called linkGebouw) that they were pointing to.
Does anyone know a way to get the FotoGebouw items that are selected from the scene, while I can still get the
QList<QGraphicsItem *>bordSceneGebouwen=bordscene->selectedItems();
FotoGebouw *teVerplaatsenFoto=dynamic_cast<FotoGebouw *>(bordSceneGebouwen[0]);
Gebouw *teVerplaatsen=teVerplaatsenFoto->linkGebouw;

Related

QGraphicsItem is not visible after adding to scene

I'm working on a diagram visualisation tool and I ran into an issue where my QGraphicsScene does not display a shared_ptr<DiagramItem> when a raw pointer obtained via .get() is passed to scene->addItem().
Subsequent check via scene->items() shows that my DiagramItem is not a part of the scene. My guess is that it got freed as the refcounter on the shared_ptr will be zero after leaving the scope of the testing function...
But that was the testing case. In my actual code I'm using a shared_ptr that I got from elsewhere and is definitely present in memory with a non-zero refcounter. I get the raw pointer of that and pass it to scene->addItem(). It is also not displayed, but this time it is present in scene->items(). So why is it not being drawn?
If I switch from using shared_ptr<DiagramItem> to DiagramItem* then the issue disappears and everything is displayed properly. But due to limitations from the rest of the project, I cannot easily abandon smart pointers here, nor do I want to.
Did I run into some kind of memory limitation or am I doing something wrong?
I already tried calling show() and update() on the item and increasing the scene size in case the item doesn't fit (it does). I also tried breakpointing the paint() method, but that one doesn't get called at all.
I found a possibly related question here where similar behaviour occurs due to the object going out of scope and being deallocated, but that doesn't seem to be the case with my actual DiagramItem.
class DiagramItem : public QGraphicsItem
{
...
}
//Create scene
auto scene = new QGraphicsScene(nullptr);
//Item is created OR obtained from elsewhere
auto item1 = std::make_shared<DiagramItem>(nullptr, QString("aaa"), true);
auto item2 = GetDiagramItem(...);
//Raw pointers get passed to addItem
scene->addItem(item1.get());
scene->addItem(item2.get());
//Item1 is not present at all (directly created DiagramItem)
//Item2 is present but invisible (DiagramItem passed from elsewhere)
//myItem gets Item2
auto myItem = scene->items()[0];
...

QT add widgets to UI anywhere

The application that I'm building is supposed to create, destroy, and manipluate widgets that I've created
The problem is I'm not making a simple program with nice buttons where everything is symmetrical and needs to be evenly spaced and handled via a layout that will automatically move everything around and space it.
And yet, the only way I know of is to manually instance a layout and add the widgets to it, but then I can't set the coordinates of them
How can I simply instance my widget, and add it to the project generated frame?
This is how I'm instantiating my class, in which case I then set my own parameters:
Tile *tile = new Tile;
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
To reiterate, I want to add my own widgets to a frame outside of the editor (only by code), and be able to manually move them around the frame by code.
I don't see an ".addWidget() as a method accessible from the QFrame, and yet they can be children within the designer, why can't I do this by code? Whenever I try to do it manually and add them to any layout, any attempt I make to manually set the widgets location doesn't do anything. I haven't overridden the setGeometry
I fixed my problem
After 2 hours of continual searching I finally came across my answer
I never thought that you could set the parent of a widget by code, as I thought you strictly had to add it in as a child of something else, not the reverse and declare that it should have a parent
So, by simply adding the line:
tile->setParent(ui->frame);
completely fixed my problem.
I will change this post back and submit the answer tomorrow when I'm allowed to by this site.
Thank you to those who actually came though. I'm just glad I managed to fix it before that.
All you need is to pass the parent to the widget's constructor:
Tile *tile = new Tile(ui->frame); // <-- here
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
Since Tile is your own class, you should definitely have a Qt-style, parent-taking explicit constructor for it:
class Tile : public QWidget {
...
public:
explicit Tile(QWidget * parent = 0) : QWidget(parent) { ... }
};
Another approach is to write your own layout that would know about the relationships that are to be held between your objects. After you do it once, writing custom layouts isn't that hard.

C++/Qt - multiple inheritance with QGraphicsItem doesn't work as expected

I recently met a strange problem of my little program and it would be great if you help me to get the reason of this behavior.
My task is quiet simple - I want to use Qt Graphics Framework to show some objects and I want Box2D to calculate bodies position. So my class hierarchy looks like the following:
I have 1 base abstract class B2DObject. It contains some Box2D staff + some common parameters for its successors (names, some flags, etc.). It also has couple of pure virtual functions that will be reimplemented in successor classes.
Then I implement some classes that represent basic shapes: circles, rectangles, polygons, etc. I am doing it in the following way:
class ExtendedPolygon : public B2DObject, public QGraphicsPolygonItem { ... };
class ExtendedCircle : public B2DObject, public QGraphicsEllipseItem { ... };
etc.
(for those who are not familiar with Qt, QGraphics***Item is inherited from QGraphicsItem).
Also I inherited QGraphicsScene and reimplemented its mousePressEvent. In this function I request an object placed at some point on the screen using QGraphicsScene::itemAt function (which returns QGraphicsItem*), convert it to B2DObject* and try to get some internal field from this object:
void TestScene::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
QGraphicsItem* item = itemAt (event->scenePos ());
if (item)
{
B2DObject* obj = reinterpret_cast < B2DObject* > (item);
QString objName = obj->Name(); // just for example,
// getting other internal fields has
// the same effect (described below)
// use retrieved field somehow (e.g. print in the screen)
}
// give the event to the ancestor
}
Unfortunately, dynamic_cast will not work here because these classes are completely unrelated.
Then I create necessary objects and add it to my scene:
ExtendedPolygon* polygon = new ExtendedPolygon (parameters);
polygon->setName (QString ("Object 1"));
...
TestScene scene;
scene.addItem (polygon);
(for those who are not familiar with Qt, here is the prototype of the last function:
void QGraphicsScene::addItem(QGraphicsItem *item);
I guess it just stores all items in internal index storage and calls QGraphicsItem::paint (...) when item needs to be repainted. I suppose QGraphicsScene doesn't make any significant changes to this item).
So my problems start when I run the program and click on an item on the screen. TestScene::mousePressEvent is called (see a piece of code above).
Mouse click position is retrieved, item is found. Casting works fine: in the debugger window (I'm using Qt Creator) I see that obj points to ExtendedPolygon (address is the same as when I add the item to the scene and in the debugger window I can see all the fields). But when I get some field, I receive garbage in any case (and it does not matter, what I'm trying to get - a QString or a pointer to some other structure).
So first of all, I would like to get any advice about my multiple inheritance. In 95% of cases I try to avoid it, but here it is very effective in the programming point of view. So I would appreciate it if you provide me with your point of view about the architecture of the classes hierarchy - does it even suppose to work as I expect it?
If on this level everything is quite fine, then it would be great if someone gets any idea why doesn't it work.
I have some ideas about workaround, but I really would like to solve this problem (just in order not to repeat the same error anymore).
Looks like I've found the root cause of my problem. It was just lack of knowledge regarding how multiple inheritance really works on data layer.
Let's assume that we have 2 basic classes, A and B. Each of them provides some internal data fields and some interfaces.
Then we create a derived class AABB, inheriting both A and B:
class AABB : public A, public B {...}
AABB could add some additional data fields and reimplement some of the interfaces, but it is not necessary.
Let's create and object of class AABB:
AABB* obj = new AABB ();
For example, obj points at address 0x8416e0. At this address starts data from ancestor class A. Data from ancestor class B starts with some offset (it should bw equal to sizeof (A)), for example, at 0x841700.
If we have some function f (B* b), and if we pass a pointer at AABB object to that function (like this: f (obj), obj is created above), actually not obj start address is passed, but rather a pointer at a start of B data section of AABB object.
Thus this misunderstanding of multiple inheritance inner works has led me to the problem I've got.
I guess Qobjects and multiple inheritance has been already treated. As an example: QObject Multiple Inheritance

Passing QSqlQueryModel through control class

How would I go about passing QSqlQueryModel from a class that connects and queries the database through the control class or QMainWindow in my attempt and back to the widget needing the information?
I thought I could pass the reference location to the QSqlQueryModel object, but this is not working or I am doing something wrong.
I haven't found any examples showing what I am doing on the Qt Developer page.
Looks like these are just compiler errors, nothing specifically to do with Qt.
In short you are getting your pointers and references mixed up.
Error #1:
cardList = new List(sqlModel->getListModel());
You are passing a reference when the List takes a pointer. Fix your return type from getListModel or fix the above line.
Next, you are not specifying the second argument, i.e. the parent QWidget. Either specify your MainWindow as the parent, pass 0, or fix your constructor's signature to provide a default (generally 0).
Error #2:
List::List(QSqlQueryModel *model, QWidget *parent) : ListUI(parent){
setListItems(&model);
}
You receive the model as a pointer and then attempted to take the address of the pointer. I.e. You're making a double pointer. Change the line to
setListItems(model);
Hope that helps.

Remove QLayoutItem from a Layout, but still use it later on?

Here is the environment first:
I have a self defined "Property Editor" which is a QGroupBox (derives from QWidget)
Currently I have a class let's call it "Holder", which has a reference to two of the Property Editors.
Now I have multiple "Holder" classes and one vertical QVBoxLayout (called Sidebar).
In this layout I want both of the Property Editors of the currently selected Holder class to be displayed.
And there is the issue:
When the user selects another holder class, I want the Property Editors of the previously selected Holder class to disappear, and add the Property Editors of the new selected Holder class.
Selecting another Holder class works once. But when I select the first Holder class again, The editors don't seem to change. Why? Does "takeAt(..)" destroy the reference in the holder class? How can I get the desired behavior?
Here is the code, thanks in advance:
void App::setSelection(Holder * holder){
if(m_currentlySelected == holder) return;
m_viewer->sideBarRemoveAt(0);
m_viewer->sideBarInsertAt(0, holder->firstPropEditor);
m_viewer->sideBarRemoveAt(1);
m_viewer->sideBarInsertAt(1, holder->secondPropEditor);
m_currentlySelected = holder;
}
void QtViewer::sideBarRemoveAt(int i){
m_sideBar->m_layout->takeAt(i);
}
void QtViewer::sideBarInsertAt(int i, QWidget * widget){
m_sideBar->m_layout->insertWidget(i, widget);
}
QLayout::takeAt() doesn't remove the widget of the QLayoutItem from its parent widget. The only reason it may seem to work the first time is probably because the other widgets were above (z-index wise) the first ones.
Rather than playing with the layout, you could
just hide/show your 2 PropertyEditor whenever the holder changes, hidden items don't generate holes in the layout, the next visible item is displayed as if the hidden items were not part of the layout, or
use a QStackedWidget to stack all the PropertyEditor at the same place and select which one is displayed (with QStackedWidget::setCurrentIndex()).
Does "takeAt(..)" destroy the reference in the holder class?
No, this method removes the QLayoutItem from the layout. See reference page for takeAt. This class doesn't release the layout item (it is your responsibility to do).
But when I select the first Holder class again, The editors don't seem
to change. Why?
I am not quite clear what you are trying to achieve (not enough code in your example), but if you are trying to change the layout using QLayoutItem's, then it is the simplest to create new layout and add items you want to display to it. Or simply, remove all items from the layout, and add the items that should be visible.