问题
I am trying to display log messages from a work thread in a GUI. I am trying to follow
redirect qDebug to QTextEdit
It started to work fine, but I am stuck, how to program
QObject::connect(otherThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
The principle I see, that one signal in the thread shall be connected to a slot in the GUI thread;
but how to trigger that signal?
Also, I make some logging with QDebug
, but also some output to std::cerr
.
Can I mix these outputs?
(I mean, probably I shall make another signal, but shall I flush the messages,
or I can use one instance of Qt::QueuedConnection
)
Another question about using QMutex
. Basically, I am just reading the values set by the other thread, and starting/stopping the tread. Do I need to use QMutex
in such simple case? (I mean I know why to use a mutex; my question is about that when using Qt, the internal mechanisms of GUI handling and thread handling may make it a need)
My trial thread is actually a demo code
void SimulatorThread::run()
{
for(int i = 0; i <= 10; i++)
{
QMutex mutex;
// prevent other threads from changing the "Stop" value
mutex.lock();
if(this->Stop) break;
mutex.unlock();
emit debug("my text");
// slowdown the count change, msec
this->msleep(500);
}
}
The connect I make in the constructor of the QMainWindow, before resize().
createMenus();
...
mThread = new SimulatorThread(this);
QObject::connect(mThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
I am using
Qt 5.9.5
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Actually, I was lazy, and I inserted the startup common in the 'About' box.
void SimulatorWindow::on_actionAbout_triggered() {
AboutWidget about;
about.exec();
mThread->run();
qInfo( "Thread started up\n");
}
The thread returns when the loop in the thread is over. I do not receive the qInfo() message, and if I put a breakpoint after the line of qInfo(), I receive a message in the Qt Creator application message.
RTTI symbol not found for class 'QObject'
If I have the breakpoint, I do not receive my messages in the GUI window. If I run it without breakpoint, I do, but only when the loop is over, and that time there is no 'RTTI symbol not found'.
Something must be wrong with the synchronization. Even, with breakpoint, it also freezes my system.
回答1:
but how to trigger that signal?
Declare the signal in your QObject
-inherited class' interface. Insert Q_OBJECT
macro in the class declaration. Read Singals and Slots in Qt.
Example. Subclass QThread
in your case (QThread
inherits QObject
). Read Threading Basics in Qt and QThread docs.
Header
#include <QThread>
class OtherThread : public QThread
{
Q_OBJECT
public:
OtherThread(QObject *parent);
~OtherThread();
signals:
void debug(QString);
// You have to override run(). Don't try to call it anywhere.
// There is start() method to start a thread
protected:
void run() override;
};
emit the signal from the place you need:
Source file
#include "OtherThread.h"
OtherThread::OtherThread(QObject *parent)
: QThread(parent)
{ }
OtherThread::~OtherThread()
{
if(isRunning())
{
// Stop our loop
requestInterruption();
// Waits until return from run()
wait();
}
}
void OtherThread::run()
{
int it = 0;
while(!isInterruptionRequested())
{
// the line below will enqueue some call to the GUI thread
// no event loop in the sender thread is needed
emit debug(QString::number(it++));
msleep(500);
}
}
GUI class header file
#include <QtWidgets/QMainWindow>
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = Q_NULLPTR);
};
GUI class source file
#include "MainWin.h"
#include "OtherThread.h"
#include <QTextEdit>
#include <QTimer>
MainWin::MainWin(QWidget *parent)
: QMainWindow(parent)
{
auto textEdit = new QTextEdit(this);
setCentralWidget(textEdit);
auto otherThread = new OtherThread(this);
/*
No need to specify the connection type.
Qt::AutoConnection will be used by default.
In case of an automatic connection, Qt looks at the thread that invoked the signal
and compares it with the thread the receiver is living in to determine
which connection type it has to use.
*/
connect(otherThread, &OtherThread::debug,
textEdit, &QTextEdit::append);
// Attention: call start(), not run()!
otherThread->start();
// For example you want to stop the thread after 5 seconds
QTimer::singleShot(5000, [=]() { otherThread->requestInterruption(); });
}
Do the same using High-Level QtConcurrent API:
#include "MainWin.h"
#include <QtConcurrent/QtConcurrent>
#include <QThread> // for msleep
#include <atomic>
#include <QTextEdit>
#include <QTimer>
// Thread-safe flag to stop the thread. No mutex protection is needed
std::atomic<bool> gStop = false;
MainWin::MainWin(QWidget *parent)
: QMainWindow(parent)
{
auto textEdit = new QTextEdit(this);
setCentralWidget(textEdit);
// Run the code in another thread using High-Level QtConcurrent API
QtConcurrent::run([=]()
{
int it = 0;
while(!gStop)
{
QString text = QString::number(it++);
// No need to explicitly specify Qt::QueuedConnection,
// Qt::AutoConnection will be used
QMetaObject::invokeMethod(textEdit, "append",
Q_ARG(QString, text));
QThread::msleep(500);
}
});
// Timer to stop the thread after 5 seconds
QTimer::singleShot(5000, [=]() { gStop = true; });
}
MainWin::~MainWin()
{
// Stop the loop if we exit the program earlier than after 5 seconds,
// to avoid undefined behaviour in that case
gStop = true;
}
Please also note that Qt provides unified place to control all the debug, warning, error and other types of messages:
Qt Debuggin Techniques for c++
qInstallMessageHandler()
Now, you can install the event handler once and then all messages will go to one place where you can output them where necessary, not using custom connections.
Please note that Qt provides several global macros for writing out warning and debug text:
qDebug()
is used for writing custom debug output.qInfo()
is used for informational messages.qWarning()
is used to report warnings and recoverable errors in your application.qCritical()
is used for writing critical error messages and reporting system errors.qFatal()
is used for writing fatal error messages shortly before exiting.
.
Also, I make some logging with
QDebug
, but also some output tostd::cerr
. Can I mix these outputs?
Seems no, I recommend to rewrite the code where you are using std::cerr <<
and replace it by "qDebug() <<"
, qWarning() <<
, etc.
Another question about using
QMutex
. Basically, I am just reading the values set by the other thread, and starting/stopping the thread. Do I need to useQMutex
in such simple case?
This question may be not so simple. For the simplest cases volatile
may be enough. Read Synchronizing Threads.
(I mean I know why to use a mutex; my question is about that when using Qt, the internal mechanisms of GUI handling and thread handling may make it a need)
Qt doesn't affect to such programming basics. But please note that all GUI elements in Qt can be accessed only from the GUI thread. See Threading Basics in Qt:
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example
QPixmap
, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
So you cannot access GUI elements directly from another thread.
textEdit->append("some text"); // may be directly called from a GUI thread only
Use Singal-Slot mechanism to access GUI elements from another threads. Read also Qt5 New Signal Slot Syntax.
You can also invoke methods using Qt::QueuedConnection
without first connecting using QMetaObject::invokeMethod:
QMetaObject::invokeMethod(textEdit, "append",
Qt::QueuedConnection,
Q_ARG(QString, "some text"));
Read also: Multithreading Technologies in Qt:
Qt offers many classes and functions for working with threads. Below are four different approaches that Qt programmers can use to implement multithreaded applications...
Edit. Enhanced useful list of articles.
Signals and Slots
Singals and Slots in Qt
Qt5 New Signal Slot Syntax
QMetaObject::invokeMethod
How Qt Signals and Slots Work
Debugging
Qt Debuggin Techniques for c++
Threading
Threading Basics in Qt
Multithreading Technologies in Qt
Synchronizing Threads
Threads and Objects
The Missing Article About Qt Multithreading in C++
Threads Events QObjects
来源:https://stackoverflow.com/questions/60753560/how-to-create-qdebug-signal-from-another-thread-to-the-qt5-gui-thread