Reputation: 6475
I want to use qInstallMessageHandler(handler)
to redirect the qDebug
to QTextEdit
.
I define a handler function in a class:
void Spider::redirect(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
console->append(msg);
}
and call qInstallMessageHandler(redirect)
in the constructor of the class (Spider).
But, when i compile this program, i got a error:
cannot convert 'Spider::redirect' from type 'void (Spider::)(QtMsgType, const QMessageLogContext&, const QString&)' to type 'QtMessageHandler {aka void (*)(QtMsgType, const QMessageLogContext&, const QString&)}'
If i define the handler function in global, it's ok.
I can't figure out the difference between these two behaviors.
Upvotes: 5
Views: 6118
Reputation: 1
Addition to phyatts answer. To make the thing thread-safe you can also create something of the following. That will resolve the issue for any thread of that application that may want to send its debug stream.
namespace Mainwindow_ {
class Emitter : public QObject
{
Q_OBJECT
public:
Emitter(){};
~Emitter(){};
signals:
void append_log(QString msg);
};
}
Similar to the s_textEdit
create a static object of the above class.
In the header:
static QTextEdit * s_text_edit;
static Mainwindow_::Emitter s_emitter;
In the .cpp:
QTextEdit * MainWindow::s_text_edit = 0;
Mainwindow_::Emitter MainWindow::s_emitter;
in its constructor:
s_text_edit = new QTextEdit("Starting .. ");
connect(&s_emitter, SIGNAL(append_log(QString)), s_text_edit, SLOT(append(QString)));
You can call that append_log
from inside the myMessageOutput
with:
emit MainWindow::s_emitter.append_log(msg);
Please excuse the naming differences. I tested this in Qt version 5.7 and 5.9. If there is any caveats please let me know.
Upvotes: 0
Reputation: 19112
I really like having this debugging ability around. I've done it a few times on the last couple projects I've worked on. Here are the relevant code snippets.
in mainwindow.h, inside your MainWindow
class, under public
static QTextEdit * s_textEdit;
in mainwindow.cpp, outside of any function
QTextEdit * MainWindow::s_textEdit = 0;
in MainWindow constructor
s_textEdit = new QTextEdit;
// be sure to add the text edit into the GUI somewhere,
// like in a layout or on a tab widget, or in a dock widget
in main.cpp, above the main()
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if(MainWindow::s_textEdit == 0)
{
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
abort();
}
}
else
{
switch (type) {
case QtDebugMsg:
case QtWarningMsg:
case QtCriticalMsg:
// redundant check, could be removed, or the
// upper if statement could be removed
if(MainWindow::s_textEdit != 0)
MainWindow::s_textEdit->append(msg);
break;
case QtFatalMsg:
abort();
}
}
}
inside main() in main.cpp, before initializing QApplication
instance.
qInstallMessageHandler(myMessageOutput);
Note: This works wonderfully for any single threaded application. Once you start using qDebug()
outside your GUI thread, you will crash. You then need to create a QueuedConnection
from any threaded function (anything not running on your GUI thread), to connect to your instance of MainWindow::s_textEdit
, like so:
QObject::connect(otherThread, SIGNAL(debug(QString)),
s_textEdit, SLOT(append(QString)), Qt::QueuedConnection);
If you end up using QDockWidget
s and making use of the QMenu
, there are some additional cool things that you can do. The end result it a very user-friendly, easy-to-manage console window.
QMenu * menu;
menu = this->menuBar()->addMenu("About");
menu->setObjectName(menu->title());
// later on...
QDockWidget *dock;
dock = new QDockWidget("Console", this);
dock->setObjectName(dock->windowTitle());
dock->setWidget(s_textEdit);
s_textEdit->setReadOnly(true);
this->addDockWidget(Qt::RightDockWidgetArea, dock);
this->findChild<QMenu*>("About")->addAction(dock->toggleViewAction());
Hope that helps.
Upvotes: 12
Reputation: 648
Non-static class method and a global function have different signatures. You cannot use a non-static method as a function.
Upvotes: 1