Reputation: 385
Similar to C# I have used a working snippet such as:
QString text = "Hello";
QMetaObject::invokeMethod(m_ui.textEdit_ConnectionStatus, "setText", Qt:QueuedConnection, Q_ARG(QString, text));
... in order to change GUI elements not from the main thread. However, it does not appear to be working in the same way with setPalette for the textEdit.
With:
QPalette pal = palette();
pal.setColor(QPalette::Base, Qt:darkGreen);
QMetaObject::invokeMethod(m_ui.textEdit_ConnectionStatus, "setPalette", Qt:QueuedConnection, Q_ARG(const QPalette&, pal));
How does one go about to change the color of this gui element from another thread?
Edit1: I forgot to mention the output spits out:
"QMetaObject::invokeMethod No such Method QTextEdit:setpalette(const QPalette&)"
Upvotes: 1
Views: 218
Reputation: 20141
The OP used the QMetaObject::invokeMethod()
which relies on registered slots (as they were usual in Qt4). QTextEdit::setText() is such a slot but QTextEdit::setPalette() is not.
Hence, the QTextEdit::setPalette()
cannot be found at runtime by its name given as string.
With Qt5, the signal-slot concept was extended to support the connection of signals and slots with compile-time checking.
Out of curiosity, I had a look into the doc. and found QMetaObject::invokeMethod() which accepts a functor:
template <typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)
This is an overloaded function.
Invokes the function in the event loop of context. function can be a functor or a pointer to a member function. Returns true if the function could be invoked. Returns false if there is no such function or the parameters did not match. The return value of the function call is placed in ret.
Note: This function is thread-safe.
This function was introduced in Qt 5.10.
Thereby, I would like to emphasize Note: This function is thread-safe.
So, I made an MCVE to check this out:
// standard C++ header:
#include <chrono>
#include <thread>
// Qt header:
#include <QtWidgets>
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QTextEdit qTextEdit(QString(
"<p>Hello world.</p>"
"<p>Hello Qt.</p>"
"<p>Hello Stack Overflow.</p>"));
qTextEdit.show();
// a separate thread to manipulate qTextEdit
std::thread threadPal([&qTextEdit]() {
using namespace std::chrono_literals;
const QColor qColors[] = { Qt::red, Qt::green, Qt::blue, Qt::white };
QColor qColor;
for (const QColor &qColor_ : qColors) {
std::this_thread::sleep_for(1s);
qColor = qColor_;
QMetaObject::invokeMethod(&qTextEdit, [&qTextEdit, qColor]() {
QPalette qPal = qTextEdit.palette();
qPal.setColor(QPalette::Base, qColor);
qTextEdit.setPalette(qPal);
});
}
});
// runtime loop
const int ret = app.exec();
// done
threadPal.join();
return ret;
}
Output:
Please, note that I (carefully) did every access to qTextEdit
exclusively
QMetaObject::invokeMethod()
.Qt widgets are by default not thread-safe. So, I have to ensure that the accesses to widgets happen in the GUI thread only (or had to be appropriately guarded).
The reference of qTextEdit
is captured in the functor of threadPal
. It is used to provide the address of qTextEdit
to QMetaObject::invokeMethod()
as context. That's necessary to make QMetaObject::invokeMethod()
aware that the provided functor has to be executed in a different thread (the GUI thread to which qTextEdit
is associated to). (In opposition to qTextEdit
itself, the address of qTextEdit
is immutable while the thread is running. Hence, an unguarded access is thread-safe.)
Upvotes: 1
Reputation: 14714
If you're using Qt 5.10 or later, you can call invokeMethod
on any invokable, such as a lambda:
QPalette pal = palette();
pal.setColor(QPalette::Base, Qt:darkGreen);
auto setPalette = [this, pal] { m_ui.textEdit_ConnectionStatus->setPalette(pal); };
QMetaObject::invokeMethod(m_ui.textEdit_ConnectionStatus, setPalette, Qt:QueuedConnection);
Upvotes: 1
Reputation: 373
As per Qt Documentation, invokeMethod()
invokes the member (a signal or a slot name) on the object.
Since in the first case, setText()
is a slot of QTextEdit
and hence it works perfect.
However in the second case, setPalette()
is neither a signal nor a slot and hence you get "QMetaObject::invokeMethod No such Method QTextEdit:setpalette(const QPalette&)" as output.
Moreover it returns false if there is no such member (a signal or a slot name) or the parameters did not match.
Upvotes: 1