PDA

View Full Version : QGraphicsPathItem(parent) constructor crashes (SIGSEGV)



lexxxel
20th May 2014, 14:56
Hallo,

if I create an path object (class Path : public QObject, public QGraphicsPathItem) in an other std::thread then his parent item (class Anchor : public QObject, public QGraphicsPixmapItem), it crashes.
The funny thing is, that it doesn't crashes if the QMainWindow that holds the QGraphicScene has no parent. If it's parent is a QTabWidget it crashes.


Anchor.cpp

void Anchor::addDistanceValue(double distanceValue) {
mutex.lock();
bool stillWorking = isWorking;
mutex.unlock();
if(!stillWorking){
mutex.lock();
isWorking = true;
mutex.unlock();
if (wallCheckerThread.joinable()){
//QTime timeStamp = QTime::currentTime();
wallCheckerThread.join();
//qDebug() << "wait to join anker: " << id << "in " << timeStamp.msecsTo(QTime::currentTime()) << "ms";
}



wallCheckerThread = std::thread([&](double distanceValue){

QTime timeStamp = QTime::currentTime();

classMutex.lock();
mutex.lock();
Path* item = new Path(distanceValue * 100, this); // <<--- here is the problem, the this
mutex.unlock();
//item->setPos(pos());
item->setVisible(false);
//scene()->addItem(item);
classMutex.unlock();


checkAdditionalPathLossChanges(distanceValue, item);

qDebug() << "Anker " << getId() << ": " << timeStamp.msecsTo(QTime::currentTime()) << "ms";

QPen pen;
pen.setColor(0x8888ff);
item->setPen(pen);
item->setFlags(0);
item->setAcceptedMouseButtons(Qt::NoButton);

distanceVector.push_back(distanceValue);

ellipses.append(item);

if (distanceVector.length() > 1){
distanceVector.pop_front();

classMutex.lock();
delete ellipses.first();
//scene()->removeItem(ellipses.first());
classMutex.unlock();

if (!ellipses.isEmpty())
ellipses.pop_front();
}
item->setVisible(true);
mutex.lock();
isWorking = false;
mutex.unlock();
}, distanceValue);
}else{
qDebug() << "skipped anker " << id;
}
}

path.h

#include <QGraphicsItem>
#include <QObject>
#include <QGraphicsSceneMouseEvent>

class Path : public QObject, public QGraphicsPathItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)

public:
explicit Path(qreal radius, QGraphicsItem *parent = 0):
QGraphicsPathItem(parent) // << here it crashes (path.h 15)
{
myPath = new QPainterPath();
myPath->addEllipse(QRectF(-radius,-radius,radius * 2, radius * 2));
setPath(*myPath);
setZValue(1);
}

enum { Type = UserType + 3 };

int type() const { return Type; }
void addPainterPath(QPainterPath path){ *myPath += path; setPath(*myPath);}
QPainterPath* getPainterPath() {return myPath; }

protected:
// QGraphicsEllipseItem *item;
void mousePressEvent(QGraphicsSceneMouseEvent *event){
QGraphicsPathItem::mousePressEvent(event);
}

void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
QGraphicsPathItem::mouseMoveEvent(event);
}
QPainterPath *myPath;
};


working if its used this way:


#include <QApplication>
#include <QFile>
#include <QObject>
#include <QDebug>
#include "serialwidget.h"
#include "radarwidget.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
RadarWidget radarWidget;
radarWidget.show();
SerialWidget mainWindow;
mainWindow.show();
QObject::connect(&mainWindow, SIGNAL(newDistanceData(int, double)), &radarWidget, SLOT(addNodeDistance(int, double)));
radarWidget.setSettingsProvider(mainWindow.getSett ingsProvider());
return app.exec();


////////////////////////

just as additional info:
the signal is passed to the right anchor


void RadarGraphicsView::addNodeDistance(int id, double distance){
if(anchors.length() == 0){
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(calculateLocation()));
timer->start(1000);
QThread::msleep(20);
}

for(int i = 0; i < anchors.length(); i++){
if(anchors.at(i)->getId() == id){
anchors.at(i)->addDistanceValue(distance);
anchors.at(i)->update();
this->scene()->update();
return;
}
}
Anchor::classMutex.lock();
Anchor *newAnchor = new Anchor(id);
this->scene()->addItem(newAnchor);
Anchor::classMutex.unlock();
double x = settings->getSetting(id, "x");
double y = settings->getSetting(id, "y");
newAnchor->setPos(x,y);
newAnchor->addDistanceValue(distance);
anchors.append(newAnchor);
}
////////////////////////////////////////
}


it isnt working this way:


SerialWidget::SerialWidget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::SerialWidget)
{
ui->setupUi(this);
Pi = 3.1415;
intValidator = new QIntValidator(0, 3000000, this);
ui->baudRateBox->setInsertPolicy(QComboBox::NoInsert);
connect(ui->serialPortInfoListBox, SIGNAL(currentIndexChanged(int)), this, SLOT(showPortInfo(int)));
connect(ui->baudRateBox, SIGNAL(currentIndexChanged(int)), this, SLOT(checkCustomBaudRatePolicy(int)));
//connect(ui->actionSaveMeasurement, SIGNAL(triggered())
//connect(ui->localEchoCheckBox, SIGNAL(clicked(bool)), ui->console, SLOT(setLocalEchoEnabled(bool)));
fillPortsParameters();
fillPortsInfo();
updateSettings();
this->showMaximized();
deviceData.clear();

replayTool = new ReplayTool();
replayTool->moveToThread(&replayToolThread);
connect(replayTool, SIGNAL(sigDataRecieved(QByteArray)), this, SLOT(readData(QByteArray)));
replayToolThread.start(QThread::LowestPriority);

ui->cBDeviceSelector->blockSignals(true);
ui->cBDeviceSelector->insertItem(0,"General Settings");
ui->cBDeviceSelector->blockSignals(false);
ui->cBDeviceSelector->setDuplicatesEnabled(false);
on_friisOkPushButton_clicked();
deviceSettings = new SettingsProvider(&friisDefaultSettings);

rssiFilter = new RssiFilter();
isReplayPaused = false;
// if the qmainwindow is a chiild of this object ----------------------|
QObject::connect(this, SIGNAL(newDistanceData(int, double)), ui->sWRadar, SLOT(addNodeDistance(int, double))); // <<--- the radar is a child of the mainwindow
ui->sWRadar->setSettingsProvider(deviceSettings);
}

the debugger says:

SIGSEGV
Segmantation fault


stack:


0 ?? 0x1d2bf51c
1 QCoreApplicationPrivate::checkReceiverThread 513 0x6b929693
2 QApplication::notify 2791 0x91eb4ba
3 QCoreApplication::notifyInternal 935 0x6b929f96
4 QCoreApplication::sendEvent 237 0x9536429
5 QGraphicsScene::setFocus 2969 0x94a835a
6 QGraphicsScenePrivate::setActivePanelHelper 743 0x94a1c63
7 QGraphicsScene::setActivePanel 5644 0x94b180c
8 QGraphicsItem::setActive 3188 0x94813fc
9 QGraphicsItemPrivate::setParentItemHelper 1222 0x947b90a
10 QGraphicsItem::setParentItem 1685 0x947ce1b
11 QGraphicsItem::QGraphicsItem 1399 0x947c412
12 QAbstractGraphicsShapeItem::QAbstractGraphicsShape Item 8076 0x948c56a
13 QGraphicsPathItem::QGraphicsPathItem 8217 0x948c8c9
14 Path::Path path.h 15 0x43010a
15 Anchor::__lambda2::operator() anchor.cpp 364 0x40f3cb
16 std::_Bind_simple<Anchor::addDistanceValue(double)::__lambda2(double )>::_M_invoke<0u>(std::_Index_tuple<0u>) functional 1732 0x41184f
17 std::_Bind_simple<Anchor::addDistanceValue(double)::__lambda2(double )>::operator()(void) functional 1720 0x41173c
18 std::thread::_Impl<std::_Bind_simple<Anchor::addDistanceValue(double)::__lambda2(double )> >::_M_run(void) thread 115 0x4116a4
19 libstdc++-6!execute_native_thread_routine C:\Qt\5.3\mingw482_32\bin\libstdc++-6.dll 0xffe58f
20 pthread_create_wrapper C:\Qt\Tools\mingw482_32\opt\bin\libwinpthread-1.dll 0x64944e4b
21 wtoi64 C:\Windows\SysWOW64\msvcrt.dll 0x759c0bc4
22 msvcrt!_beginthreadex C:\Windows\SysWOW64\msvcrt.dll 0x759c0cec
23 KERNEL32!BaseThreadInitThunk C:\Windows\SysWOW64\kernel32.dll 0x75e5919f
24 ntdll!RtlInitializeExceptionChain C:\Windows\SYSTEM32\ntdll.dll 0x77e8a8cb
25 ntdll!RtlInitializeExceptionChain C:\Windows\SYSTEM32\ntdll.dll 0x77e8a8a1
26 ??

d_stranz
21st May 2014, 04:06
All GUI objects have to be owned by the main application thread. They can't be created or parented by object instances that live in other threads.

lexxxel
21st May 2014, 08:17
Thats not 100% true. Like I wrote it is working in Qt 5.2.1 and it is created in an other std::thread but not owned. The parent was created in the main GUI thread and the std::thread only manipulate attributes, it does not draw, interact on or with the GUI.

This is a minimal example. compile it as debug and it crashes, compile it as release and it works.


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <thread>
#include <QGraphicsPathItem>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

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

private:
Ui::MainWindow *ui;

bool init;
std::thread otherThread;
QGraphicsPathItem *itemParent;

private slots:
void on_pushButton_clicked();
};

#endif // MAINWINDOW_H


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsPixmapItem>
#include <QGraphicsView>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

QGraphicsScene *scene = new QGraphicsScene(this);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(0, 0, 100, 100);

//scene->setSceneRect(scene->sceneRect() + QMarginsF(100,100,100,100));
//scene->setSceneRect(-width/2, hight/2, width/2, hight/2);
ui->graphicsView->setCacheMode(QGraphicsView::CacheBackground);
ui->graphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectV iewportUpdate);
ui->graphicsView->setRenderHint(QPainter::Antialiasing);
ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnder Mouse);
ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportU pdate);
ui->graphicsView->setUpdatesEnabled(true);

init = false;
}

MainWindow::~MainWindow()
{
if(otherThread.joinable()) otherThread.join();
delete ui;
}

void MainWindow::on_pushButton_clicked()
{
if(init == false){
init = true;
QPainterPath path;
path.addEllipse(QRectF(-10,-10,10 * 2, 10 * 2));
itemParent = new QGraphicsPathItem(path);
ui->graphicsView->scene()->addItem(itemParent);

}else{
if(otherThread.joinable()){
otherThread.join();
}
otherThread = std::thread([&](){
QPainterPath path;
path.addRect(-10,-10,20,20);
QGraphicsPathItem *child = new QGraphicsPathItem(path, itemParent);
});
}
}

d_stranz
21st May 2014, 18:33
This is a minimal example. compile it as debug and it crashes, compile it as release and it works.


This is never a test for valid code, and in fact is a clear signal that you're doing something wrong. Memory layout, variable initialization, and many other things are different between debug and release builds, so whatever is happening to corrupt memory or the stack might only happen as result of this difference.

lexxxel
21st May 2014, 21:02
That is right. In my program I keep it thread safe with some mutexs. And in my project both debug and release worked when I used Qt 5.2.1

d_stranz
21st May 2014, 21:31
I don't know. I notice you're specifying variable capture [&] in your lambda expression, but you have no variables in the function definition. Don't know if that makes a difference.

You are creating your child path item in the otherThread, but you are giving it a parent that is owned by the scene in the main thread. I don't think that works. If you implement a signal / slot pair where the thread sends a signal asking for a child pointer, and the child item is created in the slot in the main thread and its pointer passed back to otherThread, that will ensure that all of the GUI items remain owned by the main thread, but can be manipulated by otherThread (with locking, of course).

lexxxel
21st May 2014, 22:54
I changed my program. Now i have only one object as a child, which is created in the parentObject constructor. I only change the Path in the otherThread.
But still something is wrong here. The thread check didn't work correct in my main project, but in the minimal example. I dont know why and I dont have the skill and time to fix it. But maybe someone else....