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)會(huì)告訴C++編譯程序類的存在况芒,而不需要提供類定義中的所有的細(xì)節(jié)(通常放在它自己的頭文件當(dāng)中)憎账。因?yàn)槲覀兊倪@些私有變量都是指針面氓,而且沒(méi)有必要在頭文件當(dāng)中就去訪問(wèn)它們羔味,因而編譯程序就無(wú)需這些類的完整的定義河咽。我們不使用這些類的相關(guān)的頭文件(如<QCheckBox>,<QLabel>),而是使用前置聲明,這可以使得變異的過(guò)程更快一些赋元。
  • 在真正的實(shí)現(xiàn)文件.cpp當(dāng)中忘蟹,我們可以包含#include <QtGui>,這個(gè)頭文件包含了Qt GUI類的定義。Qt由數(shù)個(gè)模塊組成搁凸,每一個(gè)模塊都有自己的類庫(kù)媚值。最為重要的模塊有QtCore、QtGui护糖、QtNetwork褥芒、QtOpenGL、QtScript嫡良、QtSql锰扶、QtSvg献酗、QtXml。其中少辣,在<QtGui>頭文件中為構(gòu)成QtCore和QtGui組成部分的所有類進(jìn)行了定義凌摄。在程序中包含這個(gè)頭文件,就能夠使我們省去在每一個(gè)類中分別包含的麻煩漓帅。在頭文件中本可以直接包含<QtGui>即可锨亏,然而在一個(gè)頭文件中再包含一個(gè)那么大的頭文件實(shí)在不是一種好的編程風(fēng)格,尤其對(duì)于比較大的工程項(xiàng)目更是如此忙干。

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

  • Qt會(huì)在刪除父對(duì)象的時(shí)候自動(dòng)的刪除其所屬的子對(duì)象,包括其所有的子窗口部件和子布局對(duì)象.

信號(hào)和槽機(jī)制

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

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

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

這一機(jī)制的工作過(guò)程是:

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

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

元對(duì)象工具

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

Qt 進(jìn)程間通信

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

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

對(duì)于這種內(nèi)部對(duì)象間的通信月劈,QT主要采用了信號(hào)和槽的機(jī)制。

  • QT信號(hào)機(jī)制是用來(lái)在對(duì)象間通訊的方法藤乙,當(dāng)一個(gè)特定事件發(fā)生的時(shí)候猜揪,signal會(huì)被 emit 出來(lái),slot 調(diào)用是用來(lái)響應(yīng)相應(yīng)的 signal 的坛梁。簡(jiǎn)單點(diǎn)說(shuō)就是如何在一個(gè)類的一個(gè)函數(shù)中觸發(fā)另一個(gè)類的另一個(gè)函數(shù)調(diào)用,而且還要把相關(guān)的參數(shù)傳遞過(guò)去.好像這和回調(diào)函數(shù)也有點(diǎn)關(guān)系,但是消息機(jī)制可比回調(diào)函數(shù)有用多了,也復(fù)雜多了而姐。例如,實(shí)現(xiàn)單擊按鈕終止應(yīng)用程序運(yùn)行的代碼connect(button , SIGNAL(clicked()) , qApp , SLOT(quit()) );實(shí)現(xiàn)過(guò)程就是一個(gè)button被單擊后會(huì)激發(fā)clicked信號(hào)划咐,通過(guò)connect()函數(shù)的連接qApp會(huì)接收到此信號(hào)并執(zhí)行槽函數(shù)quit()毅人。在此過(guò)程中,信號(hào)的發(fā)出并不關(guān)心什么樣的對(duì)象來(lái)接收此信號(hào)尖殃,也不關(guān)心是否有對(duì)象來(lái)接收此信號(hào),只要對(duì)象狀態(tài)發(fā)生改變此信號(hào)就會(huì)發(fā)出划煮。此時(shí)槽也并不知曉有什么的信號(hào)與自己相聯(lián)系和是否有信號(hào)與自己聯(lián)系送丰,這樣信號(hào)和槽就真正的實(shí)現(xiàn)了程序代碼的封裝,提高了代碼的可重用性弛秋。
  • 關(guān)鍵字signals指出隨后開(kāi)始信號(hào)的聲明器躏,這里signals用的是復(fù)數(shù)形式而非單數(shù)俐载,siganls沒(méi)有public、 private登失、protected等屬性遏佣,這點(diǎn)不同于slots。另外揽浙,signals状婶、slots關(guān)鍵字是QT自己定義的,不是C++中的關(guān)鍵字馅巷。信號(hào)也可采用C++中虛函數(shù)的形式進(jìn)行聲明膛虫,也可以實(shí)現(xiàn)重載.
  • 宏定義不能用在signal和slot的參數(shù)中,因?yàn)閙oc工具不擴(kuò)展#define,因此钓猬,在signals和slots中攜帶參數(shù)的宏就不能正確地工作稍刀,如果不帶參數(shù)是可以的。
  • 函數(shù)指針不能作為信號(hào)或槽的參數(shù)敞曹。
class someClass : public QObject  
{  
    Q_OBJECT  
public slots:  
    void apply(void (*applyFunction)(QList*, void*), char*); // 不合語(yǔ)法  
}; 


typedef void (*ApplyFunctionType)(QList*, void*);   
class someClass : public QObject  
{  
    Q_OBJECT   
public slots:  
    void apply( ApplyFunctionType, char *);  // 合法   
}; 
  • 信號(hào)與槽也不能攜帶模板類參數(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 與外部對(duì)象間的通信

QT與外部通信主要是將外部發(fā)來(lái)的消息以事件的方式進(jìn)行接收處理。外部設(shè)備將主要通過(guò)socket與QT應(yīng)用程序進(jìn)行連接澳迫。在此局齿,以輸入設(shè)備與QT應(yīng)用程序的通信為例說(shuō)明QT與外部通信的原理。

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

3. Qt 與其外部進(jìn)程通信

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

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

  • QCOP協(xié)議
  • 信號(hào)-槽(Signal-Slot)機(jī)制
  • FIFO機(jī)制

4. 其他通信方式

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


Qt 的事件處理機(jī)制

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

Qt事件模型真正強(qiáng)大的特色是一個(gè)QObject的實(shí)例能夠管理另外一個(gè)QObject實(shí)例的事件.

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

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

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

  • 方法二: 安裝事件過(guò)濾器,讓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 提供五個(gè)級(jí)別的事件處理和過(guò)濾

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

例如:實(shí)現(xiàn)一個(gè)模擬時(shí)鐘小部件,主要就是使用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)
{
//時(shí)分秒指針顏色
    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();
//正式開(kāi)始繪畫
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);//無(wú)鋸齒
    painter.translate(this->width()/2, this->height()/2);//將坐標(biāo)系原點(diǎn)移動(dòng)到正中心
    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));//坐標(biāo)系旋轉(zhuǎn)曼库,根據(jù)真實(shí)時(shí)間確定旋轉(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前原來(lái)的坐標(biāo)系
    painter.setPen(hourColor);//畫刻度線,用Pen略板,不用Brush
    for(int i=0; i<12; i++){
        painter.drawLine(88,0,96,0);//12小時(shí)對(duì)應(yīng)的刻度
        painter.rotate(30.0);
    }
//畫1-12數(shù)字毁枯,根據(jù)數(shù)學(xué)半徑sin,cos計(jì)算所畫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)中胀糜,主要說(shuō)明一下paintEvent的使用:void QWidget::paintEvent(QPaintEvent *event). Paint 這個(gè)事件只要是窗體事件需要被重繪了就會(huì)被調(diào)用,他是由窗體事件產(chǎn)生的,但是要求程序重畫窗體部件的時(shí)候蒂誉,事件循環(huán)就會(huì)從事件隊(duì)列當(dāng)中選中這個(gè)事件并把它分發(fā)到那個(gè)需要重畫的widget當(dāng)中。并不是所有的paint事件都是由窗口系統(tǒng)產(chǎn)生的距帅,你也可以使用repaint()和update()來(lái)使用它右锨。但是你需要知道的是,因?yàn)閜aintEvent()函數(shù)是protected的碌秸,你無(wú)法直接調(diào)用它绍移。它也繞開(kāi)了任何存在的事件過(guò)濾器。因?yàn)檫@些原因讥电,Qt提供了一個(gè)機(jī)制蹂窖,直接sending事件而不是posting。
  • void QWidget::update():用于更新窗體部件恩敌,它規(guī)劃了所要處理的繪制事件瞬测。但是可以被Qt優(yōu)化,有時(shí)update()執(zhí)行之后不一定會(huì)直接轉(zhuǎn)到paintEvent纠炮。因?yàn)镼t會(huì)把多個(gè)繪制事件自動(dòng)的合并成一個(gè)來(lái)加快繪制的速度,所以推薦使用這個(gè)月趟,而不是repaint(),幾次調(diào)用update()的結(jié)果通常僅僅是一次paintEvent()調(diào)用。利用這一點(diǎn)恢口,在實(shí)現(xiàn)程序的時(shí)候孝宗,我們可以把所有的繪制窗體的那些語(yǔ)句、函數(shù)...都放到paintEvent中耕肩,通過(guò)各種if-else語(yǔ)句進(jìn)行判斷來(lái)繪制因妇,這樣對(duì)速度有很好的優(yōu)化并且可以防止閃爍。
  • 繪制事件還有一點(diǎn)需要注意的是:當(dāng)繪制事件發(fā)生時(shí)猿诸,更新的區(qū)域通常被擦除婚被。如果需要在上一次繪制的基礎(chǔ)上進(jìn)行繪制的話,我們的做法是:使用一個(gè)臨時(shí)變量保存著上次繪制之后的圖两芳,然后在這個(gè)圖上進(jìn)行繪制摔寨,最后再直接的顯示一下這個(gè)圖就ok了。這是個(gè)比較笨的方法怖辆,但也簡(jiǎn)單是复。通過(guò)QPaintEvent::erased()可以得知這個(gè)窗口部件是否被擦除。寫完之后記得檢查一下 竖螃,如果在設(shè)置了WRepaintNoErase窗口部件標(biāo)記的時(shí)候是不會(huì)被擦除的淑廊。

Qt 多線程

  • 假如一個(gè)類的任何函數(shù)在此類的多個(gè)不同的實(shí)例上,可以被多個(gè)線程同時(shí)調(diào)用,那么這個(gè)類被稱為是可重入的,假如不同的線程作用在同一個(gè)實(shí)例上,而其仍然能夠正常的工作,那么稱之為線程安全的.大多數(shù)的C++類天生就是可重入的,因?yàn)樗鼈兊湫偷闹皇且贸蓡T數(shù)據(jù),任何線程可以在類的一個(gè)實(shí)例上調(diào)用這樣的成員函數(shù),只要沒(méi)有別的線程在同一個(gè)實(shí)例上調(diào)用這個(gè)成員函數(shù).
class Counter{
  public:
      Counter():n(0){};
      void increment(){++n};
      void decrement(){--n};
  private:
      int n;
}

上面的這個(gè)類不是線程安全的,因?yàn)槿绻鄠€(gè)線程都試圖修改數(shù)據(jù)成員n,其結(jié)果是未定義的.這是因?yàn)閏++中的++和--操作符不是原子操作。實(shí)際上特咆,它們會(huì)被擴(kuò)展為三個(gè)機(jī)器指令:

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

要使其成為線程安全的類,最簡(jiǎn)單的方法是使用QMutex來(lái)保護(hù)數(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ù)中自動(dòng)的對(duì)mutex進(jìn)行加鎖,在析構(gòu)函數(shù)中自動(dòng)的進(jìn)行解鎖.

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

Qt 之qSetMessagePattern

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

支持以下占位符:

占位符 描述
%{appname} QCoreApplication::applicationName()
%{category} 日志類別
%{file} 原文件路徑
%{function} 函數(shù)
%{line} 源文件所在行
%{message} 實(shí)際的消息
%{pid} QCoreApplication::applicationPid()
%{threadid} 當(dāng)前線程的系統(tǒng)范圍ID(如果它可以獲得)
%{type} “debug”季惩、”warning”录粱、”critical”或”fatal”
%{time process} “debug”、”warning”画拾、”critical”或”fatal”
%{time boot} 消息的時(shí)間啥繁,啟動(dòng)進(jìn)程的秒數(shù)
%{time [format]} 消息產(chǎn)生時(shí),系統(tǒng)時(shí)間被格式化通過(guò)把格式傳遞至QDateTime::toString()。如果沒(méi)有指定的格式青抛,使用Qt::ISODate旗闽。
%{backtrace [depth=N][separator=”…”]} 很多平臺(tái)不支持,暫略…
  • 還可以使用條件類型蜜另,%{if-debug}, %{if-info} %{if-warning}, %{if-critical}%{if-fatal}后面跟著一個(gè)%{endif}适室。如果類型匹配,%{if-*}%{endif}之間的內(nèi)容會(huì)被打印举瑰。
  • 如果類別不是默認(rèn)的一個(gè)捣辆,%{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}"
  • 默認(rèn)的模式是:”%{if-category}%{category}: %{endif}%{message}”此迅。也可以在運(yùn)行時(shí)改變模式汽畴,通過(guò)設(shè)置QT_MESSAGE_PATTERN環(huán)境變量。如果既調(diào)用了 qSetMessagePattern()又設(shè)置了環(huán)境變量QT_MESSAGE_PATTERN邮屁,那么整袁,環(huán)境變量?jī)?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)境變量

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

  • 此時(shí)遵循環(huán)境變量?jī)?yōu)先的原則.即同時(shí)設(shè)置了qSetMessagePattern()和QT_MESSAGE_PATTERN環(huán)境變量,那么環(huán)境變量生效,qSetMessagePattern()不生效.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坐昙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芋忿,更是在濱河造成了極大的恐慌炸客,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戈钢,死亡現(xiàn)場(chǎng)離奇詭異痹仙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)殉了,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門开仰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人薪铜,你說(shuō)我怎么就攤上這事众弓。” “怎么了隔箍?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵谓娃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蜒滩,道長(zhǎng)滨达,這世上最難降的妖魔是什么奶稠? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮捡遍,結(jié)果婚禮上锌订,老公的妹妹穿的比我還像新娘。我一直安慰自己画株,他們只是感情好瀑志,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著污秆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昧甘。 梳的紋絲不亂的頭發(fā)上良拼,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音充边,去河邊找鬼庸推。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浇冰,可吹牛的內(nèi)容都是我干的贬媒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肘习,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼际乘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起漂佩,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤脖含,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后投蝉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體养葵,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年瘩缆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关拒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖妒貌,靈堂內(nèi)的尸體忽然破棺而出加派,到底是詐尸還是另有隱情,我是刑警寧澤畔柔,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站臣樱,受9級(jí)特大地震影響靶擦,放射性物質(zhì)發(fā)生泄漏腮考。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一玄捕、第九天 我趴在偏房一處隱蔽的房頂上張望踩蔚。 院中可真熱鬧,春花似錦枚粘、人聲如沸馅闽。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)福也。三九已至,卻和暖如春攀圈,著一層夾襖步出監(jiān)牢的瞬間暴凑,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工赘来, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留现喳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓犬辰,卻偏偏與公主長(zhǎng)得像嗦篱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幌缝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

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