redirect qDebug to QTextEdit

2020-06-04 09:41发布

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.

标签: c++ qt
2条回答
三岁会撩人
2楼-- · 2020-06-04 10:08

Non-static class method and a global function have different signatures. You cannot use a non-static method as a function.

查看更多
戒情不戒烟
3楼-- · 2020-06-04 10:26

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 QDockWidgets 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.

查看更多
登录 后发表回答