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.
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.
Non-static class method and a global function have different signatures. You cannot use a non-static method as a function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With