Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Qt emit signal after widget is closed



  • Hi,
    I have a child QDialog, when a button is pressed from it a socket is opened from QtConcurrent, and if there's an error an alert dialog is shown. But if the user closes the child QDialog to return to the main window the signal cannot be received by the child QDialog slot. I tried to broadcast the signal to parentWidget, but the program crashes, because this->parentWidget() doesn't exist anymore.
    I also tried to emit the QMainWindow ad QWidget from the signal to use in QDIalog, but the program crashes too.
    I use this code to emit the signal and connect it to the slot

    connect(this, SIGNAL(errorTcpSignal(QString)), this, SLOT(displayTcpError(QString)));
    
     connect(&t, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),[=](QAbstractSocket::SocketError err){
            emit errorTcpSignal("Error while changing game settings \n" + QVariant::fromValue(err).toString());
        });
    

    Is there a way to solve this issue? Thank you very much.


  • Qt Champions 2019

    @pmanc Do you mean you show the error dialog from the child dialog? Your description is not clear and you did not provide much code.



  • Hi, I'll try to be more clear.
    I have a QMainWindow. From this, I open a QDialog. From the QDialog i press a QPushButton. When I press the button this occurs:

    QtConcurrent::run([=](QString ip) {
            QTcpSocket t;
            connect(this, SIGNAL(errorTcpSignal(QString)), this->parent(), SLOT(displayTcpError(QString)),Qt::QueuedConnection);
            connect(&t, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),[=](QAbstractSocket::SocketError err){
                emit errorTcpSignal("Error while changing game settings \n" + QVariant::fromValue(err).toString());
            });
            t.connectToHost(ip,8888);
            t.waitForConnected();
            if(t.state() == QAbstractSocket::ConnectedState)
            {
                t.write(s.toUtf8());
                t.waitForBytesWritten();
            }
            t.close();
    
        },Settings::ipPc);
    

    In the QMainWindow I have this slot:

    
    void MainWindow::displayTcpError(QString e)
    {
        GenericErrorDialog d("Error","An error occurred.\n"
                                     "\nThe complete error is:\n" + QVariant::fromValue(e).toString());
        d.exec();
    }
    

    GenericErrorDialog is a QDialog.
    The problem is:
    if there's an error with the socket the GenericErrorDialog appears if the user does not close the QDialog, but if the user closes the QDialog (without waiting for the socket to return the error) to return to QMainWindow, the error box is not shown.
    Thank you very much.


  • Qt Champions 2019

    @pmanc said in Qt emit signal after widget is closed:

    connect(this, SIGNAL(errorTcpSignal(QString)), this->parent(), SLOT(displayTcpError(QString)),Qt::QueuedConnection);

    This should be other way around: the caller should connect the signals/slots. So, your dialog should have an error signal which you connect in MainWindow to displayTcpError. Children should not know anything about parents and especially they should not know implementation details of their parents (like displayTcpError()).



  • Thank you very much, you are right.
    The only problem is that :

    AdvancedDialog ad = new AdvancedDialog(this);
    connect(ad, SIGNAL(errorTcpSignal(QString)), this, SLOT(displayTcpError(QString)));
    ad->exec();
    ad->deleteLater();
    

    The deleteLater will make the program crash, if not included I'll have a memory leak.
    Anyway, I declared

    AdvancedDialog  ad
    

    in the header and in the QMainWindow constructor

    connect(&ad, SIGNAL(errorTcpSignal(QString)), this, SLOT(displayTcpError(QString)));
    

    This works, but is there a better way to do it?
    Cheers.


  • Qt Champions 2019

    @pmanc said in Qt emit signal after widget is closed:

    The deleteLater will make the program crash

    Did you analyse the crash? What happens? What are you doing in the destructor?



  • In the destuctor of AdvancedDialog there's only

    delete ui;
    

    The error is:

    Exception triggered
    the inferior stopped because it triggered an exception.
    Stopped in thread 18 by: Exception at 0x7fff5b5fd49 code 0xc0000005
    read access violation at 0xffffffffffffff  flags=0x0 (first chance)
    

    The debugger is stuck at

    AdvancedDialog::errorTcpSignal
    

    Thanks!

    Edit: I think the problem is that errorTcpSignal is declared in the header of AdvancedDialog, so when AdvancedDialog is deleted the signal cannot be emitted.


  • Qt Champions 2019

    We need to see the backtrace.



  • I hope this is good

    upload.PNG

    Thanks!


  • Qt Champions 2019

    @pmanc Looks like the signal is still emitted. Can you try to disconnect manually before calling deleteLater() to see whether it still crashes?



  • @jsulm I added

    disconnect(ad, nullptr, nullptr, nullptr);
    

    before deleteLater, but still crashes, plus at live 295 I have a semantic error:

    moc_advanceddialog.cpp:295:5: error: no matching function for call to 'activate'
    qobjectdefs.h:397:17: note: candidate function not viable: no known conversion from 'AdvancedDialog *' to 'QObject *' for 1st argument
    qobjectdefs.h:398:17: note: candidate function not viable: no known conversion from 'AdvancedDialog *' to 'QObject *' for 1st argument
    qobjectdefs.h:396:17: note: candidate function not viable: requires 3 arguments, but 4 were provided
    

  • Moderators

    @pmanc QObject::Connect returns a QMetaObject::Connection you're supposed to store that and call disconnect on that object



  • @J-Hilk
    Sorry, i didn't know.
    Now I have

    AdvancedDialog *ad = new AdvancedDialog(this);
    QMetaObject::Connection c = connect(ad, SIGNAL(errorTcpSignal(QString)), this, SLOT(displayTcpError(QString)));
    ad->exec();
    disconnect(c);
    ad->deleteLater();
    

    But still crashes, this SIGNAL is emitted.



  • disconnect won't stop the signal emitting, just stop calling the slot.
    I think you should disconnect the other connection, from that SocketError.
    That connection doesn't have a receiver context, so the lambda will be called even after this is destroyed.
    How about add this as the receiver context:

    connect(&t, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, [=](QAbstractSocket::SocketError err){
                emit errorTcpSignal(...);
            });
    


  • @Bonnie
    Thank you very much for your reply.
    The problem is that I want the signal to be emitted when I close the QDialog too.
    I have MainWindow with the slot for the error, and the QDialog that emits the signal.
    The core of the problem is that I want to receive the signal from QDialog 's QtConcurrent thread also after QDialog has been closed.



  • From your screen capture, the errorTcpSignal is a member from AdvancedDialog, right?
    Where do you emit it then? I'm not quite understanding your code since I don't know what is this in those connections.
    If you want the dialog to emit the signal, then you can't delete it.



  • @Bonnie
    Yes, errorTcpSignal is a member from AdvancedDialog.
    I emit it from a QtConcurrent thread in AdvancedDialog when an AdvancedDialog 's button is pressed.
    If I don't delete it everything works, but I'll have a memory leak.



  • @pmanc said in Qt emit signal after widget is closed:

    If I don't delete it everything works, but I'll have a memory leak.

    No, you won't.
    It have a parent widget (in your code when you new it), so it will be deleted after the parent is destoyed.
    But I still don't think it is a right design since you want to delete the dialog but also need it to send signals.
    Though I don't fully understand your logic, I think you should handle the QtConcurrent related code in your QMainWindow.



  • @Bonnie
    I see. But every time I open a new AdvancedDialog I see my memory use to grow in the task manager.
    Should I initialize AdvancedDialog once in the MainWindow constructor and show / hide it when I need?

    By the way, great community, everyone!


  • Qt Champions 2019

    @pmanc said in Qt emit signal after widget is closed:

    But every time I open a new AdvancedDialog

    If you need this dialog often then don't delete it and don't recreate it each time you need it. Create it once.



  • @pmanc said in Qt emit signal after widget is closed:

    Should I initialize AdvancedDialog once in the MainWindow constructor and show / hide it when I need?

    If you want to keep handling QtConcurrent related things in the dialog , I think you should.



  • Ok thank you very much for your time!


Log in to reply