Qt notes

為什么在頭文件中有的是使用前置聲明,而有的是包含頭文件?

如下代碼:

#include <QDialog>
class QCheckBox;
class QLabel;

class FindDialog :public QDialog
{
  Q_OBJECT
public:
  FindDialog(QWidget *parent=0);
  
private:
  QCheckBox *checkBox;
  QLabel    *label;
}
  • 前置聲明(forward declaration)會告訴C++編譯程序類的存在,而不需要提供類定義中的所有的細節(jié)(通常放在它自己的頭文件當(dāng)中)。因為我們的這些私有變量都是指針,而且沒有必要在頭文件當(dāng)中就去訪問它們,因而編譯程序就無需這些類的完整的定義。我們不使用這些類的相關(guān)的頭文件(如<QCheckBox>,<QLabel>),而是使用前置聲明,這可以使得變異的過程更快一些。
  • 在真正的實現(xiàn)文件.cpp當(dāng)中,我們可以包含#include <QtGui>,這個頭文件包含了Qt GUI類的定義。Qt由數(shù)個模塊組成,每一個模塊都有自己的類庫。最為重要的模塊有QtCore、QtGui、QtNetwork、QtOpenGL、QtScript、QtSql、QtSvg、QtXml。其中,在<QtGui>頭文件中為構(gòu)成QtCore和QtGui組成部分的所有類進行了定義。在程序中包含這個頭文件,就能夠使我們省去在每一個類中分別包含的麻煩。在頭文件中本可以直接包含<QtGui>即可,然而在一個頭文件中再包含一個那么大的頭文件實在不是一種好的編程風(fēng)格,尤其對于比較大的工程項目更是如此。

為什么在Qt中大多數(shù)時候都是只見new 不見delete?

  • Qt會在刪除父對象的時候自動的刪除其所屬的子對象,包括其所有的子窗口部件和子布局對象.

信號和槽機制

  • 信號和槽機制是Qt編程的基礎(chǔ).它可以讓應(yīng)用程序的編程人員把這些互不了解的對象綁定在一起.槽和普通的C++成員函數(shù)幾乎是一樣的----可以是虛函數(shù);可以被重載,可以是公有,保護,私有類型,并且也可以直接被其他的C++成員函數(shù)直接調(diào)用.它們的參數(shù)可以使任意的類型.唯一不同的是:槽還可以和信號連接在一起.每次發(fā)射和槽連接在一起的信號時,就會自動的觸發(fā)槽函數(shù).
  • 一個信號可以連接多個槽: connect(slider,SIGNAL(valueChanged(int)),this,SLOT(setValue(int))),connect(slider,SIGNAL(valueChanged(int)),this,SLOT(updateStatusBarIndicator(int))).在發(fā)射這個信號的時候會以不確定的順序一個接一個地調(diào)用這些槽.
  • 多個信號可以連接同一個槽函數(shù)
  • 一個信號也可以和另外的一個信號相連接:connect(slider,SIGNAL(valueChanged(int)),this,SIGNAL(updateRecord(int)));當(dāng)發(fā)射第一個信號的時候,第二個信號也會被觸發(fā).除此之外,信號和信號之間的連接和信號和槽之間的連接是難以區(qū)分的.
  • 連接可以被移除:disconnect(slider,SIGNAL(valueChanged(int)),this,SIGNAL(updateRecord(int)));這種情況比較少用,因為當(dāng)刪除掉對象時,Qt會自動的移除和這個對象相關(guān)的所有的連接.
  • 對應(yīng)的信號和槽必須要有相同順序和類型的參數(shù),但是允許槽的參數(shù)比信號的參數(shù)少,信號中多余的參數(shù)會被簡單的忽略掉.
  • Qt的信號和槽機制是在QObject中實現(xiàn)的,并不僅僅局限于圖形用戶界面編程當(dāng)中,這種機制可以使用于任何的QObject的子類當(dāng)中.

Qt的元對象系統(tǒng)

  • Qt的主要成就之一就是使用了一種機制對C++進行了擴展,并且使用了這種機制創(chuàng)建了獨立的軟件組件.這些組件可以綁定在一起,但是任何組件對于他所要連接的組件的情況事先都一無所知.這種機制稱為元對象系統(tǒng)(meta-object system),它提供了關(guān)鍵的兩項技術(shù):信號-槽以及內(nèi)省(introspection).內(nèi)省功能對于實現(xiàn)信號和槽是必須的.并且允許應(yīng)用程序的開發(fā)人員在運行的時候獲得有關(guān)QObject子類的元信息(meta-information),包括一個含有對象的類名以及他所支持的信號和槽的列表.這一機制也支持屬性(廣泛用于Qt的設(shè)計師中)和文本翻譯(用于國際化),并且它也為QtScript模塊奠定了基礎(chǔ).
  • 標準C++中并沒有對Qt的元對象系統(tǒng)所需要的動態(tài)元信息提供支持.Qt通過一個獨立的moc工具解決了這個問題,moc解析Q_OBJECT類的定義并且通過C++函數(shù)來提供可供使用的信息.由于moc使用純C++來實現(xiàn)其所有的功能,所以Qt的元對象系統(tǒng)可以在任意的C++編譯器上工作.

這一機制的工作過程是:

  1. Q_OBJECT宏聲明了在每一個QObject子類中必須要實現(xiàn)的一些內(nèi)省函數(shù):metaObject()、tr(),qt_metacall(),以及其他的一些函數(shù).
  2. Qt的moc工具生成了用于由Q_OBJECT聲明的所有函數(shù)和所有信號的實現(xiàn).
  3. 像connect()和disconnect()這樣的QObject的成員函數(shù)使用這些內(nèi)省函數(shù)來完成他們的工作.

由于所有的這些工作都是由qmake,moc和QObject自動處理的,所以很少需要再去考慮這些事情.但是如果你對此充滿好奇,那么也可以閱讀以下有關(guān)QMetaObject類的文檔和由moc生成的C++源代碼文件,可以從中看出這些實現(xiàn)工作是如何進行的.

元對象工具

  • 元對象編譯器moc(meta object compiler)對C++文件中的類聲明進行分析并產(chǎn)生用于初始化元對象的C++代碼,元對象包含全部信號和槽的名字以及指向這些函數(shù)的指針. moc讀取C++源文件,如果發(fā)現(xiàn)有Q_OBJECT宏聲明的類,他就會生成另外一個C++源文件,這個新生成的源文件中包含有該類的元對象代碼. 例如,假設(shè)我們有一個頭文件mysignal.h,在這個文件中包含有信號或槽的聲明,那么在編譯之前 moc 工具就會根據(jù)該文件自動生成一個名為mysignal.moc.h的C++源文件并將其提交給編譯器;類似地,對應(yīng)于mysignal.cpp文件moc 工具將自動生成一個名為mysignal.moc.cpp文件提交給編譯器。
  • 元對象代碼是signal/slot機制所必須的。用moc產(chǎn)生的C++源文件必須與類實現(xiàn)一起進行編譯和連接,或者用#include語句將其包含到類的源文件中。moc并不擴展#include或者#define宏定義,它只是簡單的跳過所遇到的任何預(yù)處理指令。

Qt 進程間通信

Qt的通信可分為Qt內(nèi)部通信外部通信兩大類。對于這兩類通信機制及應(yīng)用場合做如以下分析:

1. Qt內(nèi)部對象間通信

對于這種內(nèi)部對象間的通信,QT主要采用了信號和槽的機制。

  • QT信號機制是用來在對象間通訊的方法,當(dāng)一個特定事件發(fā)生的時候,signal會被 emit 出來,slot 調(diào)用是用來響應(yīng)相應(yīng)的 signal 的。簡單點說就是如何在一個類的一個函數(shù)中觸發(fā)另一個類的另一個函數(shù)調(diào)用,而且還要把相關(guān)的參數(shù)傳遞過去.好像這和回調(diào)函數(shù)也有點關(guān)系,但是消息機制可比回調(diào)函數(shù)有用多了,也復(fù)雜多了。例如,實現(xiàn)單擊按鈕終止應(yīng)用程序運行的代碼connect(button , SIGNAL(clicked()) , qApp , SLOT(quit()) );實現(xiàn)過程就是一個button被單擊后會激發(fā)clicked信號,通過connect()函數(shù)的連接qApp會接收到此信號并執(zhí)行槽函數(shù)quit()。在此過程中,信號的發(fā)出并不關(guān)心什么樣的對象來接收此信號,也不關(guān)心是否有對象來接收此信號,只要對象狀態(tài)發(fā)生改變此信號就會發(fā)出。此時槽也并不知曉有什么的信號與自己相聯(lián)系和是否有信號與自己聯(lián)系,這樣信號和槽就真正的實現(xiàn)了程序代碼的封裝,提高了代碼的可重用性。
  • 關(guān)鍵字signals指出隨后開始信號的聲明,這里signals用的是復(fù)數(shù)形式而非單數(shù),siganls沒有public、 private、protected等屬性,這點不同于slots。另外,signals、slots關(guān)鍵字是QT自己定義的,不是C++中的關(guān)鍵字。信號也可采用C++中虛函數(shù)的形式進行聲明,也可以實現(xiàn)重載.
  • 宏定義不能用在signal和slot的參數(shù)中,因為moc工具不擴展#define,因此,在signals和slots中攜帶參數(shù)的宏就不能正確地工作,如果不帶參數(shù)是可以的。
  • 函數(shù)指針不能作為信號或槽的參數(shù)。
class someClass : public QObject  
{  
    Q_OBJECT  
public slots:  
    void apply(void (*applyFunction)(QList*, void*), char*); // 不合語法  
}; 


typedef void (*ApplyFunctionType)(QList*, void*);   
class someClass : public QObject  
{  
    Q_OBJECT   
public slots:  
    void apply( ApplyFunctionType, char *);  // 合法   
}; 
  • 信號與槽也不能攜帶模板類參數(shù)。
public slots:  
    void MyWidget::setLocation (pair location); // 不和法

signals:  
    void MyObject::moved (pair location);   //不和法
    


typedef pair IntPair;  
public slots:  
    void MyWidget::setLocation (IntPair location); //合法    
signals:  
    void MyObject::moved (IntPair location); //合法

2. Qt 與外部對象間的通信

QT與外部通信主要是將外部發(fā)來的消息以事件的方式進行接收處理。外部設(shè)備將主要通過socket與QT應(yīng)用程序進行連接。在此,以輸入設(shè)備與QT應(yīng)用程序的通信為例說明QT與外部通信的原理。

  • 在QT的應(yīng)用程序開始運行時,主程序?qū)⑼ㄟ^函數(shù)調(diào)用來創(chuàng)建并啟動qwsServer服務(wù)器,然后通過socket建立該服務(wù)器與輸入硬件設(shè)備的連接。服務(wù)器啟動后將會打開鼠標與鍵盤設(shè)備,然后將打開的設(shè)備文件描述符fd連接到socket上。等到QT應(yīng)用程序進入主事件循環(huán)時,事件處理程序?qū)⑼ㄟ^Linux系統(tǒng)的select函數(shù)來檢測文件描述符fd的狀態(tài)變化情況以實現(xiàn)對socket的監(jiān)聽。如果文件描述符fd狀態(tài)改變,說明設(shè)備有數(shù)據(jù)輸入。此時,事件處理程序?qū)l(fā)出信號使設(shè)備輸入的數(shù)據(jù)能及時得到QT應(yīng)用程序的響應(yīng)。數(shù)據(jù)進入服務(wù)器內(nèi)部就會以事件的形式將數(shù)據(jù)放入事件隊列里,等待QT客戶應(yīng)用程序接收處理。處理結(jié)束后再將事件放入請求隊列里,通過服務(wù)器將事件發(fā)送到相應(yīng)硬件上,完成外部輸入設(shè)備與QT應(yīng)用程序的整個通信過程。

3. Qt 與其外部進程通信

QT可以通過QProcess類實現(xiàn)前端程序?qū)ν獠繎?yīng)用程序的調(diào)用。這個過程的實現(xiàn)首先是將前端運行的程序看成是QT的主進程,然后再通過創(chuàng)建主進程的子進程來調(diào)用外部的應(yīng)用程序。這樣QProcess的通信機制就抽象為父子進程之間的通信機制。QProcess在實現(xiàn)父子進程間的通信過程中是運用Linux系統(tǒng)的無名管道來實現(xiàn)的.

4. Qt 內(nèi)部進行間通信

  • QCOP協(xié)議
  • 信號-槽(Signal-Slot)機制
  • FIFO機制

4. 其他通信方式

般操作系統(tǒng)中常用的進程間通信機制也都可以用于QT系統(tǒng)內(nèi)部不同進程之間的通信,如消息隊列、共享內(nèi)存、信號量、有名管道等機制。其中信號量機制在QT中已經(jīng)重新進行了封裝;有些機制則可以直接通過操作系統(tǒng)的系統(tǒng)調(diào)用來實現(xiàn)。另外,如果我們只是想通過管道或socket來實現(xiàn)較簡單的外部通信,也可以重新創(chuàng)建管道或socket來實現(xiàn)自己要求的功能。


Qt 的事件處理機制

  • 產(chǎn)生事件: 輸入設(shè)備,鍵盤鼠標等。keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他們被封裝成QMouseEvent和QKeyEvent),這些事件來自底層的操作系統(tǒng),他們以異步的形式通知Qt事件處理系統(tǒng)。Qt也會產(chǎn)生很多事件,如QObject::startTimer()會觸發(fā)QTimerEvent。用戶的程序還能夠自定義事件。
  • 事件的接收和處理者:QObject類是整個的Qt對象模型的核心,事件處理機制是QObject三大職責(zé)(內(nèi)存管理、內(nèi)省(intropection)與事件處理機制)之一。任何一個想要接收并處理事件的對象必須要繼承自QObject類,可以選擇重載QObject::event()函數(shù)或者是事件的處理權(quán)轉(zhuǎn)交給父類。
  • 事件的派送者:對于non-GUI的Qt程序,由QCoreApplication負責(zé)將QEvent分發(fā)給QObject的子類-Receiver;對于GUI程序,則由QApplication負責(zé)派送。
Qt 事件過濾器

Qt事件模型真正強大的特色是一個QObject的實例能夠管理另外一個QObject實例的事件.

假設(shè)已有一個CustomerInfoDialog的小部件,其包含有一系列的QLineEdit,現(xiàn)在,我們想要使用空格來代替Tab,使得焦點在這些QLineEdit之間進行切換.

  • 方法一: 子類化QLineEdit,重新實現(xiàn)keyPressEvent(),并在keyPressEvent()里面調(diào)用focusNextChild().如下:
void MyLineEdit::keyPressEvent(QKeyEvent *event)   
{   
     if (event->key() == Qt::Key_Space) {   
         focusNextChild();   
     } else {   
         QLineEdit::keyPressEvent(event);   
     }   
} 

但是這樣做的缺點也是很明顯的,若有很多不同的空間(如QComboBox,QEdit,QSpinBox等),我們就必須要子類化這么多控件,這是一個繁瑣的任務(wù).

  • 方法二: 安裝事件過濾器,讓CustomerInfoDialog去管理他的子部件的按鍵事件.通常在構(gòu)造函數(shù)中安裝事件管理器.如下:
CustomerInfoDialog::CustomerInfoDialog(QWidget *parent)     
    : QDialog(parent)
{     
//  ...      
     firstNameEdit->installEventFilter(this);  
     lastNameEdit->installEventFilter(this);  
     cityEdit->installEventFilter(this);  
     phoneNumberEdit->installEventFilter(this);  
} 

bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event)   
{   
     if (target == firstNameEdit || target == lastNameEdit   
             || target == cityEdit || target == phoneNumberEdit) {   
         if (event->type() == QEvent::KeyPress) {   
             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);   
             if (keyEvent->key() == Qt::Key_Space) {   
                 focusNextChild();   
                 return true;   
             }   
         }   
     }   
     return QDialog::eventFilter(target, event);   
}

Qt 提供五個級別的事件處理和過濾

  1. 重新實現(xiàn)事件.如:mousePressEvent(),keyPressEvent(),paintEvent()...這是最常規(guī)的事件處理方法.
  2. 重新實現(xiàn)QObject::event().這一般用在Qt沒有提供該事件的處理函數(shù)時.也就是我們自己增加新的事件.
  3. 安裝事件過濾器
  4. 在QApplication上安裝事件過濾器.QApplication上的事件過濾器將會捕獲應(yīng)用程序的所有的事件,而且第一個獲得該事件.也就是說事件在發(fā)送給其他的任何一個event filter之前發(fā)送給QApplication的event filter.
  5. 重新實現(xiàn)QApplication的notify()方法.Qt使用notify()來分發(fā)事件.要想在任何的事件處理器捕獲事件之前捕獲事件,唯一的方法就是重新實現(xiàn)QApplication的notify()方法.

例如:實現(xiàn)一個模擬時鐘小部件,主要就是使用paintEvent事件.

/********************widget.h********************/
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void paintEvent(QPaintEvent *e);
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
/********************widget.cpp*************************/
#include "widget.h"
#include "ui_widget.h"
#include <QtGui>
#include <QTime>
#include <QDebug>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //一秒更新一次,repaint
    QTimer *timer = new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(repaint()));
    timer->start(1000);
    this->setWindowTitle(tr("Clock"));
    this->resize(400,400);
}
Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *e)
{
//時分秒指針顏色
    QColor hourColor(127,0,127);
    QColor minuteColor(0,127,127,191);
    QColor secColor(Qt::black);
    int side = qMin(this->width(), this->height());
    QTime time = QTime::currentTime();
//正式開始繪畫
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);//無鋸齒
    painter.translate(this->width()/2, this->height()/2);//將坐標系原點移動到正中心
    painter.scale(side/200.0, side/200.0);//
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(hourColor));//繪制特殊圖形,用brush
    painter.save();//保存當(dāng)前painter狀態(tài)
    painter.rotate(30.0 * (time.hour() + time.minute()/60.0));//坐標系旋轉(zhuǎn),根據(jù)真實時間確定旋轉(zhuǎn)角度
    static const QPoint hourHand[3] = {
        QPoint(7,8),
        QPoint(-7,8),
        QPoint(0,-40)
    };//
    painter.drawConvexPolygon(hourHand, 3);  //畫不規(guī)則多邊形
    painter.restore();//恢復(fù)save前原來的坐標系
    painter.setPen(hourColor);//畫刻度線,用Pen,不用Brush
    for(int i=0; i<12; i++){
        painter.drawLine(88,0,96,0);//12小時對應(yīng)的刻度
        painter.rotate(30.0);
    }
//畫1-12數(shù)字,根據(jù)數(shù)學(xué)半徑sin,cos計算所畫text的位置
    int textR = 80;     //big ridaus
    int textW = 12;   //width = height
    int textH = 8;
    const double pi = 3.1415926;
    for(int i=0; i<12; i++){
        double angle = 30.0 * i * pi/ 180;
        int x = textR * cos(angle) - textW/2;
        int y = textR * sin(angle) - textH/2;
        //qDebug()<<i<<angle<<x<<y;
        painter.drawText(QRect(x, y, textW, textH), Qt::AlignCenter,
                         QString("%1").arg((i+3)>12?(i+3-12):(i+3)));
    }
//畫分,同上
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(minuteColor));
    painter.save();
    painter.rotate(6.0 * (time.minute() + time.second()/60.0));
    static const QPoint minuteHand[3] = {
        QPoint(7,8),
        QPoint(-7,8),
        QPoint(0,-70)
    };
    painter.drawConvexPolygon(minuteHand, 3);
    painter.restore();
    painter.setPen(minuteColor);
    for (int j=0; j<60; j++){
        if ((j%5) != 0){
            painter.drawLine(92, 0, 96, 0);
        }
        painter.rotate(6.0);
    }
//畫秒,同上
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(secColor));
    painter.save();
    painter.rotate(time.second() * 6.0);
    static const QPoint secHand[3] = {
        QPoint(3,4),
        QPoint(-3,4),
        QPoint(0,-85)
    };
    painter.drawConvexPolygon(secHand, 3);
    painter.restore();
}


Qt 中的窗口刷新事件

  • 在窗體刷新事件當(dāng)中,主要說明一下paintEvent的使用:void QWidget::paintEvent(QPaintEvent *event). Paint 這個事件只要是窗體事件需要被重繪了就會被調(diào)用,他是由窗體事件產(chǎn)生的,但是要求程序重畫窗體部件的時候,事件循環(huán)就會從事件隊列當(dāng)中選中這個事件并把它分發(fā)到那個需要重畫的widget當(dāng)中。并不是所有的paint事件都是由窗口系統(tǒng)產(chǎn)生的,你也可以使用repaint()和update()來使用它。但是你需要知道的是,因為paintEvent()函數(shù)是protected的,你無法直接調(diào)用它。它也繞開了任何存在的事件過濾器。因為這些原因,Qt提供了一個機制,直接sending事件而不是posting。
  • void QWidget::update():用于更新窗體部件,它規(guī)劃了所要處理的繪制事件。但是可以被Qt優(yōu)化,有時update()執(zhí)行之后不一定會直接轉(zhuǎn)到paintEvent。因為Qt會把多個繪制事件自動的合并成一個來加快繪制的速度,所以推薦使用這個,而不是repaint(),幾次調(diào)用update()的結(jié)果通常僅僅是一次paintEvent()調(diào)用。利用這一點,在實現(xiàn)程序的時候,我們可以把所有的繪制窗體的那些語句、函數(shù)...都放到paintEvent中,通過各種if-else語句進行判斷來繪制,這樣對速度有很好的優(yōu)化并且可以防止閃爍。
  • 繪制事件還有一點需要注意的是:當(dāng)繪制事件發(fā)生時,更新的區(qū)域通常被擦除。如果需要在上一次繪制的基礎(chǔ)上進行繪制的話,我們的做法是:使用一個臨時變量保存著上次繪制之后的圖,然后在這個圖上進行繪制,最后再直接的顯示一下這個圖就ok了。這是個比較笨的方法,但也簡單。通過QPaintEvent::erased()可以得知這個窗口部件是否被擦除。寫完之后記得檢查一下 ,如果在設(shè)置了WRepaintNoErase窗口部件標記的時候是不會被擦除的。

Qt 多線程

  • 假如一個類的任何函數(shù)在此類的多個不同的實例上,可以被多個線程同時調(diào)用,那么這個類被稱為是可重入的,假如不同的線程作用在同一個實例上,而其仍然能夠正常的工作,那么稱之為線程安全的.大多數(shù)的C++類天生就是可重入的,因為它們典型的只是引用成員數(shù)據(jù),任何線程可以在類的一個實例上調(diào)用這樣的成員函數(shù),只要沒有別的線程在同一個實例上調(diào)用這個成員函數(shù).
class Counter{
  public:
      Counter():n(0){};
      void increment(){++n};
      void decrement(){--n};
  private:
      int n;
}

上面的這個類不是線程安全的,因為如果多個線程都試圖修改數(shù)據(jù)成員n,其結(jié)果是未定義的.這是因為c++中的++和--操作符不是原子操作。實際上,它們會被擴展為三個機器指令:

  • 把變量值裝入寄存器.
  • 增加或減少寄存器中的值
  • 把寄存器中的值寫回內(nèi)存

要使其成為線程安全的類,最簡單的方法是使用QMutex來保護數(shù)據(jù)成員:

class Counter{
  public:
      Counter():n(0){};
      void increment(){QMutexLocker locker(&mutex);++n};
      void decrement(){QMutexLocker locker(&mutex);--n};
      int value() const{QMutexLocker locker(&mutex);return n;}
  private:
      mutable QMutex mutex;
      int n;
}

QMutexLocker類在構(gòu)造函數(shù)中自動的對mutex進行加鎖,在析構(gòu)函數(shù)中自動的進行解鎖.

  • 大多數(shù)的Qt類是可重入的,非線程安全的.有一些類與函數(shù)是線程安全的,它們主要是線程相關(guān)的類.如QMutex,QCoreApplication::postEvent().

Qt 之qSetMessagePattern

  • 改變默認的消息處理輸出. 允許改變qDebug(), qWaring(), qCritical(), qFatal()的輸出.

支持以下占位符:

占位符 描述
%{appname} QCoreApplication::applicationName()
%{category} 日志類別
%{file} 原文件路徑
%{function} 函數(shù)
%{line} 源文件所在行
%{message} 實際的消息
%{pid} QCoreApplication::applicationPid()
%{threadid} 當(dāng)前線程的系統(tǒng)范圍ID(如果它可以獲得)
%{type} “debug”、”warning”、”critical”或”fatal”
%{time process} “debug”、”warning”、”critical”或”fatal”
%{time boot} 消息的時間,啟動進程的秒數(shù)
%{time [format]} 消息產(chǎn)生時,系統(tǒng)時間被格式化通過把格式傳遞至QDateTime::toString()。如果沒有指定的格式,使用Qt::ISODate。
%{backtrace [depth=N][separator=”…”]} 很多平臺不支持,暫略…
  • 還可以使用條件類型,%{if-debug}, %{if-info} %{if-warning}, %{if-critical}%{if-fatal}后面跟著一個%{endif}。如果類型匹配,%{if-*}%{endif}之間的內(nèi)容會被打印。
  • 如果類別不是默認的一個,%{if-category} ... %{endif}之間的內(nèi)容將被打印。

例如:

QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"
  • 默認的模式是:”%{if-category}%{category}: %{endif}%{message}”。也可以在運行時改變模式,通過設(shè)置QT_MESSAGE_PATTERN環(huán)境變量。如果既調(diào)用了 qSetMessagePattern()又設(shè)置了環(huán)境變量QT_MESSAGE_PATTERN,那么,環(huán)境變量優(yōu)先。

示例:

  1. qSetMessagePattern()
int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    // 改變?nèi)笔∠⑻幚沓绦虻妮敵?    qSetMessagePattern("Message:%{message} File:%{file} Line:%{line} Function:%{function} DateTime:%{time [yyyy-MM-dd hh:mm:ss ddd]}");

    // 打印信息
    qDebug("This is a debug message.");
    qInfo("This is a info message.");
    qWarning("This is a warning message.");
    qCritical("This is a critical message.");
    qFatal("This is a fatal message.");

    ...
    return app.exec();
}

輸出如下:

Message:This is a debug message. File:..\MessagePattern\main.cpp Line:138 Function:main DateTime:[2016-07-06 15:21:40 周三] 
Message:This is a info message. File:..\MessagePattern\main.cpp Line:139 Function:main DateTime:[2016-07-06 15:21:40 周三] 
Message:This is a warning message. File:..\MessagePattern\main.cpp Line:140 Function:main DateTime:[2016-07-06 15:21:40 周三] 
Message:This is a critical message. File:..\MessagePattern\main.cpp Line:141 Function:main DateTime:[2016-07-06 15:21:40 周三] 
Message:This is a fatal message. File:..\MessagePattern\main.cpp Line:142 Function:main DateTime:[2016-07-06 15:21:40 周三]
  1. QT_MESSAGE_PATTERN環(huán)境變量

選擇:項目 -> 構(gòu)建環(huán)境,添加環(huán)境變量:QT_MESSAGE_PATTERN = [%{type}] %{appname} (%{file}:%{line}) - %{message}

  • 此時遵循環(huán)境變量優(yōu)先的原則.即同時設(shè)置了qSetMessagePattern()和QT_MESSAGE_PATTERN環(huán)境變量,那么環(huán)境變量生效,qSetMessagePattern()不生效.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:作者簡介作者:唐新華 (xhsmart@263.net)軟件工程師 ?? 信號和槽作為QT的核心機制在QT編...
    njukay閱讀 1,359評論 0 49
  • 15.Qt 和 C++(Qt and C++) 本章的作者:jryannel ** 注意: **最新的構(gòu)建時間:2...
    趙者也閱讀 1,254評論 0 3
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,242評論 25 708
  • 韓元旭、余橙、沈開洋 Qt介紹 Qt是一個跨平臺的C++圖形用戶界面應(yīng)用程序框架。它早在1991年奇趣科技公司兩位...
    開洋_shen閱讀 16,231評論 4 24
  • 很慶幸自己進去到這樣一個圈圈,有博覽群書的Teacher 譚,有僅見過幾面但是無比賢淑的大師姐,自己不舒服卻還一直...
    Shelly07閱讀 141評論 0 0