Program.exe has stopped working
-
I'm trying to display a chart when clicking on a push button. My problem is the following: If I press two times on the button, the a chart is displayed two times; if I close the first chart , and then the second the program stops working. Closing the second widget and than the first works fine. Here is the code of the push button slot. The .pro file and everything else is fine:
void MainWindow::on_pushButton_clicked() { QChart* chart = new QChart; QLineSeries* series = new QLineSeries; series->append(10,15); series->append(13,17); series->append(21,12); series->append(24,18); chart->addSeries(series); chart->createDefaultAxes(); QChartView* chartView=new QChartView(chart); chartView->move(300,200); chartView->resize(1000,400); QEventLoop loop; chartView->setAttribute( Qt::WA_DeleteOnClose ); connect(chartView,&QChartView::destroyed,&loop,&QEventLoop::quit); chartView->show(); loop.exec(); delete series; }
I've created the loop and called chartView->setAttribute( Qt::WA_DeleteOnClose ); because I need to pause the function until I close the widget, or else the series are deleted. This is a as much as possible simplified version of the a bigger program.
-
Hi,
It looks like you are implementing that like a QDialog so why not use one to show your chart ?
-
Commodity I guess, with directly displaying the widget I wouldn't need to declare another class. I've managed to find a solution to the problem by inheriting from QChartView and emitting a signal from overridden closeEvent function. But would it be a better idea to wrap the widget in a QDialog instead ?
However, I'm still curios what is wrong in the original code, if you mind to explain.
-
Likely the delete of the
series
object. The chart takes ownership of the series so technically you have a double deletion. -
@SGaist Don't think that is the problem, if I'm closing the second opened widget first, everything is ok.
It also seems that the series are not deleted when the QChartView is destroyed. The QChart however, is destroyed. I've tested by doing qDebug()<<series after loop.exec(). -
How did you determine that the series wasn't destroyed ?
-
Having a pointer with an address in doesn't make it valid. Calling delete on a pointer in C++ doesn't result in having it re-initialized. You have a dangling pointer that you are trying to delete.
-
@cpper
Consider this:char * test = new char[4]; test[0] = 'A'; test[3] = 'C'; delete test; //< Now we don't have the memory anymore qDebug() << test[3]; //< Reading an invalid location, yet you'd probably get 'C' and no crash
It's the same with objects, until memory is overwritten you have a good chance that your object is still there, so getting a value for:
qDebug() << series->at(0).x();
just means nothing.
What does this print out:
void MainWindow::on_pushButton_clicked() { // ... Your code before loop.exec() goes here ... QPointer<QLineSeries> guarded(series); loop.exec(); if (!guarded) qDebug() << "Ouch! We have a nasty ownership problem, we want to delete an object we don't own ..."; else { qDebug() << "We are okay, series points to a valid object"; delete series; } }
-
It prints ""We are okay, series points to a valid object".
If guarded pointer points to the QChart it prints ""Ouch! We have a nasty ownership problem, we want to delete an object we don't own ...";
So this finally confirms that the chart gets deleted but the series not, right ?@SGaist said in Program.exe has stopped working:
Having a pointer with an address in doesn't make it valid. Calling delete on a pointer in C++ doesn't result in having it re-initialized. You have a dangling pointer that you are trying to delete.
After loop.exec() try qDebug()<<series , and qDebug()<<chart . The first one works but the second crashed the program.
Or consider this: QWidget* w=new QWidget(); qDebug()<<w; . This works and prints an address. But QWidget* w=new QWidget(); delete w; qDebug()<<w; crashes the program.
I'm not saying that you are wrong, but trying to qDebug() a deleted pointer crashes the program. Since qDebug()<<series worked for me means series wasn't deleted. Or ? -
@cpper said in Program.exe has stopped working:
So this finally confirms that the chart gets deleted but the series not, right ?
No, not necessarily. It means that
series
is not deleted directly (withdelete
), it can however be scheduled for deletion (withQObject::deleteLater
). And one of the specific things about local event loops is that they don't process those events, they delegate them to the higher-level event loop. So, you may still be getting a double deletion. The other thing that you could try and it will ultimately show whether or not the object is deleted by thechart
is if you attach to theQObject::destroyed
signal (instead of deleting it explicitly):void MainWindow::on_pushButton_clicked() { // ... loop.exec(); // delete series; QObject::connect(series, &QObject::destroyed, [] (QObject *) -> void { qDebug() << "Series is deleted in the parent event loop!" }); }
Of course, you can also just try substituting
delete series
withseries->deleteLater()
, which would solve your problem, if it is in fact a double deletion. -
The slot is indeed called. So the conclusion of the illusion is that the chart is immediately destroyed after the widget is closed, but the series is later destroyed. This explains everything. Thanks guys for being pacient and making everything clear to me.
-
This was the original code:
QEventLoop loop; chartView->setAttribute( Qt::WA_DeleteOnClose ); connect(chartView,&QChartView::destroyed,&loop,&QEventLoop::quit); chartView->show(); loop.exec(); delete series;
Now that I've learned that deleting the chartView also deletes the chart,series, and axis, I've replaced the code with :
chartView->setAttribute( Qt::WA_DeleteOnClose ); chartView->show();
And it works like a charm now. Of course, the complete version of the program.
One last thing I've noticed: If I explicitly delete series, the program does not crash if it automatically wants to delete series (later). But if the program has already automatically deleted series and I try to explicitly delete it afterwards, I get the "program.exe has stopped working". That being said, it seems the automatic deletion(the ownership thing) checks firstly if the pointer is still valid, like you did in the example with the "guarded" pointer.
-
@cpper said in Program.exe has stopped working:
That being said, it seems the automatic deletion(the ownership thing) checks firstly if the pointer is still valid, like you did in the example with the "guarded" pointer.
It does, sort of - it removes the pending events for the object, because Qt has to guarantee that multiple calls to QObject::deleteLater don't lead to side effects (as is documented) .