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

本案例對應的源代碼目錄: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實戰指南》目錄


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章