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?
-
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.
-
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:
- Add a custom slot to my main window, invoke it and have it call setText() on the action.
- 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);