Changing button text from another thread.



  • I'm fairly new to Qt, so excuse me I'm missing something obvious.

    I have an action button named actionConnect on my mainToolbar that is labeled 'Connect'. I'm trying to change the text to read 'Disconnect' from another thread, when I receive an event from a socket saying the connection was successful. The following crashes the app when called from my other thread (which for reasons beyond my control happens to be a pthread, not a QThread):

    	QMetaObject::invokeMethod(ui->actionConnect, "setText", Q_ARG(QString, "Disconnect."));
    

    And if I call it from any other place, on my gui thread it does nothing at.

    Is there any reason setText would not be invokable on a QAction?


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    QThread is not a thread but a thread handler around platform specific implementation so on unix pthreads.

    Where is that ui variable coming from ?



  • It's the designer-supplied ui in my main window, looks like it is created magically by compiling the .ui xml file. This is all happening in a method of my MainWindow class.

    void MainWindow::connect()
    {
        // member variable, my websocket client wrapper which runs on its own thread.
         _client.request_session();
    
        //	add an open handler using a lambda.
        _client.add_open_handler([this]() {
                
    	std::cout << "Open Session.\n";   // <=== this works fine.
    
        // this does nothing.
    	QMetaObject::invokeMethod(ui->actionConnect, "setText", Q_ARG(QString, "Disconnect."));
    
       // ui->actionConnect->setText("Disconnect");    <== this works, oddly enough, even though I'm on another thread at this point.		
    });
    }
    

    The more I play around with it, though, I don't think this has anything to do with the threading. I happened to be using invokeMethod because I'm coming from another thread (and that is working fine in a few other cases, where I invoke methods on my other widgets). But no matter where I put the call, invokeMethod on a QAction (as opposed to a QWidget) seems to be ignored. There is also no Debug output, which seem to occur when you try to invoke things that don't exist.



  • OK, so I lied.

    This is produced in the debug window:

    QMetaObject::invokeMethod: No such method QAction::setText(QString)
    

    But according to these docs, it does exist, and as I said I can call it directly. My ui->actionConnect parameter seems like it must be valid, because QMetaObject is able to determine that it's type is QAction.

    http://doc.qt.io/qt-4.8/qaction.html#text-prop



  • The setText() is not a signal or a slot name on the QAction.
    The invokeMethod() can't invokes general functions.



  • Right, that makes sense, and now that I look, the api docs tag all the signals and slots, so it easy to know whether you can invoke a given method.

    So to achieve this (changing that button label from a non-ui thread), I'm thinking my options would be:

    1. Add a custom slot to my main window, invoke it and have it call setText() on the action.
    2. Subclass QAction and add a slot to it to do the dirty work.

    Option one seems simpler.

    Thanks for a clear explanation.



  • If you are already subclassing QThread it might be easier to add a signal to that subclass

    // Inside MyThread class, the QThread subclass
    signals:
    void updateConnectedButton(const QString&);
    

    and when the connection is enstablished call

    emit updateConnectedButton(tr("Disconnect"));
    

    now you can use Qt5 connection in your main window to link the two

    connect(otherthread, &MyThread::updateConnectedButton, ui->actionConnect, &QAction::setText);
    

Log in to reply
 

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