I am trying to redirect my log in a qtextedit windows. I use this post : Redirecting std::cout from DLL in a separate thread to QTextEdit.
Here is my test program :
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();
}
My problem : logs from SPDLOG_INFO appear in my console, but not in my qtextedit window. Obviously, the logs from spdlog are not catch by the function QMessageOutput from qInstallMessageHandler.
My question : Is it possible to catch the log from spdlog on a qtextedit box ?
In spdlog last versions have introduced qt_sinks feature. To use this include the header spdlog/sinks/qt_sinks then call a factory function (spdlog::qt_logger_mt("logger_name", ui->plainTextEdit)) that constructs st or mt(thread-safe) loggers. Also you are not limited to QTextEdit or QPlainTextEdit widgets so you can sink logs into QML, QUdpSocket and any custom widget or QObject based classes with your provided a slot name with a QString parameter.
Usage
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();
}
output:
Related
I am quite new to programming, so hopefully someone can help me out. After calculating a value with a function and storing it into the variable with a setter, I want to call it in my viswindow.cpp to visualize it in a Graph. In the mainwindow the right value is calculated and stored by pressing a button, which also opens the viswindow. I tried to pass it with a pointer or with signals and slots, but it wouldn't work.
mainwindow.cpp:
double MainWindow::berechneFussabdruck(double strecke, int anzahl, double sfcWert)
{
if(strecke <= 1500 && strecke > 0){
double aequivalente_Co2 = (((sfcWert*3.116*50*(((0.0011235955)*(strecke-2*48.42))+0.233))*0.001)/anzahl);
setAequivalente(aequivalente_Co2);
}
else{
double aequivalente_Co2 = (((sfcWert*3.116*75*(((0.0011235955)*(strecke-2*208))+0.833))*0.001)/anzahl);
setAequivalente(aequivalente_Co2);
}
return aequivalente_Co2;
}
Button to open viswindow:
void MainWindow::on_pushButton_clicked()
{
MainWindow::berechneFussabdruck(strecke, anzahl, sfcWert);
visWindow = new VisWindow(this);
visWindow->show();
}
viswindow.cpp:
VisWindow::VisWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::VisWindow)
{
ui->setupUi(this);
...
*set0 << aequivalente_Co2 << aequivalente_Co2;
*set1 << 11.07 << 0;
*set2 << 0 << 0.49;
...
}
Thanks for helping!
Here are examples of:
Single time value transfer from MainWindow to VisWindow in constructor parameter.
Value update from MainWindow to VisWindow by slot-signal chain.
Value update from MainWindow to VisWindow by direct call of VisWindow->updateValue() slot as regular function.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "viswindow.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
signals:
void updatevalue(const double value);
private:
Ui::MainWindow *ui;
VisWindow * m_viswindow;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::on_pushButton_clicked() {
m_viswindow = new VisWindow(12.34, nullptr);
connect(this, &MainWindow::updatevalue,
m_viswindow, &VisWindow::updateValue);
m_viswindow->show();
}
void MainWindow::on_pushButton_2_clicked() {
emit updatevalue(23.45);
}
void MainWindow::on_pushButton_3_clicked() {
m_viswindow->updateValue(45.67);
}
viswindow.h
#ifndef VISWINDOW_H
#define VISWINDOW_H
#include <QWidget>
namespace Ui {
class VisWindow;
}
class VisWindow : public QWidget
{
Q_OBJECT
public:
explicit VisWindow(const double value, QWidget *parent = nullptr);
~VisWindow();
public slots:
void updateValue(const double value);
private:
Ui::VisWindow *ui;
};
#endif // VISWINDOW_H
viswindow.cpp
#include "viswindow.h"
#include "ui_viswindow.h"
VisWindow::VisWindow(const double value, QWidget *parent) :
QWidget(parent),
ui(new Ui::VisWindow)
{
ui->setupUi(this);
ui->lblValue->setText(QString::number(value,'f',2));
this->setWindowTitle("VisWindow");
}
VisWindow::~VisWindow() {
delete ui;
}
void VisWindow::updateValue(const double value) {
ui->lblValue->setText(QString::number(value,'f',2));
}
This is the code i'm using now the issue is when i press on the pushbutton
the thread is starting and the value in line edit is updating.
but it slows down the GUI overall.
I am learning QThread so implemented this code and facing difficulties in it.
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
qDebug()<<"pd1";
work->moveToThread(thread);
connect(work, SIGNAL(finished()), work, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), work, SLOT(process()));
connect(work, SIGNAL(datar(int)), this, SLOT(display(int)));
connect(work, SIGNAL(finished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
qDebug()<<"pd2";
}
void MainWindow::display(int i)
{
ui->lineEdit->setText(QString::number(i));
}
void MainWindow::on_pushButton_2_clicked()
{
qDebug()<<"In push button - 2";
for(int i = 0; i < 200; i++)
{
qDebug()<<i;
ui->lineEdit_2->setText(QString::number(i));
}
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <worker.h>
#include <QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void display(int i);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
private:
Ui::MainWindow *ui;
worker* work = new worker();
QThread* thread = new QThread;
};
worker.cpp
#include "worker.h"
#include <QDebug>
worker::worker(QObject *parent) : QObject(parent)
{
}
void worker::process()
{
int index = 0;
qDebug()<<"In here";
while(true)
{
qDebug("Hello World!");
index += 1;
if(index > 10000)
{
index = 0;
}
emit datar(index);
}
emit finished();
}
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class worker : public QObject
{
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
signals:
void finished();
void datar(int);
public slots:
void process();
};
#endif // WORKER_H
What i wanted was to update the line edit continusoly from thread such that it doesn't affect the GUI performance.
It would be great if you identify the mistake and suggest me the changes to do.
Consider your worker::process implementation...
void worker::process()
{
int index = 0;
qDebug()<<"In here";
while(true)
{
qDebug("Hello World!");
index += 1;
if(index > 10000)
{
index = 0;
}
emit datar(index);
}
emit finished();
}
It emits the datar signal continuously and without any intervening delays. But the signal emitter and receiver are on different threads meaning the signal will be delivered to the receiver via its event queue. So you are basically saturating the GUI thread's event loop with events from the datar signal.
Try putting even a slight delay between signals with something like...
QThread::msleep(10);
I've successfully redirected qDebug() output to a QTextEdit widget. For several reasons, I'd like every qDebug() message to be included in an emitted signal. One reason is that the object which shall receive the output isn't available. Another reason is that I want to redirect the output to different objects depending on which activity is active at the moment (using connect/disconnect of the signal to different slots).
I've made a working example code that redirects qDebug to a QTextEdit widget. Can someone please help me to get this code to emit a signal which includes the qDebug message?
I'm not sure if its possible to have Q_DebugStream emit a signal (I've tried and failed to make a Qt Class out of it).
It must be possible to pass a pointer to a function/slot instead of a pointer to a QTextEdit when calling Q_DebugStream, but I'm not sure how this is done.
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void logSignal(QString);
public slots:
void logSlot(QString);
private:
QTextEdit *logView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "q_debugstream.h"
#include <QGridLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
setCentralWidget(mainWidget);
logView = new QTextEdit;
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(logView,0,0,1,1);
mainWidget->setLayout(mainLayout);
connect(this, SIGNAL(logSignal(QString)),
this, SLOT(logSlot(QString)));
emit logSignal("Message from a signal\n");
new Q_DebugStream(std::cout, logView); //Redirect Console output to QTextEdit
Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
qDebug() << "DEBUG MODE ACTIVE";
}
MainWindow::~MainWindow(){}
void MainWindow::logSlot(QString log) {
logView->append(log);
}
q_debugstream.h
//As per forum:
//http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html
//A couple of lines added to ensure newlines go between each call.
//Thanks, James!
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QTextEdit>
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));
log_window->append("\n");
}
}else{
log_window->moveCursor (QTextCursor::End);
log_window->insertPlainText (str);
log_window->insertPlainText ("\n");
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
QTextEdit* log_window;
};
#endif // Q_DEBUGSTREAM_H
When application is started, I get both messages in my QTextEdit:
"Message from a signal"
"DEBUG MODE ACTIVE"
(This answer extracted from an edit to the question - now rolled back).
Here's how I solved this:
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void logSignal(QString);
public slots:
void logSlot(QString);
private:
void dbgMsg(QString);
QTextEdit *logView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "q_debugstream.h"
#include <QGridLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *mainWidget = new QWidget(this);
setCentralWidget(mainWidget);
logView = new QTextEdit;
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(logView,0,0,1,1);
mainWidget->setLayout(mainLayout);
connect(this, SIGNAL(logSignal(QString)),
this, SLOT(logSlot(QString)));
emit logSignal("Now call Q_DebugStream");
//Redirect qDebug() output to dbgMsg(QString)
new Q_DebugStream(std::cout, this, &MainWindow::dbgMsg);
Q_DebugStream::registerQDebugMessageHandler();
qDebug() << "Debug message";
qWarning() << "Warning!";
qCritical() << "Critical issue!";
qInfo() << "Information";
qDebug() << "This\nis\na\nlong\none.";
}
MainWindow::~MainWindow(){}
void MainWindow::logSlot(QString log) {
logView->append(log);
}
void MainWindow::dbgMsg(QString log) {
emit logSignal(log);
}
q_debugstream.h
#ifndef Q_DEBUGSTREAM_H
#define Q_DEBUGSTREAM_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QString>
#include "mainwindow.h"
class Q_DebugStream : public std::basic_streambuf<char> {
public:
Q_DebugStream(std::ostream &stream, MainWindow* obj, void (MainWindow::*dbgMsgPtr)(QString log)): m_stream(stream) {
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
msgObj = obj;
msgHandler = dbgMsgPtr;
}
~Q_DebugStream() {
m_stream.rdbuf(m_old_buf);
}
static void registerQDebugMessageHandler() {
qInstallMessageHandler(myQDebugMessageHandler);
}
private:
static void myQDebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QString message = msg;
switch (type) {
case QtDebugMsg:
message.prepend("qDbg(): ");
break;
case QtWarningMsg:
message.prepend("qWarn(): ");
break;
case QtCriticalMsg:
message.prepend("qCrit(): ");
break;
case QtInfoMsg:
message.prepend("qInfo(): ");
break;
case QtFatalMsg:
message.prepend("qFatal(): ");
abort();
break;
}
message.append(" (" + QString::fromUtf8(context.file) + ")");
message.append(" line: " + QString::number(context.line));
std::cout << message.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') {
(msgObj->*msgHandler)("\n");
}
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n) {
QString str(p);
if(str.contains("\n")) {
QStringList strSplitted = str.split("\n");
(msgObj->*msgHandler)(strSplitted.at(0)); //Index 0 is still on the same old line
for(int i = 1; i < strSplitted.size(); i++) {
(msgObj->*msgHandler)("\\ " + strSplitted.at(i));
}
} else {
(msgObj->*msgHandler)(str);
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
MainWindow* msgObj;
void (MainWindow::*msgHandler)(QString);
};
#endif // Q_DEBUGSTREAM_H
When application is started, I get these messages in my QTextEdit:
Now call Q_DebugStream
qDbg(): Debug message (..\qDebugFetch\mainwindow.cpp) line: 25
qWarn(): Warning! (..\qDebugFetch\mainwindow.cpp) line: 26
qCrit(): Critical issue! (..\qDebugFetch\mainwindow.cpp) line: 27
qInfo(): Information (..\qDebugFetch\mainwindow.cpp) line: 28
qDbg(): This
\ is
\ a
\ long
\ one. (..\qDebugFetch\mainwindow.cpp) line: 29
The goal is to display all the application output shown in QtCreator to a QTextEdit as a debug console window so with the same application only those who have password are allowed to see the console window while normal users cannot. There is an exe with several dlls. All std::cout from DLLs and qDebug are needed to be shown in the debug console window.
To achieve this, I have followed
http://www.qtforum.org/article/39768/redirecting-std-cout-std-cerf-qdebug-to-qtextedit.html
The code works great for single thread but is hanged when a thread is started to call functions in DLL. I would like to know how to fix the problem.
code
In mainwindow.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;
}
In mainwindow.h
#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
in 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
In DLL,
int run()
{
std::cout << "Hello World" << std::endl;
return 0;
}
The code sample is uploaded to github for reference. Build ToyProj1 and ToyProj1GUI when repeat the problem.
https://github.com/kuwt/ToyProject.git
With the help comments in question, this solution works well with signal and slot mechanism. std::cout and qDebug are redirected to QTextEdit.
main.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();
}
mainwindow.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
mainwindow.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
for long transactions, you can add:
QCoreApplication::processEvents();
in the catchMessage(QString(msg) method of the class MessageHandler in q_debugstream.h, after the append() call.
This updates textEdit 'as soon as possible'.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a problem with accessing progressBar from another class.
Here's the method that does not work.
I have progress bar in UI(mainwindow.ui) with name progressBar.
void QtDownload::downloadProgress(qint64 recieved, qint64 total) {
ui->progressBar->setMaximum(total);
ui->progressBar->setValue(recieved);
}
Here are all parts of the code:
downloader.pro
#-------------------------------------------------
#
# Project created by QtCreator 2017-01-12T15:22:17
#
#-------------------------------------------------
QT += core gui
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = downloader
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
FORMS += mainwindow.ui
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QObject>
#include <QString>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
class QtDownload : public QObject {
Q_OBJECT
public:
explicit QtDownload();
QNetworkAccessManager manager;
QString target;
void setTarget(const QString& t);
private:
signals:
void done();
public slots:
void download();
void downloadFinished(QNetworkReply* data);
void downloadProgress(qint64 recieved, qint64 total);
};
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QtCore>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QUrl>
#include <QtNetwork/QNetworkRequest>
#include <QFile>
#include <QDebug>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkReply>
#include <QByteArray>
#include <QObject>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// QCoreApplication app(argc, argv);
QtDownload *dl = new QtDownload();
dl->setTarget( "http://media09.vbox7.com/s/21/21bbc2dca3r3634e3389.mp4");
dl->download();
//quit when the download is done.
// QObject::connect(&dl, SIGNAL(done()), &app, SLOT(quit()));
}
MainWindow::~MainWindow()
{
delete ui;
}
QtDownload::QtDownload() : QObject(0) {
QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(downloadFinished(QNetworkReply*)));
}
void QtDownload::setTarget(const QString &t) {
this->target = t;
}
void QtDownload::downloadFinished(QNetworkReply *data) {
QFile localFile("C:/downloadedfile.mp4");
if (!localFile.open(QIODevice::WriteOnly))
return;
const QByteArray sdata = data->readAll();
localFile.write(sdata);
qDebug() << sdata;
localFile.close();
emit done();
}
void QtDownload::download() {
QUrl url = QUrl::fromEncoded(this->target.toLocal8Bit());
QNetworkRequest request(url);
QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
}
void QtDownload::downloadProgress(qint64 recieved, qint64 total) {
ui->progressBar->setMaximum(total); // DOES NOT WORK
ui->MainWindow.progressBar->setValue(recieved); // DOES NOT WORK
}
That's because your slot shouldn't be inside the QtDownload, it should be inside the MainWindow.
You should pass the MainWindow inside its constructor (just as a parent widget), and reuse this MainWindow in your connect, instead of uthing this.
Edits in your QtDownload :
Header
QtDownload(QObject* parent = 0); //Don't forget to make it in the source file
signals:
void done();
void downloadProgress(qint64 recieved, qint64 total);
public slots:
void download();
void downloadFinished(QNetworkReply* data);
private:
MainWindow* parent; //Set it in the new constructor
Source
QtDownload::QtDownload(QObject *parent) : QObject(parent)
{
//The code that was initially inside QtDownload::QtDownload()
}
void QtDownload::download() {
QUrl url = QUrl::fromEncoded(this->target.toLocal8Bit());
QNetworkRequest request(url);
QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), parent, SLOT(downloadProgress(qint64,qint64)));
}
Edits in your MainWindow
Header
public slots:
void downloadProgress(qint64 recieved, qint64 total);
Source
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// QCoreApplication app(argc, argv); (Remove this line, it should be in the main.cpp
QtDownload *dl = new QtDownload(this); //Pass this as parameter
dl->setTarget( "http://media09.vbox7.com/s/21/21bbc2dca3r3634e3389.mp4");
dl->download();
//quit when the download is done.
// QObject::connect(&dl, SIGNAL(done()), &app, SLOT(quit()));
}
void MainWindow::downloadProgress(qint64 recieved, qint64 total) {
ui->progressBar->setMaximum(total);
ui->progressBar->setValue(recieved);
}