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.


  • Lifetime Qt Champion

    Hi,

    It looks like you are implementing that like a QDialog so why not use one to show your chart ?



  • @SGaist

    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.


  • Lifetime Qt Champion

    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().


  • Lifetime Qt Champion

    How did you determine that the series wasn't destroyed ?



  • By doing qDebug()<<series; after loop.exec(); which successfully prints QtCharts::QLineSeries(0x37e7530).
    Placing delete series; before qDebug()<<series; , or doing qDebug()<<chart; results in "program.exe has stopped working"


  • Lifetime Qt Champion

    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.



  • Try to open 2 or more charts and close them lifo. The instruction "delete series;" doesn't cause any trouble if doing so. If it's about dangling pointers why does this work ?

    Also, the instruction qDebug()<<chart; alone blocks the program.



  • @SGaist I also tried printing a point from the series by doing qDebug()<<series->at(0).x(); and get a valid result.


  • Qt Champions 2016

    @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 ?


  • Qt Champions 2016

    @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 (with delete), it can however be scheduled for deletion (with QObject::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 the chart is if you attach to the QObject::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 with series->deleteLater(), which would solve your problem, if it is in fact a double deletion.



  • @kshegunov @SGaist

    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.


  • Qt Champions 2016

    @cpper
    Yes, the object's deferred deletion is yet again deferred ...
    Local event loops are discouraged in principle, and you can see why.



  • 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.


  • Qt Champions 2016

    @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) .


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.