从 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)
)。此外,您不限于 QTextEdit
或 QPlainTextEdit
小部件,因此您可以使用您提供的带有 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();
}
我正在尝试在 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)
)。此外,您不限于 QTextEdit
或 QPlainTextEdit
小部件,因此您可以使用您提供的带有 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();
}