PDA

View Full Version : Error code after inheriting QGraphicsView



K.Sejdak
12th March 2011, 21:10
Hi everyone!

I have some problem with QGraphicsView. I want to create a program that will allow me to draw rectangles on loaded image. I have created my own GraphicsView class that inherits from QGraphicsView and I have overloaded QGraphicsView::mouseReleaseEvent to get the mouse position. I also added 'Widget' from Qt Creator to my form and replaced it with my class. It works fine until I close the window. After that I get an error code (-1073741819). Debugger shows segmentation fault in function QScopedPointer (no idea why). Probably I have to call some function, but I don't know which one and where. I will show you my code (only .cpp). Tell me if you need the header files.

I hope you understand what I mean:) Could you help me?

MainWindow class:

#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QWidget(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->widget->setScene(&scene);

connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(close()));
connect(ui->loadButton, SIGNAL(clicked()), this, SLOT(loadImage()));
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::loadImage()
{
QString fileName = QFileDialog::getOpenFileName();
image.load(fileName);
scene.addPixmap(image);
ui->widget->loadImage(image);
ui->widget->show();
}

GraphicsView class:

#include "GraphicsView.h"

GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent)
{
firstRect = true;
}

GraphicsView::~GraphicsView()
{
scene()->clear();
}

void GraphicsView::loadImage(QPixmap item)
{
image = item;
}

void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
if(point1.isNull())
{
point1.setX(event->x());
point1.setY(event->y());
}
else
{
point2.setX(event->x());
point2.setY(event->y());
int width = point2.x() - point1.x();
int height = point2.y() - point1.y();

if(firstRect)
{
rect.setRect(point1.x(), point1.y(), width, height);
rect.setPen(QPen(Qt::red));
firstRect = false;
}
else
{
scene()->removeItem(&rect);
rect.setRect(point1.x(), point1.y(), width, height);
}

scene()->clear();
scene()->addPixmap(image);
scene()->addItem(&rect);

point1.setX(0);
point1.setY(0);
}

event->accept();
}
Platform: Windows 7
Qt: 4.7.0

Thanks in advance
Kuba

wysota
12th March 2011, 21:34
Please provide a backtrace from the crash.

K.Sejdak
13th March 2011, 00:39
0 QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> >::data qscopedpointer.h 135 0x00fc8bee
1 qGetPtrHelper<QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> > > qglobal.h 2328 0x00de5b5d
2 QGraphicsScene::d_func qgraphicsscene.h 297 0x00ebbff0
3 QGraphicsScene::clear qgraphicsscene.cpp 2423 0x00df8d2f
4 ~GraphicsView GraphicsView.cpp 10 0x00401f02
5 QObjectPrivate::deleteChildren qobject.cpp 1949 0x6a2120f8
6 ~QWidget qwidget.cpp 1589 0x008938e8
7 ~MainWindow MainWindow.cpp 16 0x004018be
8 qMain main.cpp 10 0x0040140d
9 WinMain@16 qtmain_win.cpp 131 0x00402892
10 main 0 0x004025b8

wysota
13th March 2011, 00:45
Please show us how you created the view and scene objects.

K.Sejdak
13th March 2011, 01:14
The scene object is a private member of MainWindow class (not pointer) and the view, just as I said, was replaced with the "widget" object in Qt Designer so I don't create it myself. I will provide the header files to be clear:

MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QtGui>

namespace Ui
{
class MainWindow;
}

class MainWindow : public QWidget
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

public slots:
void loadImage();

private:
Ui::MainWindow *ui;
QPixmap image;
QGraphicsScene scene;
};

#endif // MAINWINDOW_H
GraphicsView.h
#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H

#include <QGraphicsView>
#include <QtGui>

class GraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit GraphicsView(QWidget *parent = 0);
~GraphicsView();
void loadImage(QPixmap item);
virtual void mouseReleaseEvent (QMouseEvent *event);

signals:

public slots:

private:
QPixmap image;
QPoint point1;
QPoint point2;
QGraphicsRectItem rect;
bool firstRect;
};

#endif // GRAPHICSVIEW_HNow you have almost all my code (only main is left).

Lykurg
13th March 2011, 06:37
Wild guess: Check if scene() returns a valid pointer in GraphicsView's destructor.

wysota
13th March 2011, 08:51
I would suspect there is a parent-child relationship between some objects that causes double deletion of something but I can't see anything like that in the code.

stampede
13th March 2011, 10:21
// qgraphicsscene.cpp
//...
void QGraphicsScene::clear(){
Q_D(QGraphicsScene);
// Recursive descent delete
for (int i = 0; i < d->indexedItems.size(); ++i) {
if (QGraphicsItem *item = d->indexedItems.at(i)) {
if (!item->parentItem())
delete item; //!< here
}
}
Maybe scene tries to delete your rect created on stack. Try to call removeItem() before clear() and see if it still crashes:

GraphicsView::~GraphicsView()
{
scene()->removeItem(&rect);
scene()->clear();
}

wysota
13th March 2011, 10:50
If the lifespan of the scene is tied to the lifespan of the view then the easiest way is to make the scene a dynamically allocated object with its parent set to the view. Then you don't have to clear the scene because the scene's destructor will do it for you. Maybe the problem will go away then. Also, what is causing the view to be deleted?

K.Sejdak
13th March 2011, 12:47
If the lifespan of the scene is tied to the lifespan of the view then the easiest way is to make the scene a dynamically allocated object with its parent set to the view. Then you don't have to clear the scene because the scene's destructor will do it for you. Maybe the problem will go away then. Also, what is causing the view to be deleted?

That was it! I did just as you said and it worked perfectly. In fact, it is much simplier and logical to modify an existing widget this way, than to replace it. Thanks for all your help. I will provide the changed code for the others with similar problems:


#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QWidget(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
view = new GraphicsView(ui->widget); // <--- here
view->setScene(&scene); //
view->resize(ui->widget->size()); //

connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(close()));
connect(ui->loadButton, SIGNAL(clicked()), this, SLOT(loadImage()));
}

MainWindow::~MainWindow()
{
delete view;
delete ui;
}

void MainWindow::loadImage()
{
QString fileName = QFileDialog::getOpenFileName();
image.load(fileName);
scene.addPixmap(image);
view->loadImage(image);
}

wysota
13th March 2011, 18:00
So what exactly did you change? Because if you mean line #7 of the above snippet then it's certainly not what I meant and it shouldn't have any influence on your application.

K.Sejdak
13th March 2011, 19:29
Hmm, now I realized that when I was reading your post I saw "view" instead of "scene" in part about dynamic allocation. Nevertheless it somehow solved the problem with the exit code. Do you think that it was just a luck? Maybe there is another mistake?

And to answer your question: I just dynamically allocated memory for view object and I set wiget from the main form to be a parent for it.

wysota
13th March 2011, 22:24
If you look inside setupUi(), that's exactly what was happening previously too.