如何创建从另一个线程到Qt5 GUI线程的qDebug信号 [英] How to create qDebug signal from another thread to the Qt5 GUI thread

查看:109
本文介绍了如何创建从另一个线程到Qt5 GUI线程的qDebug信号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在GUI中显示来自工作线程的日志消息.我正在尝试

将qDebug重定向到QTextEdit

它开始工作正常,但是我被卡住了,如何编程

QObject::connect(otherThread, SIGNAL(debug(QString)),
                 s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);

我看到的原理是,线程中的一个信号应连接到GUI线程中的插槽; 但是如何触发该信号? 另外,我使用QDebug进行了一些日志记录,但是还对std::cerr进行了一些输出. 我可以混合这些输出吗? (我的意思是,也许我会再发出一个信号,但我应该清空消息, 或者我可以使用Qt::QueuedConnection)

的一个实例

关于使用QMutex的另一个问题.基本上,我只是在读取其他线程设置的值,然后启动/停止胎面.在这种简单情况下是否需要使用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);
    }
}

在resize()之前,我在QMainWindow的构造函数中建立的连接.

    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

实际上,我很懒,我在关于"框中插入了常见的启动项.

void SimulatorWindow::on_actionAbout_triggered() {
    AboutWidget about;
    about.exec();
    mThread->run();
    qInfo( "Thread started up\n");
}

当线程中的循环结束时,线程返回.我没有收到qInfo()消息,并且如果在qInfo()的行后放置一个断点,则会在Qt Creator应用程序消息中收到一条消息.

RTTI symbol not found for class 'QObject'

如果有断点,则不会在GUI窗口中收到消息.如果我在没有断点的情况下运行它,那么我会这样做,但只有在循环结束时才能这样做,并且那时没有"RTTI符号未找到".

同步一定有问题.即使有断点,它也冻结了我的系统.

解决方案

但是如何触发该信号?

在您的QObject继承的类'接口中声明信号.在类声明中插入Q_OBJECT宏.阅读 Qt中的单字和空位.

示例.您的子类QThread(QThread继承了QObject).阅读 Qt中的线程基础

GUI类头文件

#include <QtWidgets/QMainWindow>

class MainWin : public QMainWindow
{
    Q_OBJECT

public:
    MainWin(QWidget *parent = Q_NULLPTR);
};

GUI类源文件

#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(); });
}


使用执行相同的操作 高级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提供了统一的位置来控制所有调试,警告,错误和其他类型的消息:

用于c ++的Qt调试技术
qInstallMessageHandler()

现在,您可以安装一次事件处理程序,然后所有消息都将转到一个位置,您可以在需要的位置将其输出,而无需使用自定义连接.

请注意,Qt提供了几个全局宏来写警告和调试文本:

qDebug()用于编写自定义调试输出.
qInfo()用于参考消息.
qWarning()用于报告警告和 您的应用程序中的可恢复错误.
qCritical()用于 编写严重错误消息并报告系统错误.
qFatal()用于在退出前不久写入致命错误消息.

.

此外,我使用QDebug进行一些日志记录,但也将一些输出记录到 std::cerr.我可以混合这些输出吗?

似乎没有,我建议在使用std::cerr <<的地方重写代码,并用"qDebug() <<"qWarning() <<等替换它.

关于使用QMutex的另一个问题.基本上,我只是在阅读 另一个线程设置的值,以及启动/停止线程.我会吗 在这种简单情况下需要使用QMutex吗?

这个问题可能不是那么简单.对于最简单的情况,volatile可能就足够了.阅读同步线程.

(我的意思是我知道为什么要使用互斥锁;我的问题是关于何时 使用Qt,GUI处理和线程处理的内部机制 可能有需要)

Qt不会影响此类编程基础.但是请注意,只能从GUI线程访问Qt中的所有GUI元素.请参见 Qt中的线程基础:

如上所述,每个程序在启动时都有一个线程.该线程称为主线程" (在Qt应用程序中也称为"GUI线程"). Qt GUI必须 在此线程中运行. 所有小部件和几个相关的类,用于 例如QPixmap,则不能在辅助线程中工作. 通常被称为工作线程",因为它用于 从主线程分担处理工作.

因此,您不能直接从另一个线程访问GUI元素.

textEdit->append("some text"); // may be directly called from a GUI thread only

使用单槽机制从另一个线程访问GUI元素.另请阅读 Qt5新信号插槽语法.

您还可以使用Qt::QueuedConnection调用方法,而无需先使用 QMetaObject进行连接: :invokeMethod :

QMetaObject::invokeMethod(textEdit, "append", 
    Qt::QueuedConnection, 
    Q_ARG(QString, "some text"));

另请阅读: Qt中的多线程技术:

Qt提供了许多用于处理线程的类和函数.以下 Qt程序员可以使用四种不同的方法来实现 多线程应用程序...


编辑.增强了有用的文章列表.

信号和插槽
Qt中的单数和插槽
Qt5新信号插槽语法
QMetaObject :: invokeMethod
Qt信号和插槽如何工作

调试
用于c ++的Qt调试技术

线程
Qt中的线程基础
Qt中的多线程技术
同步线程
线程和对象
有关C ++中Qt多线程的缺失文章
线程事件QObjects

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.

解决方案

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 to std::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 use QMutex 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

这篇关于如何创建从另一个线程到Qt5 GUI线程的qDebug信号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆