baizy77的个人主页

http://www.qtcn.org/bbs/u/88608  [收藏] [复制]

baizy77

小白

  • 2

    关注

  • 8

    粉丝

  • 1

    访客

  • 等级:新手上路
  • 总积分:0
  • 男,1977-11-27

最后登录:2021-01-06

更多资料

日志

4.4 案例8 用qDebug()输出信息

2020-12-24 14:54


本案例对应的源代码目录:src/chapter04/ks04_04。
在开发C/S(Client/Server,客户端/服务端)模式的软件时,服务端程序(有时也称作服务)经常运行在两种模式下。
(1)终端模式。
终端模式,也可称作命令行模式。在这种模式下,服务端程序占用终端(命令行)运行,用户既可以看到服务端程序向终端输出的信息,也可以在终端输入命令以调整程序的行为。
(2)后台模式。
后台模式就是Windows的服务模式(在Linux、Unix下也有服务模式)。在这种模式下,服务端程序以后台服务方式运行,而且没有任何界面。用户无法通过终端查看模块状态或者输入命令,因为根本就没有终端。当软件运行在这种模式的时候,维护人员给服务器加电后就可以不管了,服务器加电启动后进入操作系统并且自动启动预先配置好的服务。因为这种模式几乎无须人员干预,所以对用户来说非常方便。
通常可以在软件中通过命令行参数的方式区分这两种模式。如果软件运行在终端模式,可以将输出信息发送到标准输出(也就是命令行);如果软件运行在后台模式,可以将输出信息保存到文件。那么该怎么实现这样的信息输出功能呢?
Qt提供了qDebug()来实现输出功能。下面分4种场景介绍qDebug()相关的功能。
(1)用qDebug()<<方式输出信息。
(2)使用qDebug(“%”)格式化输出信息。
(3)将自定义类输出到qDebug()。
(4)将标准输出重定向到文件。
下面进行详细介绍。
1.用qDebug()<< 方式输出信息
最简单的方法是直接向终端输出信息,方法是使用<<操作符实现信息输出,见代码清单4-22。
代码清单4-22

#include <QDebug>
void example01() {                      
    int iVal = 334;
    QString str = "I live in China";
    qDebug() << "My Value is " << iVal << ". " << str;
    qWarning() << "My Value is " << iVal << ". " << str;
    qCritical() << "My Value is " << iVal << ". " << str;
}

从代码清单4-22可以看出,使用<<操作符将变量输出到qDebug()的方法跟STL的cout类似,即把变量左移到qDebug()即可。Qt的常用类都可以输出到qDebug(),原生数据类型也是。qWarning()、qCritical()的用法与qDebug()相同,只是严重等级不同。使用时需要包含<QDebug>文件。
2.使用qDebug(“%”)格式化输出信息
为了便于信息的阅读,实际工作中运行的软件一般都采用格式化的方式输出信息,见代码清单4-23。
代码清单4-23

void example02(){                        
    QString str = "China";
    QDateTime dt = QDateTime::fromTime_t(time(NULL));
   qDebug("I live in %s. Today is %04d-%02d-%02d",str.toLocal8Bit().data(), dt.date().year(), dt.date().month(),dt.date().day());
    qWarning("I live in %s. Today is %04d-%02d-%02d", str.toLocal8Bit().data(),
    dt.date().year(), dt.date().month(), dt.date().day());
    qCritical("I live in %s. Today is %04d-%02d-%02d",
str.toLocal8Bit().data(), dt.date().year(), dt.date().month(), dt.date().day());
    // 下面几行代码如果解封,其功能是弹出异常界面,并显示给出的异常信息。
    // qFatal("I live in %s. Today is %04d-%02d-%02d",                        
    //         str.toLocal8Bit().data(),
    //         dt.date().year(), dt.date().month(), dt.date().day());
}

在代码清单4-23中,使用类似sprintf()的方式实现信息的格式化输出。代码中用%语法将信息格式化。qWarning()、qCritical()、qFatal()的用法与之相同。标号①处封掉的代码中,qFatal()正常运行的效果是弹出异常界面。
3.将自定义类输出到qDebug()
除了Qt自带的类之外,还可以将项目中的自定义类输出到qDebug(),如代码清单4-24所示。
代码清单4-24

// myclass.h
#pragma once
#include <QDebug>
#include <QString>
class CMyClass {
    ...
};
QDebug operator<<(QDebug debug, const CMyClass &mc);                                

代码清单4-24提供了自定义类CMyClass的头文件。为了将自定义类输出到qDebug,在标号①处为CMyClass编写左移操作符的重载接口。该接口的实现见代码清单4-25。在代码清单4-25中的重载接口内部,根据实际需要将CMyClass类对象mc的数据输出到debug对象。
代码清单4-25

// myclass.cpp
#include "myclass.h"
QDebug operator<<(QDebug debug, const CMyClass &mc) {
    debug << "My id is " << mc.getId() << ", My Name is " << mc.getName() << "";
    return debug;
}

完成CMyClass类向qDebug()的左移操作符的重载操作后,就可以在代码中使用它了,见代码清单4-26中标号①处。
代码清单4-26

void example03(){
    CMyClass mc;
    mc.setId(10000);
    mc.setName(QString::fromLocal8Bit("秦始皇"));
    qDebug() << mc;                                                              
}

4.将标准输出重定向到文件
除了将信息输出到终端,还可以通过重定向的方式将信息输出到文件。当软件模块以服务模式运行在后台时,如果能把调试信息输出到文件中,就可以方便地监视软件运行状态。这将用到Qt的重定向输出接口的注册函数qInstallMessageHandler()。该函数的原型为:

Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

从qInstallMessageHandler()的定义可以看出,需要给它传入一个QtMessageHandler类型的新的重定向输出函数地址,然后它返回前一个(旧的)QtMessageHandler函数地址。QtMessageHandler定义如下。

typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);

为了使用qInstallMessageHandler(),开发者需要定义自己的重定向接口customMessageHandler(),如代码清单4-27所示。
代码清单4-27

QMutex g_mutex; // 为了支持多线程功能,需要使用锁来保护对日志文件的操作。           ①
QtMessageHandler g_systemDefaultMessageHandler = NULL; // 用来保存系统默认的输出接口 ②
void customMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& info) {
    // 把信息格式化
    QString log =
   QString::fromLocal8Bit("msg-[%1], file-[%2], func-[%3],cate-[%4]\r\n").arg(info).arg(context.file).arg(context.function).arg(context.category);          
    bool bok = true;
    switch (type) {
    case QtDebugMsg:
        log.prepend("Qt dbg:");
        break;
    case QtWarningMsg:
        log.prepend("Qt warn:");
        break;
    case QtCriticalMsg:
        log.prepend("Qt critical:");
        break;
    case QtFatalMsg:
        log.prepend("Qt fatal:");
        break;
    case QtInfoMsg:
        log.prepend("Qt info:");
        break;
    default:
        bok = false;
        break;
    }
    if (bok) {
        // 加锁
        QMutexLocker locker(&g_mutex);                                        
        QString strFileName = getPath("$TRAINDEVHOME/bin/log04_04.inf");
        QFile file(strFileName);
        if (!file.open(QFile::ReadWrite | QFile::Append)) {
            return;
        }
        file.write(log.toLocal8Bit().data());
        file.close();
    }
    if (bok) {
        // 调用系统原来的函数完成信息输出,比如输出到调试窗口
        if(NULL != g_systemDefaultMessageHandler)    {                        
            g_systemDefaultMessageHandler(type, context, log);
        }
    }
}
// main.cpp
int main(int argc, char * argv[]) {
    QApplication app(argc, argv);
    // 输出重定向
    g_systemDefaultMessageHandler = qInstallMessageHandler(customMessageHandler);  
    ...
}

代码清单4-27中定义的customMessageHandler()接口提供3个参数。参数type用来区分报警等级,其取值见表4-2。参数context用来指示上下文,比如输出信息时所在文件、行号、所在函数等。参数info用来描述需要输出的信息内容。在代码清单4-27中的customMessageHandler()接口中,根据type的不同,对info进行了重新组织并将格式化后的信息存放到log中,最后将log写入日志文件。为了防止多线程对同一个日志文件的操作,在标号①处定义一个互斥对象g_mutex,并在标号③处通过QMutexLocker自动锁来操作g_mutex,以便对日志文件的操作进行互斥。QMutexLocker实现的功能是在构造QMutexLocker对象时可以对传入的g_mutex进行加锁处理,并在析构时对g_mutex进行解锁处理,这样就实现了加锁解锁的自动化操作,开发者无须关注加锁、解锁操作。为了调用系统原来的信息输出功能(比如将信息输出到调试窗口),可以先定义变量用来保存旧的信息输出接口,见标号②处、标号⑤处代码,然后在标号④处调用旧的信息输出接口将信息输出到调试窗口。在标号⑤处,main()函数中调用qInstallMessageHandler()来注册自定义的重定向输出接口customMessageHandler,这样当后续代码中调用qDebug()、qWarning()、qCritical()、qFatal()时程序就会自动调用自定义的customMessageHandler()接口来输出信息。请注意,在Release版本中有可能出现参数context对象中的文件信息和行数为空,原因是Qt在Release版本默认丢弃了文件信息、行数等信息。解决方案是在项目的pro文件中定义一个宏:

// ks04_04.pro
DEFINES += QT_MESSAGELOGCONTEXT

表4-2 QtMsgType取值

取值

说明

取值

说明

QtDebugMsg

调试类信息

QtFatalMsg

致命错误信息

QtWarningMsg

一般的警告信息

QtInfoMsg

一般的信息提示

QtCriticalMsg

严重错误信息

QtSystemMsg = QtCriticalMsg

系统信息  

----------------------------------------------------------------------------------------------------------------------------------------------
《Qt 5/PyQt 5实战指南》目录
分类:Qr入门与提高|回复:0|浏览:394|全站可见|转载
 

Powered by phpwind v8.7 Certificate Copyright Time now is:05-02 07:27
©2005-2016 QTCN开发网 版权所有 Gzip disabled