C++ 如何创建从另一个线程到Qt5 GUI线程的qDebug信号

C++ 如何创建从另一个线程到Qt5 GUI线程的qDebug信号,c++,multithreading,qt5,qthread,C++,Multithreading,Qt5,Qthread,我试图在GUI中显示来自工作线程的日志消息。我正在努力跟上 它开始工作的很好,但我被卡住了,如何编程 QObject::connect(otherThread, SIGNAL(debug(QString)), s_textEdit, SLOT(append(QString)), Qt::QueuedConnection); 我看到的原理是,线程中的一个信号应连接到GUI线程中的一个插槽; 但是如何触发这个信号呢? 此外,我还使用QDebug进行了一些日志记录

我试图在GUI中显示来自工作线程的日志消息。我正在努力跟上

它开始工作的很好,但我被卡住了,如何编程

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);
    }
}
我正在使用

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 symbol not found”

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

但是如何触发这个信号呢

QObject
-继承类的接口中声明信号。在类声明中插入
Q_对象
宏。阅读

例如。在您的情况下,子类
QThread
QThread
继承
QObject
)。读和读

标题

#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;
};
GUI类头文件

#include <QtWidgets/QMainWindow>

class MainWin : public QMainWindow
{
    Q_OBJECT

public:
    MainWin(QWidget *parent = Q_NULLPTR);
};
#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(); });
}
使用该机制从其他线程访问GUI元素。也请阅读

您还可以使用
Qt::QueuedConnection
调用方法,而无需首先使用以下连接:

另请阅读: :

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


编辑。增强的有用文章列表

信号和插槽



调试

线程化






I工作得很好,只是发出的信号只有在OtherThread.run()退出时才被接收。在我的试验中,“emit debug”是在for循环中发出的,带有延迟。我在GUI编辑器中看不到任何更改,但是当for循环结束时,我会收到所有的输出行。这不是我所期望的线程式行为。我可以做些额外的事吗?我希望在msleep(500)中,GUI线程会收到控件和消息,并且会立即显示。@katang请编辑您的问题以添加for循环代码,以及您使用的Qt版本和编译器是什么?@katang,您在哪里调用connect()?信号发射应该适用于这种情况。接收问题似乎是其他问题。您的主要错误是调用
mThread->run()。您必须重写
run()
(受
保护的
成员),但要调用
start()
。请参阅我更新的示例。还请注意,您可以使用
std::atomic
变量来避免布尔变量的互斥保护。请注意,这是互斥的错误用法。我将研究它。正如我提到的,我从这个链接中选取了一个例子;因此,在那个地方添加一条评论是值得的。
#include <QtWidgets/QMainWindow>

class MainWin : public QMainWindow
{
    Q_OBJECT

public:
    MainWin(QWidget *parent = Q_NULLPTR);
};
#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(); });
}
#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;
}
textEdit->append("some text"); // may be directly called from a GUI thread only
QMetaObject::invokeMethod(textEdit, "append", 
    Qt::QueuedConnection, 
    Q_ARG(QString, "some text"));