如何创建从另一个线程到 Qt5 GUI 线程的 qDebug 信号
How to create qDebug signal from another thread to the Qt5 GUI thread
我正在尝试在 GUI 中显示来自工作线程的日志消息。我正在努力关注
redirect qDebug to QTextEdit
开始工作正常,但是卡住了,怎么编程
QObject::connect(otherThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
我看到的原理,线程中的一个信号应该连接到GUI线程中的一个插槽;
但如何触发该信号?
此外,我使用 QDebug
进行了一些记录,但也向 std::cerr
进行了一些输出。
我可以混合这些输出吗?
(我的意思是,我可能会发出另一个信号,但我是否应该刷新消息,
或者我可以使用 Qt::QueuedConnection
)
的一个实例
另一个关于使用 QMutex
的问题。基本上,我只是在读取另一个线程设置的值,以及 starting/stopping 胎面。在这种简单的情况下我需要使用 QMutex
吗? (我的意思是我知道为什么要使用互斥量;我的问题是关于在使用 Qt 时,GUI 处理和线程处理的内部机制可能需要它)
我的试用帖实际上是一个演示代码
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);
}
}
我在 QMainWindow 的构造函数中进行的连接,在 resize() 之前。
createMenus();
...
mThread = new SimulatorThread(this);
QObject::connect(mThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
我正在使用
Qt 5.9.5
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
其实是我偷懒,在'About'框内插入了启动common
void SimulatorWindow::on_actionAbout_triggered() {
AboutWidget about;
about.exec();
mThread->run();
qInfo( "Thread started up\n");
}
线程中的循环结束时的线程returns。我没有收到 qInfo() 消息,如果我在 qInfo() 行之后放置断点,我会在 Qt Creator 应用程序消息中收到一条消息。
RTTI symbol not found for class 'QObject'
如果我有断点,我就不会在 GUI 中收到消息 window。如果我 运行 它没有断点,我会这样做,但只有当循环结束时,那个时候没有 'RTTI symbol not found'.
一定是同步出了问题。即使有断点,它也会冻结我的系统。
but how to trigger that signal?
在您的 QObject
-inherited class' 接口中声明信号。在 class 声明中插入 Q_OBJECT
宏。阅读 Singals and Slots in Qt.
例子。 Subclass QThread
在你的情况下(QThread
继承 QObject
)。阅读 Threading Basics in Qt and QThread docs.
页眉
#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;
};
从你需要的地方发出信号:
源文件
#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 头文件
#include <QtWidgets/QMainWindow>
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = Q_NULLPTR);
};
GUI class源文件
#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(); });
}
同样使用 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;
}
另请注意,Qt 提供了统一的位置来控制所有调试、警告、错误和其他类型的消息:
Qt Debuggin Techniques for c++
qInstallMessageHandler()
现在,您可以安装一次事件处理程序,然后所有消息都将转到一个地方,您可以在必要时输出它们,而不是使用自定义连接。
请注意 Qt 提供了几个全局宏来写出警告和调试文本:
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 to
std::cerr
. Can I mix these outputs?
似乎没有,我建议重写您使用 std::cerr <<
的代码并将其替换为 "qDebug() <<"
、qWarning() <<
等
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 use QMutex
in such simple case?
这个问题可能没那么简单。对于最简单的情况,volatile
可能就足够了。阅读 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 不影响此类编程基础知识。但请注意,Qt 中的所有 GUI 元素只能从 GUI 线程访问。见 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.
因此您不能直接从另一个线程访问 GUI 元素。
textEdit->append("some text"); // may be directly called from a GUI thread only
使用Singal-Slot mechanism to access GUI elements from another threads. Read also Qt5 New Signal Slot Syntax.
您还可以使用 Qt::QueuedConnection
调用方法,而无需先使用 QMetaObject::invokeMethod 连接:
QMetaObject::invokeMethod(textEdit, "append",
Qt::QueuedConnection,
Q_ARG(QString, "some text"));
另请阅读:
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...
编辑。 增强的有用文章列表。
信号与槽
Singals and Slots in Qt
Qt5 New Signal Slot Syntax
QMetaObject::invokeMethod
How Qt Signals and Slots Work
调试中
Qt Debuggin Techniques for c++
线程
Threading Basics in Qt
Multithreading Technologies in Qt
Synchronizing Threads
Threads and Objects
The Missing Article About Qt Multithreading in C++
Threads Events QObjects
我正在尝试在 GUI 中显示来自工作线程的日志消息。我正在努力关注
redirect qDebug to QTextEdit
开始工作正常,但是卡住了,怎么编程
QObject::connect(otherThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
我看到的原理,线程中的一个信号应该连接到GUI线程中的一个插槽;
但如何触发该信号?
此外,我使用 QDebug
进行了一些记录,但也向 std::cerr
进行了一些输出。
我可以混合这些输出吗?
(我的意思是,我可能会发出另一个信号,但我是否应该刷新消息,
或者我可以使用 Qt::QueuedConnection
)
另一个关于使用 QMutex
的问题。基本上,我只是在读取另一个线程设置的值,以及 starting/stopping 胎面。在这种简单的情况下我需要使用 QMutex
吗? (我的意思是我知道为什么要使用互斥量;我的问题是关于在使用 Qt 时,GUI 处理和线程处理的内部机制可能需要它)
我的试用帖实际上是一个演示代码
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);
}
}
我在 QMainWindow 的构造函数中进行的连接,在 resize() 之前。
createMenus();
...
mThread = new SimulatorThread(this);
QObject::connect(mThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
我正在使用
Qt 5.9.5
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
其实是我偷懒,在'About'框内插入了启动common
void SimulatorWindow::on_actionAbout_triggered() {
AboutWidget about;
about.exec();
mThread->run();
qInfo( "Thread started up\n");
}
线程中的循环结束时的线程returns。我没有收到 qInfo() 消息,如果我在 qInfo() 行之后放置断点,我会在 Qt Creator 应用程序消息中收到一条消息。
RTTI symbol not found for class 'QObject'
如果我有断点,我就不会在 GUI 中收到消息 window。如果我 运行 它没有断点,我会这样做,但只有当循环结束时,那个时候没有 'RTTI symbol not found'.
一定是同步出了问题。即使有断点,它也会冻结我的系统。
but how to trigger that signal?
在您的 QObject
-inherited class' 接口中声明信号。在 class 声明中插入 Q_OBJECT
宏。阅读 Singals and Slots in Qt.
例子。 Subclass QThread
在你的情况下(QThread
继承 QObject
)。阅读 Threading Basics in Qt and QThread docs.
页眉
#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;
};
从你需要的地方发出信号:
源文件
#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 头文件
#include <QtWidgets/QMainWindow>
class MainWin : public QMainWindow
{
Q_OBJECT
public:
MainWin(QWidget *parent = Q_NULLPTR);
};
GUI class源文件
#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(); });
}
同样使用 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;
}
另请注意,Qt 提供了统一的位置来控制所有调试、警告、错误和其他类型的消息:
Qt Debuggin Techniques for c++
qInstallMessageHandler()
现在,您可以安装一次事件处理程序,然后所有消息都将转到一个地方,您可以在必要时输出它们,而不是使用自定义连接。
请注意 Qt 提供了几个全局宏来写出警告和调试文本:
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?
似乎没有,我建议重写您使用 std::cerr <<
的代码并将其替换为 "qDebug() <<"
、qWarning() <<
等
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?
这个问题可能没那么简单。对于最简单的情况,volatile
可能就足够了。阅读 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 不影响此类编程基础知识。但请注意,Qt 中的所有 GUI 元素只能从 GUI 线程访问。见 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.
因此您不能直接从另一个线程访问 GUI 元素。
textEdit->append("some text"); // may be directly called from a GUI thread only
使用Singal-Slot mechanism to access GUI elements from another threads. Read also Qt5 New Signal Slot Syntax.
您还可以使用 Qt::QueuedConnection
调用方法,而无需先使用 QMetaObject::invokeMethod 连接:
QMetaObject::invokeMethod(textEdit, "append",
Qt::QueuedConnection,
Q_ARG(QString, "some text"));
另请阅读: 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...
编辑。 增强的有用文章列表。
信号与槽
Singals and Slots in Qt
Qt5 New Signal Slot Syntax
QMetaObject::invokeMethod
How Qt Signals and Slots Work
调试中
Qt Debuggin Techniques for c++
线程
Threading Basics in Qt
Multithreading Technologies in Qt
Synchronizing Threads
Threads and Objects
The Missing Article About Qt Multithreading in C++
Threads Events QObjects