从 qt 的 qtextedit 对象中的 spdlog 获取日志

Get log from spdlog in qtextedit object from qt

我正在尝试在 qtextedit windows 中重定向我的日志。我用这个 post : Redirecting std::cout from DLL in a separate thread to QTextEdit.

这是我的测试程序:

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

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QTextEdit>

#include "q_debugstream.h"
#include "spdlog/spdlog.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" ;
        SPDLOG_INFO("Hello world2 spdlog");
        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

mainwindow.cpp

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

#include "spdlog/spdlog.h"

// 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" ;
    SPDLOG_INFO("Hello world1 spdlog");
}

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;
}

main.cpp

#include "mainwindow.h"
#include <QApplication>

#include "spdlog/spdlog.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    SPDLOG_INFO("Starting main");

    MainWindow w;
    w.show();

    // Setup QMessageCatch
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
     qInstallMessageHandler(MainWindow::QMessageOutput);
     /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    return a.exec();
}

我的问题:来自 SPDLOG_INFO 的日志出现在我的控制台中,但没有出现在我的 qtextedit window 中。显然,来自 spdlog 的日志不会被 qInstallMessageHandler 的函数 QMessageOutput 捕获。

我的问题:是否可以在 qtextedit 框上从 spdlog 捕获日志?

在 spdlog 的最新版本中引入了 qt_sinks 功能。要使用它,请包含 header spdlog/sinks/qt_sinks 然后调用构造 st 或 mt(thread-safe) 记录器的工厂函数 (spdlog::qt_logger_mt("logger_name", ui->plainTextEdit))。此外,您不限于 QTextEditQPlainTextEdit 小部件,因此您可以使用您提供的带有 QString 参数的插槽名称将日志接收到 QML、QUdpSocket 和任何自定义小部件或基于 类 的 QObject。

用法

custom_class.hpp

#ifndef CUSTOM_CLASS_HPP
#define CUSTOM_CLASS_HPP

#include <QObject>

class custom_class : public QObject
{
    Q_OBJECT
public:
    explicit custom_class(QObject *parent = nullptr);

public slots:
    void log2qml(const QString &str); // slot for using spdlog with custom objects

signals:
    void send2qml(const QString& str);

};

#endif // CUSTOM_CLASS_HPP

custom_class.cpp

#include "custom_class.hpp"

custom_class::custom_class(QObject *parent) : QObject(parent)
{
    
}

void custom_class::log2qml(const QString &str)
{
    emit send2qml(str);
}

mainwindow.hpp:

#include "custom_class.hpp"
#include <QMainWindow>
#include <QThread>

#include <spdlog/sinks/qt_sinks.h>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void log();

private:
    Ui::MainWindow *ui;
    custom_class c;
    QThread *th = nullptr;
    std::shared_ptr<spdlog::logger> logger = nullptr, logger_custom = nullptr;
};
#endif // MAINWINDOW_HPP

mainwindow.cpp:

#include "mainwindow.hpp"
#include "ui_mainwindow.h"

#include "spdlog/spdlog.h"


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
      , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // logger write into ui->plainTextEdit
    logger = spdlog::qt_logger_mt("plaintextedit_logger", ui->plainTextEdit);
    
    // custom_logger write into qml or any custom object. 3rd param is slot name that passes QString param in custom_object
    logger_custom = spdlog::qt_logger_mt("custom_qml_logger", &c, "log2qml");
    
    // set default logger
    spdlog::set_default_logger(logger);
    log();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::log()
{
    th = QThread::create([this] {
   

 for (int i = 0; i < 1000; ++i) {
        SPDLOG_INFO(i); // default logger write into ui->plainTextEdit
        logger_custom->warn(i); // custom_logger write into qml or any custom object
        std::this_thread::sleep_for(std::chrono::microseconds(1));
    }
});

 th->start();
}

输出: