将 std::cout 从单独线程中的 DLL 重定向到 QTextEdit



目标是将QtCreator中显示的所有应用程序输出作为调试控制台窗口显示在QTextEdit中,以便在同一应用程序中,只有那些有密码的人才能看到控制台窗口,而普通用户则不能。有一个带有多个 dll 的 exe。来自 DLL 和 qDebug 的所有 std::cout 都需要显示在调试控制台窗口中。

为了实现这一目标,我遵循了http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html

该代码适用于单线程,但在线程启动以调用 DLL 中的函数时会挂起。我想知道如何解决这个问题。


法典

在主窗口中.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QObject>
#include <QThread>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
     ui->setupUi(this);
    m_qd = new Q_DebugStream(std::cout,ui->textEdit); //Redirect Console output to QTextEdit
    m_qd->Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
    connect(ui->pushButtonSingleThreadTest, SIGNAL(clicked()),this,SLOT(SingleThreadTest()));
    connect(ui->pushButtonMultiThreadTest, SIGNAL(clicked()),this,SLOT(MultiThreadTest()));

}
void MainWindow::SingleThreadTest()
{
    run();
}
void MainWindow::MultiThreadTest()
{
    QThread     *workerThread= new QThread;
    ThreadWorker *worker  = new ThreadWorker();
    worker->moveToThread(workerThread);
    connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()));
    connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
    workerThread->start();
}
MainWindow::~MainWindow()
{
    delete ui;
}

在主窗口中。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "q_debugstream.h"
#include "../ToyProj1/Header.h"
namespace Ui {
class MainWindow;
}
class ThreadWorker : public QObject
{
    Q_OBJECT
public:
    ThreadWorker()
    {
    }
private:
signals:
    void finished();
public slots:
    void doWork()
    {
        run();
        emit finished();
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
public slots:
    void SingleThreadTest();
    void MultiThreadTest();

private:
    Ui::MainWindow *ui;
      Q_DebugStream* m_qd;
};
#endif // MAINWINDOW_H

在q_debugstream.H

#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H

#include <iostream>
#include <streambuf>
#include <string>
#include "QTextEdit.h"
class Q_DebugStream : public std::basic_streambuf<char>
{
public:
    Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
    {
        log_window = text_edit;
        m_old_buf = stream.rdbuf();
        stream.rdbuf(this);
    }
    ~Q_DebugStream()
    {
        m_stream.rdbuf(m_old_buf);
    }
    static void registerQDebugMessageHandler(){
        qInstallMessageHandler(myQDebugMessageHandler);
    }
private:
    static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
    {
        std::cout << msg.toStdString().c_str();
    }
protected:
    //This is called when a std::endl has been inserted into the stream
    virtual int_type overflow(int_type v)
    {
        if (v == 'n')
        {
            log_window->append("");
        }
        return v;
    }

    virtual std::streamsize xsputn(const char *p, std::streamsize n)
    {
        QString str(p);
        if(str.contains("n")){
            QStringList strSplitted = str.split("n");
            log_window->moveCursor (QTextCursor::End);
            log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line
            for(int i = 1; i < strSplitted.size(); i++){
                log_window->append(strSplitted.at(i));
            }
        }else{
            log_window->moveCursor (QTextCursor::End);
            log_window->insertPlainText (str);
        }
        return n;
    }
private:
    std::ostream &m_stream;
    std::streambuf *m_old_buf;
    QTextEdit* log_window;
};
#endif // Q_DEBUGSTREAM_H

在 DLL 中,

int run()
{
    std::cout << "Hello World" << std::endl;
    return 0;
}

代码示例上传到 github 以供参考。重复问题时构建 ToyProj1 和 ToyProj1GUI。

https://github.com/kuwt/ToyProject.git

通过有问题的帮助注释,此解决方案适用于信号和插槽机制。 std::cout 和 qDebug 被重定向到 QTextEdit。

主.cpp

#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    // Setup QMessageCatch
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
     qInstallMessageHandler(MainWindow::QMessageOutput);
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    return a.exec();
}

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QTextEdit>
#include "q_debugstream.h"
namespace Ui {
class MainWindow;
}
class ThreadWorker : public QObject
{
    Q_OBJECT
public:
    ThreadWorker()
    {
    }
private:
signals:
    void finished();
public slots:
    void doWork()
    {
         std::cout<< "Hello World2" <<std::endl;
        qDebug() << "Hello World2q" ;
        emit finished();
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    // QMessage
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
   static  void QMessageOutput(QtMsgType , const QMessageLogContext &, const QString &msg);
   /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
public slots:
    void SingleThreadTest();
    void MultiThreadTest();

private:
    Ui::MainWindow *ui;
    // MessageHandler for display and ThreadLogStream for redirecting cout
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    MessageHandler *msgHandler = Q_NULLPTR;
      ThreadLogStream* m_qd;
        /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
};
#endif // MAINWINDOW_H

主窗口.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QObject>
#include <QThread>

// Catch QMessage, redirect to cout
 /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
void MainWindow::QMessageOutput(QtMsgType , const QMessageLogContext &, const QString &msg)
{
   std::cout<<msg.toStdString().c_str()<<std::endl;
}
 /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
     ui->setupUi(this);
     // Set up ThreadLogStream, which redirect cout to signal sendLogString
     // Set up  MessageHandler,  wgucg catch message from sendLogString and Display
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    m_qd = new ThreadLogStream(std::cout); //Redirect Console output to QTextEdit
    this->msgHandler = new MessageHandler(this->ui->textEdit, this);
    connect(m_qd, &ThreadLogStream::sendLogString, msgHandler, &MessageHandler::catchMessage);
    /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/

    connect(ui->pushButtonSingleThreadTest, SIGNAL(clicked()),this,SLOT(SingleThreadTest()));
    connect(ui->pushButtonMultiThreadTest, SIGNAL(clicked()),this,SLOT(MultiThreadTest()));
}

void MainWindow::SingleThreadTest()
{
    std::cout<< "Hello World1" <<std::endl;
    qDebug() << "Hello World1q" ;
}
void MainWindow::MultiThreadTest()
{
    QThread     *workerThread= new QThread;
    ThreadWorker *worker  = new ThreadWorker();
    worker->moveToThread(workerThread);
    connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()));
    connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
    workerThread->start();
}
MainWindow::~MainWindow()
{
    delete ui;
}

q_debugstream.h

#ifndef ThreadLogStream_H
#define ThreadLogStream_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>
#include "QTextEdit"
#include "QDateTime"
// MessageHandler
 /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
class MessageHandler : public QObject
{
    Q_OBJECT
    public :
        MessageHandler(QTextEdit *textEdit, QObject * parent = Q_NULLPTR) : QObject(parent), m_textEdit(textEdit){}
    public slots:
        void catchMessage(QString msg)
        {
           this->m_textEdit->append(msg);
        }
        /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    private:
        QTextEdit * m_textEdit;
};
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/

class ThreadLogStream : public QObject, public std::basic_streambuf<char>
{
Q_OBJECT
public:
    ThreadLogStream(std::ostream &stream, QObject * parent = Q_NULLPTR) :QObject(parent), m_stream(stream)
    {
        m_old_buf = stream.rdbuf();
        stream.rdbuf(this);
    }
    ~ThreadLogStream()
    {
        // output anything that is left
        if (!m_string.empty())
        {
        emit sendLogString(QString::fromStdString(m_string));
        }
        m_stream.rdbuf(m_old_buf);
        }
protected:
    virtual int_type overflow(int_type v)
    {
        if (v == 'n')
        {
            emit sendLogString(QString::fromStdString(m_string));
            m_string.erase(m_string.begin(), m_string.end());
        }
        else
            m_string += v;
        return v;
    }
    virtual std::streamsize xsputn(const char *p, std::streamsize n)
    {
        m_string.append(p, p + n);
        long pos = 0;
        while (pos != static_cast<long>(std::string::npos))
        {
            pos = static_cast<long>(m_string.find('n'));
            if (pos != static_cast<long>(std::string::npos))
            {
                std::string tmp(m_string.begin(), m_string.begin() + pos);
                emit sendLogString(QString::fromStdString(tmp));
                m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
            }
        }
        return n;
    }
private:
    std::ostream &m_stream;
    std::streambuf *m_old_buf;
    std::string m_string;
signals:
    void sendLogString(const QString& str);
};
#endif // ThreadLogStream_H
对于

多头交易,您可以添加:QCoreApplication::p rocessEvents();在 q_debugstream.h 中类 MessageHandler 的 catchMessage(QString(msg) 方法中,在 append() 调用之后。这会"尽快"更新文本编辑。

最新更新