小言_互联网的博客

Qt 中捕获三方库&自身标准打印方法

522人阅读  评论(0)

【写在前面】

        很多时候,我们为了方便调试,常常需要加入一些打印。

        例如 Qt 中的 QDebug,C 和 C++ 中的 printf / cout 等等,又或者是三方库提供的标准打印接口。

        然而大部分时候,这些打印相当不统一(格式和位置),并且因为 Qt 作为 GUI 框架,调试信息实在不应该直接置于 UI 之上。

        因此,需要一种能统一和标准化所有标准打印的方法( 所谓标准打印即标准输出 stdout 等),并且能够动态配置。


 【正文开始】

  • 对于 Qt 自身的打印,捕获起来相当容易:

        我们使用 qInstallMessageHandler() 安装一个消息处理器,它指向一个函数,其函数签名如下第一行所示:


  
  1. typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
  2. Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

         该函数能够捕获由 Qt Debug 产生的各种类型的打印消息,然后可以在此函数集中处理。

  • 对于三方库,只要他是标准输出,我们就可以使用一些技巧来捕获它:

        这里我们需要借助一个C库函数 freopen(),其声明如下:

FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream);

        该函数用于重定向输入输出流。它可以在不改变代码原貌的情况下改变输入输出环境,但使用时应当保证流是可靠的

        有了这些函数,接下来我们整合一下,来实现一个完整的,能够处理所有情况的函数:


  
  1. static void initializeDebugEnveriment()
  2. {
  3. static bool initialized = false;
  4. static QTextEdit *edit = new QTextEdit;
  5. static int lineCount = 0;
  6. static const QString stdoutFileDir = qApp-> applicationDirPath() + "/cache";
  7. static QTimer *watcher = new QTimer(qApp);
  8. static quint64 fileSize = 0;
  9. static QFile watchedStdoutFile(stdoutFileDir + "/stdout");
  10. if (!initialized) {
  11. qRegisterMetaType<QTextCursor>( "QTextCursor");
  12. auto palette = edit-> palette();
  13. palette. setBrush(QPalette::Highlight, QColor( 0, 120, 215));
  14. edit-> setPalette(palette);
  15. edit-> setReadOnly( true);
  16. edit-> setWindowTitle( QStringLiteral( "调试窗口"));
  17. edit-> setWindowFlag(Qt::WindowStaysOnTopHint);
  18. edit-> resize( 700, 500);
  19. edit-> show();
  20. if (! QDir(). exists(stdoutFileDir)) QDir(). mkpath(stdoutFileDir);
  21. std:: freopen((stdoutFileDir + "/stdout"). toLocal8Bit(). data(), "w", stdout);
  22. watchedStdoutFile. open(QIODevice::ReadOnly);
  23. watcher-> start( 100);
  24. QObject:: connect(watcher, &QTimer::timeout, watcher, []{
  25. if (watchedStdoutFile. size() != fileSize) {
  26. fileSize = watchedStdoutFile. size();
  27. auto watchedMsg = QString:: fromLocal8Bit(watchedStdoutFile. readAll());
  28. if (!watchedMsg. isEmpty()) {
  29. auto list = watchedMsg. split( '\n');
  30. for ( auto msg: qAsConst(list)) {
  31. msg = msg. trimmed();
  32. auto time = QDateTime:: currentDateTime(). toString( "[yyyy-MM-dd-hh:mm:ss:zzz] ");
  33. if (!msg. isEmpty()) msg = time + msg;
  34. edit-> append(msg);
  35. if (!edit-> textCursor(). hasSelection()) edit-> moveCursor(QTextCursor::End);
  36. if (++lineCount > 50000) {
  37. lineCount = 0;
  38. edit-> clear();
  39. }
  40. }
  41. }
  42. }
  43. });
  44. initialized = true;
  45. }
  46. static auto myMsgHandler = [](QtMsgType, const QMessageLogContext &, const QString &msg) -> void {
  47. auto time = QDateTime:: currentDateTime(). toString( "[yyyy-MM-dd-hh:mm:ss:zzz] ");
  48. edit-> append(time + msg);
  49. if (!edit-> textCursor(). hasSelection()) edit-> moveCursor(QTextCursor::End);
  50. if (++lineCount > 50000) {
  51. lineCount = 0;
  52. edit-> clear();
  53. }
  54. };
  55. qInstallMessageHandler(myMsgHandler);
  56. }

        这里我用一个 QTextEdit 来显示所有捕获的打印,但这不是必要的,可以根据自己需求来改造。

        首先,因为要将标准输出重定向到文件,所以我们要创建一个文件夹用于缓存其输出。

        接着,将 stdout 重定向至一个文件中( 实际上可以是任意输出流 ),然后创建一个定时器用于监视其文件大小变化

        如果监视文件大小改变,则将内容读入( 即打印内容 ),并添加到 textEdit 中显示出来,此时就完成了标准打印的捕获。

        并且,这种方法能够捕获三方库的内部打印消息。

        最后,我添加了一段模拟代码来测试一下:


  
  1. int main(int argc, char *argv[])
  2. {
  3. QApplication app(argc, argv);
  4. initializeDebugEnveriment();
  5. QTimer timer;
  6. QObject:: connect(&timer, &QTimer::timeout, &timer, []{
  7. static int count = 1;
  8. qDebug() << "This is Qt Debug message! Count:" << count++;
  9. });
  10. timer. start( 1000);
  11. QTimer otherTimer;
  12. QObject:: connect(&otherTimer, &QTimer::timeout, &otherTimer, []{
  13. static int count = 1;
  14. printf( "This is printf stdout message! Count: %d", count++);
  15. fflush(stdout);
  16. });
  17. otherTimer. start( 1000);
  18. return app. exec();
  19. }

        效果图如下:


 【结语】

        现在有了这个函数,我们还可以动态控制打印窗口,即控制 textEdit 是否显示。

        另外提一点,如果嫌弃麻烦,可以直接 CONFIG += console,直接使用控制台打印。当然这个方法的缺点则是不能运行时配置,并且启动时会有一个控制台窗口。

        最后,源码地址:

        Github的:GitHub - mengps/QtExamples: 分享各种 Qt 示例,,说不定用得上呢~分享各种 Qt 示例,,说不定用得上呢~. Contribute to mengps/QtExamples development by creating an account on GitHub.https://github.com/mengps/QtExamples        CSDN的:

https://download.csdn.net/download/u011283226/87097616https://download.csdn.net/download/u011283226/87097616


转载:https://blog.csdn.net/u011283226/article/details/127950587
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场