Qt中的每一個類,都有一個對應的同名頭文件,其中包含其類定義。例如要使用QApplication類,則須要在程序中添加" #include <QApplication>"html
QApplication類用於管理應用程序範圍內的資源。其構造函數須要main函數的argc和argv做爲參數。java
widget被建立時都是不可見的(always created hidden)。widget中可容納其它widget。linux
Qt中的widget在有用戶行爲或狀態改變時會emit signal。 signal能夠和slot函數鏈接在一塊兒(connect),這樣當有signal被emit時,對應的slot函數會被自動調用。git
QWidget類的構造函數須要一個 QWidget * 指針做爲參數,表示其parent widget(默認值爲0,即不存在parent widget)。在parent widget被刪除時,Qt會自動刪除其全部的child widget。程序員
Qt中有三種Layout Manager 類: QHBoxLayout,QVBoxLayOut,QGridLayOut。基本模式是將widget添加進LayOut,由Layout自動接管widget的尺寸和位置。算法
啓動Qt程序時能夠經過 -style 參數改變程序的默認顯式風格。sql
Chapter 2 Creating Dialogs數據庫
2.1 Subclassing Dialog編程
Qt中全部dialog的基類是QDialog。QDialog派生自QWidget。windows
Qt中全部定義了signal或slot的類,在其類定義的開始處都要使用Q_OBJECT宏。Qt中的signal關鍵字其實是宏定義。相似的,slots關鍵字也是宏定義。
Qt所提供的類分爲若干模塊:QtGui,QtNetWork,QtOpenGL,QtSql, QtSvg和QtXml等。
QObject::tr() 函數將輸入的字符串轉換爲其餘語言(國際化)。對全部用戶可見的字符串都使用tr()函數是一個良好的習慣。
Buddy:兩個widget A和B,若A擁有快捷鍵,當用戶按下該快捷鍵時,程序的輸入焦點自動轉移到B上,則稱B是A的buddy。
QWidget::close() 是一個slot,其默認行爲是使對應的widget隱藏不可見,但並不刪除該widget。
Layout 中可包含widget和其餘layout。經過嵌套使用QHBoxLayOut、QVBoxLayOut、QGridLayOut,能夠構造很是複雜的dialog。值得注意的是:layout manager 類並不屬於widget。實際上,它派生自QLayout,而QLayout又派生自QObject。
QWidget::sizeHint() 返回一個widget()的理想大小(ideal size)。
emit關鍵字是Qt特有的,用於產生signal。
MOC(Meta-Object-Compiler): 對於全部使用了Q_OBJECT宏的類,在編譯時都須要經過MOC的處理,不然會出現連接錯誤。解決該錯誤的辦法也很簡單,從新執行qmake以更新makefile,而後從新編譯。
2.2 Signal and Slot in Depth
Signal & Slot 機制是Qt的根基。
Slot和普通的C++類成員函數幾乎徹底一致;能夠是virtual的,能夠被重載,能夠是public、protected或private的,並且也可因此被其餘成員函數直接調用。
signal與slot之間的關聯能夠是一對1、一對多或多對一。
signal和signal之間也能夠被關聯,此種狀況與signal-slot的區別在於,當第一個sigal被emit時,第二個signal也被emit。
能夠調用disconnect()來解除signal 與slot之間的關聯,一般狀況下不多須要顯式調用disconnect(),由於對象被刪除時Qt會自動移除與其相關的關聯。
sigal-slot或signal-signal這樣的關聯,要求兩者具有相同的參數列表;若signal比slot中的參數多,多餘的參數會被忽略。
思惟定勢:signal-slot機制只能用於widget。實際上signal-slot機制是由QObject實現的,並不只侷限於GUI編程,能夠用於任何QObject子類。
2.3 Rapid Dialog Design
使用Qt Designer建立的form最終被轉換爲C++代碼。
qmake工具可以檢測到interface file(*.ui files),並調用uic,即Qt的user interface compiler。uic將.ui文件轉換爲C++代碼,並存放在形式爲ui_xxx.h的文件中。該文件中給出了dialog對應類的完整定義,幷包含一個 setupUi()成員函數,用於初始化form。
注意,由uic建立的這個類未派生自任何Qt class。
Qt的 parent-child機制是由QObject實現的。當建立一個對象時若指定了parent,則parent將該對象添加至其 childern list。當parent被刪除時,Qt會遍歷其childern list並刪除每一個child,該過程會遞歸進行。這一機制極大的簡化了內存管理,下降了內存泄露的風險——程序員只需顯式的刪除經過new建立而且沒有parent的對象。
對於widget,parent還有一層附加的意義:chidl widget 是顯示在parent widget的範圍以內的。若是刪除parent widget,不只child widget從內存中被釋放,在屏幕上也會消失。
QDialog::accept() 將dialog的返回值設爲QDialog::Accepted(值爲1),而QDialog::reject()將返回值設爲QDIalog::Rejected(值爲0)。
2.5 Dynamic Dialogs
Dynamic Dialog指的是程序在運行時根據.ui文件建立的dialog。這樣的dialog不是經過uic將.ui轉換爲C++代碼,而是在運行時使用QUiLoader類裝載.ui文件。
可使用QObject::findChild<T> ()來訪問form的child widget。
要使用QUiLoader,須要在Qt程序的.pro文件中添加如下內容: CONFIG += uitools
Dynamic dialog容許在不從新編譯程序的前提下更改form的佈局。
Chapter 3 Creating Main Windows
3.1 Subclassing QMainWindow
應用程序的主窗口是經過建立QMainWindow的派生類來完成的。QMainWindow和QDialog同樣,都是派生自QWidget。
closeEvent()是由QWidget提供的一個虛函數,在用戶關閉窗口時會被自動調用。
setCentralWidget()將某個Widget設置爲主窗口的central widget, 而central widget意味着在顯示時會佔據主窗口的中央位置。
Qt下的GUI 編程支持多種圖形格式。可使用多種方式爲應用程序提供圖像,最多見的包括:
1). 將圖像存儲在文件中,運行時加載之。
2). 在源碼中include XPM文件(XPM文件也是合法的C++文件)。
3). 利用Qt的資源機制。
Qt的資源機制比之運行時加載更方便,並對全部支持的圖像格式都能良好工做。
爲了利用Qt的資源機制,須要建立一個資源文件,並在.pro文件中對應添加一行來對資源文件進行標識。例如:
RESOURCES= spreadsheet.qrc
資源文件自己採用了簡單的XML格式。它被編譯進程序的可執行文件,所以不會被丟失。在對資源進行定位時,使用路徑前綴":/",例如「 :/images/icon.png "。資源自己能夠是任何類型的文件。
3.2 Creating Menus and Toolbars
Qt經過引入Action這一律念簡化了對menu和toolbar的編程。一個Action能夠被添加到任意數量menu和toobar中。
在Qt中對menu和toolbar的編程涉及到三個步驟:
1). 建立並設置Action
2). 建立menu,並在其中添加Action
3). 建立toolbar,並在其中添加Action
Action的建立是經過QAction類來實現的,對每一個Action,能夠爲其設置accelerator,parent,shortcut key, 可見性以及status tip等屬性,並能夠經過調用connect()爲ACtion設置被觸發要執行的操做。
QTableWidget的基類QAbstraceItemView提供了selectAll()這個slot。
QApplication類提供了aboutQt()這個slot,能夠經過全局變量qApp(一個類型爲QApplication *的指針)來使用之。
在Qt中,menu由QMenu類的實例表示。而Qmenu是要被放入QMenuBar之中的。函數QMainWindow::menuBar()返回一個類型爲QMenuBar * 的指針。QMenuBar::addMenu()根據指定文本建立一個QMenu widget並將其添加進MenuBar中。QMenu::addAction() 則爲Menu添加Action。
任意Qt Widget均可以具有相關的一系列QAction。經過調用QWidget::addAction() ,能夠爲Widget添加Action。這一特性可用來建立上下文菜單。
3.3 Setting Up the Status Bar
QMainWindow::statusBar() 返回一個指向status bar的指針;status bar 在statusBar()第一次被調用時被建立。
3.4 Implementing The Menu
QMessageBox::Defalut修飾符使得被修飾的Button成爲默認Button,而QMessage::Escape修飾符則使得Esc鍵自動觸發被修飾的Button。
QMessageBox::warning()用於彈出提示對話框。該函數屬於Qt提供的static convenicence function
static convenience function
QFileDialog::getOpenFileName() 可用於從用戶處得到文件名——該函數彈出一個文件選擇對話框,要求用戶選擇一個文件,並返回文件名,或者在用戶選擇"Cancel"時返回空字符串。該函數的第一個參數是其parent widget。對於dialog和其餘widget,parent-child關係的意味是不徹底相同的。一個dialog永遠是一個獨立的窗口,可是若是它擁有parent,則默認在parent之上居中顯示。
當用戶發出關閉窗口的操做時,Qwidget::close() 這個slot會被調用,該slot向對應的widget發送close event。從新實現QWidget::closeEvent()可以攔截這個event,以便肯定是否真的要關閉窗口,防止誤操做。
每一個QWidget都有一個windowModified屬性,在窗口文檔被修改時應該被設爲True,不然被設爲false。
QString::arg() 函數將字符串中編號最低的"%n"用參數進行替換,並返回替換後的字符串。
每一個Action均可以擁有一個類型爲QVariant的關聯數據。
Qt中的qobject_cast<T>() 機制對於動態庫也能夠正常工做。
3.5 Using Dialog
modeless window——one that runs independently of any other windows in the application
對於modeless dialog ,當其被彈出時,可能處於三種狀況:
1). 這是該對話框第一次被激活
2). 該對話框以前曾被激活,但用戶又將其關閉
3). 該對話框以前曾被激活,並且仍可見
show() 將一個隱藏窗口變爲可見,而activateWIndow()則將窗口的狀態變爲active。
model window——pops up when invoked and blocks the application,preventing any other processing or interactions until it is closed.
一個dialog如果用show()來激活,則是modeless dialog;若經過exec()來激活,則是model dialog。此外,還能夠調用setModel()來設置dialog的顯示模式。
QDialog::exec() 的返回至在dialog被確認時爲true,不然爲false。
在棧上建立 model dialog是一種良好的編程慣例,由於在使用完後就再也不須要,而model dialog會在做用域結束後自動被銷燬。
因爲多數應用程序的About box 都是高度雷同的,Qt中提供了一個方便的static convenicence function QMessage::about(),該函數和QMessageBox::warning()很類似。
3.6 Storing Setting
Qt中是經過QSettings類來將應用程序的設置信息存儲到平臺相關的位置——windows下存入註冊表中,unix中存在文本文件中。
QSettings的構造函數包含兩個參數,分別是organization's name 和 application's name ,Qt使用這兩個參數來對應用程序的設置信息進行定位。
QSettings以key-value pair的形式存儲信息。
3.7 Multiple Documents
要想實現多文檔程序,首先必需要經過new在堆上建立主窗口,而不是在棧上建立主窗口。
QAplication::closeAllWindows() 這個slot完成的操做是關閉應用程序全部的窗口,除非其中某個窗口拒絕了close event。程序員不須要擔憂未保存的修改,由於這會由QWidget::closeEvent()負責處理。
經過在MainWindow的構造函數中調用setAttribute()函數來設置Qt::WA_DeleteOnClose屬性,能夠要求Qt在窗口被關閉時將其自動銷燬
Qt在其可用全部平臺上都支持SDI和MDI程序的建立。
3.8 Splash Screnns
在Qt中爲程序添加splash screen很是簡單,可經過QSplashScreen類來實現。
一般狀況下,與splash screen相關的代碼都放在main()中,出如今調用QApplication::exec()以前。
Chapter 4 Implementing Application Functionality
4.1 The Central Widget
QMainWindow的中央區域能夠被任何類型的widget佔據。
4.2 Subclassing QTableWidget
QTableWidget會自動建立QTableWidgetItem來存儲用戶的輸入。
QTableWidgetItem類並非widget,而是一個純粹的data class。
QTabeWidget::setItermProtype()能夠設置在得到用戶輸入的狀況下自動建立哪一種cllass。
4.3 Loading and Saving
QFile & QDataStream
QFile的析構函數負責將打開的文件關閉。
QDataStream類具備很強的通用性,可做用於QFile,QBuffer,QProcess,QTcpSocket,QUdpSocket。
Qt還提供了一個QTextStream類用於專門讀寫文本文件。
4.6 Subclassing QTableWidgetItem
每一個QTableWidgetIterm中可存儲若干數據,這是經過個QVariant來實現的。每個QVariant對象都以某個role來存儲某一類數據,經常使用的role有Qt::EditRole和Qt::DiaplayRole。
QVarinant對象能夠存放多種類型的變量值,並提供向其餘類型轉型的函數接口。
使用默認構造函數建立的QVariant對象被視爲invalid variant。
Chapter 5. Creating Custom Widgets
用戶自定義的控件能夠經過繼承現有的Qt控件實現,也能夠直接從QWidget繼承來實現
5.1 Customizing Qt Widgets
5.2 Subclassing QWidget
經過對QWidget進行派生,並從新編寫其部分event handler來進行繪圖和響應用戶操做,程序員能夠實現對widget的外觀和行爲的徹底控制。
Qt的內置Widget如QLabel、QPushButton、QTabelWidget等,就是以這種方式實現的。
宏Q_PROPERTY()用來爲widget聲明和添加自定義屬性。
每一個屬性的定義都對應一個數據類型(任何被QVarinat支持的類型均可以),一個read function以及可選的write function。
對於包含自定義屬性的類,Q_OBJECT和Q_PROPERTY()這兩個宏都是必備的。
QImage類以一種硬件無關的方式存儲圖像信息。
Qt中提供了兩個類型用於存儲色彩信息:QRgb和QColor。
QRgb實際上是一個typedef,用於存放32-bit的像素信息。
QColor則是一個提供了許多接口函數的類,在Qt中普遍的用於存儲色彩。
QWidget::update()函數用於對widget進行強制性的重繪。
QWidget::updateGeometry()用於告知包含該widget的layout:該widget的size hint已發生變化,layout會自動進行調整。
經過調用QWidget::update()和QWidget::repaint(),能夠強制性的產生一個 paint event,二者的卻別在於repaint()致使當即重繪,而update()只是將一個paint event放入event queue中。
若是對update()進行連續屢次調用,Qt會將連續的paint event壓縮合併爲一個paint event,以防止圖像抖動。
每一個widget都擁有一個palette,用於設置widget中在什麼狀況下使用什麼色彩,如背景色、文本色等。
widget的palette由三個color group組成:active ,inactive ,disabled。
QWidget::palette()以QPalette的形式返回widget的palette,而clolor group則經過枚舉類型QPalette::ColorGroup指定
5.3 Intergrating Custom Widgets with Qt Designer
要像在Qt Designer中使用自定義widget的話,必需要讓Qt Designer可以瞭解到它們的存在。
有兩種機制:promotion approach &plugin approach
promotion approach 很容易也很省時,但缺點是自定義widget的自定義屬性在Qt Designer中是不可見和不可訪問的,而使用plugin approach時則不存在這些問題。
plugin approach要求建立一個Qt Designer 能夠在運行時加載的plugin library,以用於建立widget的實例。因爲Qt的MOC機制,Qt Designer能夠動態獲取widget的property list。
要使用plugin approach ,首先要對QDesignCustomWidgetInterface進行派生,並重寫某些虛函數。
Q_INTERFACES()宏用於告知Qt該類實現了哪一個interface。
在實現plugin class的源文件尾部,必須使用Q_EXPORT_PLUGIN2()宏使得該plugin對Qt Designer可見、可用。該宏第一個參數是plugin的名字,第二個參數是實現該plugin的class name。
5.4 Double Buffering
QWidget::style()返回用於繪製該widget時所使用的style。Qt中的style都是QStyle的派生類。同一應用程序中的 widget通常都使用相同的style,然而能夠調用QWidget::setStyle()來進行widget層次的特別設置。
Chatper 6 Layout Management
6.1 Laying Out Widgets on a Form
Qt提供的的基本的Layout Manager包括:QHBoxLayout,QVBoxLayout,QGridLayout和QStackLayout。
Qt中其它能完成Layout management功能的類包括 QSplitter,QScrollArea,QMainWindow和QWorkspace。
Qt中管理child widget的layout共有三種方式:absolute positioning, manual layout和layout managers。
Absolute positioning:即由程序員經過hard-coded的形式管理child widget的位置和尺寸。
Manual Layout:child widget的位置依然由程序員經過hard-coded的方式肯定,而尺寸與父窗口的大小成必定比例,而不是徹底的hard-coded。這種方式經過對form的resizeEvent()進行再實現來對child widget的定位。
最重要的三個Layout Manger是QHBoxLayout,QVBoxLayout,QGridLayOut,他們都是派生自QLayout
QGridLayout的使用略微有些複雜,它工做在一個由Cell組成的二維grid上。對於QGridLayout,爲其添加widget的方式以下:
layout->addWidget(widget,row ,colum,rowSpan,columnSpan)
其中widget爲待添加的child widget, row和clomun肯定該widget所佔據空間中左上角那個Cell的位置座標,rowSpan和columnSpan則指定widget的大小,這兩個參數的的缺省值爲1。
addStretch()向Layout Manager中添加「佔位符」。
每一個widget都有本身的size policy,由其告知layout 系統如何處理該widget外形上的stretch或是shrink。Qt中widget的size policy是經過QSizePolicy類來表示的。每一個QSizePolicy由水平和豎直兩組size policy組成,最多見的值包括:
Fixed Minimum Maximum Prefered Expanding
除了上述兩組size policy外,QSizePolicy中還存儲水平和豎直方向的stretch factor,該值用來代表在form尺寸擴展時widget隨之擴展的比率。
6.2 Stacked Layouts
QStackLayout 類能夠管理多個page,但每次只顯示其中之一,而將其餘page向用戶隱藏。
QStackLayout類自己是不可見的。
爲了方便起見,Qt包含有QStackedWidget類,即一個內置了QStackedLayout的QWidget。
6.3 Splitter
類QSplitter是一個能包含其餘widget的widget。QSplitter中包含的widget按順序排列,並被splitter handle相互分隔開。
QSplitter經過構造函數中的參數來來決定是水平方向仍是豎直方向。
不一樣於前面介紹的Layout Mangener們只是負責處理widget的layout而自身沒有可視化的表示,QSplitter派生自QWidget,所以能夠同其它widget同樣的被使用。
QSplitter類提供了存儲自身狀態的兩個函數:savestate()和restorestate()。
6.4 Scrolling Areas
QScrollArea類提供了1個可滑動的viewport和2個滑動條。
QScrollArea的使用方法是調用其提供的setWidget()函數,將但願爲其添加滑動條的widget添加。QScrollArea自動將添加進來的widget的parent設定爲其viewport,而其viewpoint可經過QScrollArea::viewport()進行訪問。
QScrollArea的多數功能是經過繼承QAbstraceScrollArea類而得到的。而諸如QTextEdit和 QAbstractIterView這樣的類是派生自QAbstractScrollArea的,所以不須要將其用QScrollArea類包裹起來以得到scroll bar。
6.5 Dock Widgets and Toolbars
在Qt中,dock widget是經過QDockWidget類來實現的。
每一個dock widget都有本身的title bar。
從Qt4開始,toolbar擁有本身專屬的顯示空間,而再也不是如以前的版本中容許dock widget與其分享。
對一個widget調用setAllowedArea()能夠指定容許在那些dock areas上放置該widget。
6.6 Multiple Document Interface
Qt中,編寫MDI程序是經過使用QWorkspace類,將其做爲程序的central widget,並把每一個文檔窗口都做爲QWorkspace的child。
Qt程序的命令行參數中與Qt相關的參數,會由QApplication的構造函數負責自動移除掉,不會傳遞給Qt程序的main()函數。
Chapter 7 Event Processing
Qt中大多數event都是做爲對用戶操做的響應而產生的,但也有一些是系統內部獨立生成的。
在使用Qt進行編程時,一般不多須要考慮event,由於Qt中的widget會在有重要事件發生時emit signal。Event在咱們要編寫自定義widget或是要修改現有Qt widget的特性時,則變得很重要。
不要在概念上將event和signal兩個概念混淆。當程序員操縱使用widget時,signal是須要關注的對象;而當程序員須要實現一個widget時,event則是須要關注的對象。
例如,當使用QPushButton時,咱們更關注其提供的clicked() signal 而不是致使該signal被QPushButton emit的低層次的鼠標或鍵盤event。可是若是咱們要本身實現一個相似於QPushButton的類,那就輪到咱們編寫代碼來處理鼠標和鍵盤操做,並在必要時emit clicked() signal。
7.1 Reimplementing Event Handles
Qt中,每一個event都是一個派生自QEvent的對象。Qt中包含超過100種的event,每種event有一個enum value進行標識。
QEvent::type()返回event type。
全部event都是經過其對應類中的event()函數,來向對象發送通告的;這個event()函數繼承自QObject。QWidget中 event()的實現是將最多見類型的event轉發給對應的event handlers,例如mousePressEvent(),keyPress-Event,painEvent()。
鍵盤event對應的event handler是keyPressEvent()和keyReleaseEvent()。
QKeyEvent::modifiers()
Tab鍵和Shift+Tab鍵的處理有些特殊,它們不是由keyPressEvent()負責處理,而是在QWidget::event()中進行處理,並且發生在調用keyPressEvent()以前,處理方式是將輸入焦點按照鏈中的順序向後或向前傳遞。
實現key binding的更高層次的實現方式是利用QAction。QAction類內部使用了QShortCut類來實現key binding。
QObject::startTimer() 用於建立定時器,並返回相應的ID。QObject支持多個定時器同時存在。
killTimer()用於銷燬定時器,參數爲定時器的ID。
Timer event位於底層,實現定時功能更簡單的方式是使用QTimer類,QTimer會按期的emit timeout() siganl。
7.2 Installing Event Filter
Qt的evnet model中很強大的一個特性,就是能夠設定某個對象來監控另一個對象,後者全部的event在對其可見前都要先經過前一個對象的監控和處理。
Qt中設置event filter涉及到兩步操做:
1. 在要被監控的對象中調用installEventFilter() 來完成對監控者的註冊。
2 在監控者的eventFilter() 中對被監控者的event進行處理。
Qt的event model中,一個event若未獲得處理(event handler的返回值爲false),則Qt會負責將該event向上傳遞,即交由其parent負責處理
Qt中對event的處理能夠分爲下面5個層次:
1. 對某個特定的event handler進行重實現
2. 對Widget中的QObject::event()進行重實現,這樣在event被傳遞給特定的event handler以前就獲得了處理。
3. 爲某個對象安裝event filter
4. 爲QApplication object安裝filter,這樣能夠監控應用程序中全部對象收到的全部event,在進行debug是很是有用。
5. 派生QAapplication的子類,並對notify()進行重實現。Qt調用notify()來發送event的。這是捕獲全部event的惟一方法。
7.3 Staying Responsive During Intensive Processing
在完成耗時操做的同時,還要保證程序可以對用戶操做正常相應,常見的解決機制是多線程。
一種簡單的解決方案是在耗時操做的過程當中有規律的調用QApplication::processEvnets()。該函數通知Qt處理pending events,處理結束後再將控制權返回至調用者。
Qt中的進度對話框是由QProgressDialog類來實現的。
除了使用多線程和進度對話框外,還存在一種徹底不一樣的處理耗時操做的方法:操做只在程序空閒(無用戶交互)時再進行,而不是馬上開始執行。
這種方法能夠利用0-milisecond timer來實現,每次定時器觸發時,檢查是否有pending event,若無則繼續好事操做,如有則處理event,完成與用戶的交互。
Chapter 9 Drag and Drop
9.1 Enabling Drag and Drop
默認狀況下,QTextEdit這個widget接受來自於其餘程序的文本拖拽的;若是用戶將一個文件拖拽至其上,它會將文件名插入顯示文本。能夠調用setAcceptDrops()來容許或禁止接受拖拽。
dragEnterEvent() ,該函數在用戶將一個對象拖(drag)至widget之上時被調用,其參數爲QDragEnterEvent類型的指針。
默認狀況下,widget不接受用戶的拖拽行爲;若對該指針調用acceptProposedAction(),則是告知Qt容許該widget接受用戶的拖拽行爲,Qt會經過改變鼠標形狀來提示用戶。
dropEvent() ,該函數在用戶將一個對象拽(drop)至widget之上時被調用,參數爲QDropEvent類型的指針。
QWidget類還提供了dragMoveMent()和dragLeaveEvent()這兩個函數,但對於大多數應用而言不須要對其進行再實現。
mousePressEvent(): 鼠標被按下時該函數被調用
mouseMoveEvent() :鼠標保持按下的狀態且移動時,該函數被調用。
QDrag類使用QMimeData類來存儲與拖拽操做相關的信息。
9.2 Supporting Custom Drag Types
能夠從如下三種機制中進行選擇:
1. 在源這一邊調用QMimeData::setData(),將信息存儲在QByteArray中,而在接受者這一方,調用QMimeData::data()將信息提取出來。
2. 對QMimeData進行派生,在子類中對formats()和retrieveData()這兩個函數進行從新實現,來處理自定義數據。
3. 若是拖拽動做發生在一個應用程序的內部,那麼能夠對QMimeData進行派生,將信息存儲在該子類中。
QMimeData::formats()返回其支持的MIME類型列表。
QMimeData::retrieveData()將某個指定MIME類型的數據以QVariant的形式返回。QMimeData所提供 text()、html()、urls()、data()等接口函數,都是依靠retrieveData()來完成底層操做的。
9.3 Clipboadr Handling
Qt中經過QApplication::clipboard()來得到對QClipboard的指針。對系統clipboard的寫操做經過 setText(),setImage或setPixmap()完成,而讀操做則經過text(),image()和pixmap()來完成。
QClipboard::setMimeData()
QClipboard::MimeData()
QClipboard::supportsSelection() 在X11平臺下返回true,其餘環境下返回fasle。
若是但願每當clipboard中的內容發生變更時收到通知,能夠利用Qt提供的QClipboard::dataChanged()這個slot
Chapter 10 Item View Classes
MVC機制:Model-View-Controller
Qt中提供一種模仿MVC的model/viewer機制。
Qt中的delegate這個抽象概念與Controller略微不一樣,它負責爲item的生成和編輯提供良好的控制。
Qt爲每種類型的view都提供了默認的delegate,這對於大多數應用程序已經足夠了,一般狀況下程序員不須要考慮delegatd的問題。
能夠爲一個model註冊兩個或更多的view,Qt自動保持多個view之間數據的同步和一致性,當數據在某個view中被修改後,會自動在其餘相關view中反映出來。
大多數狀況下,程序向用戶提供的item的數量並不龐大,所以能夠簡單的使用Qt內置的item view clas(QListWidget,QTableWidget和QtreeView),而沒有必要使用Qt提供的model/view 機制。但對於成員數量很大的數據集,採用model/view機制則是明智的選擇。
10.1 Using Item View Convenience Classes
QListWidget
QListWidget中包含多個roles,每一個都與一個QVariant變量關聯。最經常使用的roles由Qt::DisplayRole,Qt::EditRole,Qt::IconRole
對於上述role,Qt都提供了方便的接口函數用於讀寫數據。程序員還能夠對role進行自定義,經過Qt::UserRole或更大的數值進行標識。
默認狀況下QListWidget是隻讀的,須要調用QAbstractItemView::setEditTriggers()爲其設定能引起編輯操做的動做。
QTableWidget
默認狀況下QTableWidget是容許編輯的,能夠調用QAbstractItemView::setEditTriggers(QAbstractItemView::NoEditTriggers)來禁止編輯。
QtreeWidget
默認狀況下,QtreeWidget是隻讀的。
10.2 Using Predefined Models
Qt提供的預約義model有如下幾種:
QStringListModel 存儲一組字符串
QStandardItemModel 存儲任意層次結構的數據
QDirModel 對文件系統進行封裝
QSqlQueryModel 對SQL的查詢結果集進行封裝
QSqlTableModel 對SQL中的table進行封裝
QSqlRelationalTableModel 對帶有foreign key的SQL table進行封裝
QSortFilterProxyModel 對另外一個model執行sort and/or filter
model中存放的每項數據都有相應的"model index",由QModelIndex類來表示。
每一個index由三個部分構成:row,column和代表所屬model的指針。對於一維的list model,column部分永遠爲0。
在model/view 機制中,對數據的操做都是經過model執行的,而model負責保證在數據發生變更時view自動更新。
QDirModle::mkdir()——建立文件夾的工做能夠經過QDir類來完成,不過QDirModel提供了工做於QModelIndex之上更方便的函數。
不一樣於其它model,QSortFilterProxyModel對一個已有的model進行封裝,並完成數據在底層modle和view之間的傳遞。
setSourceModel()
10.3 Implementing Custom Models
model中的每項數據都有對應的index和一組稱爲「role」的屬性,其中最經常使用的有Qt::DisplayRole和Qt::EditRole。
對於list和table這兩類model,其中每一個元素的parent都是root,即表示爲一個invalid QModelIndex。
而對於tree model,某些元素的parent爲root,而有些元素的parent爲model中的其它元素。
Qt中提供了幾種model 基類,包括 QAbstractListModel,QAbstractTableModel和QAbstractItemModel ,其中QAbstractItemModel是另外二者的基類,用於支持範圍很廣的modles,包括具有遞歸層次結構的;而 QAbstractListModel用於支持一維數據集,QAbstractTableModel用於支持二維數據集。
實際上要建立只讀的自定義model的話,並非件困難的事情。
要自定義只讀的table model的話, 須要從新實現rowCount(),columnCount()和data()這三個函數。
createIndex() 用於建立並返回一個model index
flags()被model用於表示能夠對數據執行的操做(例如,是否可編輯),從QAbstractTableModel繼承而來的默認實現是返回Qt::ItemSelectable | Qt::ItemIsEnabled
qDeleteALl()對一個含有指針的容器進行迭代,並對其中每一個指針元素執行delete操做。
10.4 Implementing Custom Delegates
view中的每一個item是由delegate負責顯示和編輯的,在大多數狀況下,view默認的delegate足夠知足用戶需求。
setItemDelegate() 爲view指定其delegate。
QItemDelegate & QAbstractItemDelegate
要提供一個容許編輯的自定義delegate,咱們必須對createEditor(),setEditorData(),setModelData()進行從新實現,並且還必須從新實現paint()以改變item的顯示。
QTimeEdit & QTime
當用戶開始編輯操做時,view會調用createEditor()來建立一個Editor,而後調用setEditorData()來用item的當前值對Editor進行初始化。
Chapter 11 Container Class
Qt提供的容器類的最大優勢在於平臺無關性和隱式共享特性
11.1 Sequentail Containers
QVector<T> array-like data structure(在尾部插入數據時效率很高,而在中間和頭部插入數據時開銷很大)。
QVector提供了[]運算符
QVector能夠用<<運算符代替append()函數。
QVector中的基本類型及指針被初始化爲0。
QLinkedList<T>
QLinkedList不提供[]運算符,因此必須經過迭代器來對其進行遍歷。
QList<T> array-list:綜合了QVector<T>和QLinkedList<T>最重要的優勢: 支持[]運算符
在頭部或尾部的插入/刪除操做很迅速,而尺寸在1000如下時,在中間的插入/刪除操做也很迅速。
一般狀況下,QList是最合適的通用型容器。
QStringList: QList<QString>的子類,在Qt中的API中被普遍使用
QStack<T> 和QQueue<T>是Qt提供的兩個convenience subclasses,QStack<T>其實是一個額外提供了push(),pop()top()接口的QVector,而 QQueue<T>實際是一個額外提供了enqueue()和dequeue()和head()的QList。
容器中能夠放置的類必須擁有default constructor、copy constructor 和 assignment operator(顯式定義或由編譯器生成)
注意,派生自QObject的類不符合上述要求,由於其不具有copy constructor和assignment operator;解決方法是在容器中存儲對象指針而不是對象自己。
容器中所存放的元素自己也能夠是容器,便可以嵌套——不過須要注意將連續的尖括號用空格分隔開,以避免編譯器誤認爲>>運算符。
Iterator
Qt支持兩種風格的迭代器——Java-style和STL-style
Java-style的迭代器更容易使用,而STL-style的迭代器能夠同Qt和STL中的算法聯合使用,更爲強大。
Java-style Iterator
每一個sequential容器類,都有兩個Java-style的迭代器類型:只讀迭代器和讀寫迭代器。
在使用Java-style的迭代器時,要清楚的第一件事情就是:迭代器並不直接指向容器中的元素,而是指向元素以前或以後的位置。迭代器被初始化時指向容器中第一個元素以前;若迭代器的右側有元素存在,hasNext()函數返回true;next()函數返回位於迭代器右側的元素,並將迭代器向右方移動一個元素的位置;hasPrevious()和previous()函數執行反方向的操做。
remove()函數老是刪除最近一次被跳過的那個元素。
setValue()函數老是對最近一次被跳過的那個元素執行更新操做
insert()函數在迭代器當前指向的位置處插入新元素,並將迭代器指向新元素及其後續元素之間的位置。
STL-style Iterator
每一個sequential容器類,都有兩個STL-style的迭代器類型:Container<T>::iterator和Container<T>::const_iterator。
容器的begin()函數返回一個指向容器中頭部元素的iterator,而end()返回指向容器中尾部元素以後位置的iterator;
在容器爲空時,begin()和end()的結果相同。
一般經過調用isEmpty()來檢查容器是否爲空,而不是經過比較begin()和end()的結果。
能夠對STL-style的iterator使用+、-、*這三個運算符,相似於指針的用法。
某些Qt函數的返回值是容器類;若是須要使用STL-style的迭代器來對這樣的返回值進行遍歷,必須保存返回值的一個副本,並在副本上完成遍歷,不然會可能會致使所謂的"dangling iterator"。
注意,若使用java-style的只讀迭代器,在這種狀況下會隱式的完成複製的工做,保證迭代器老是在副本上進行遍歷操做。
implicit sharing(copy on write)
Qt中的implicit sharing機制的美妙之處在於它鼓勵程序員在返回對象時採用傳值這種簡明的方式而不是引用或指針。
STL與此相反,鼓勵程序員使用non-const引用來傳遞vector以免將函數返回值的複製開銷。
Qt中全部的容器都採用了implicit sharing機制;此外不少其餘類QByteArray,QBrush,QFont,QImage,QString也採用了該機制——這保證這些類在以傳值方式進行傳遞時有很高的效率,不管是做爲參數仍是函數返回值。
在Qt提供的implicit sharing機制下,對vector或list執行只讀操做時,採用at()而不是[]運算符是一個更好的選擇。
相似的,儘量的使用constBegin()和constEnd()以免沒必要要的拷貝操做。
foreach syntax
foreach在進入循環體時自動複製容器的副本並在此副本上進行迭代,所以若是迭代過程當中有經過迭代器對容器的修改操做的話,並不會影響循環的進行,循環結束後容器的內容也不會發生變化。
固然,若是在foreach循環中直接使用[]運算符對容器進行寫操做的話,容器內容天然會發生變化。
foreach中支持break和continue語句
11.2 Associative Containers
QMap
QMap中的key-value對是升序排列的
插入和刪除操做中均可以使用[]運算符,其下標爲key;爲避免建立沒必要要的空值,推薦用vlaue()而不是[]從QMap中取值。
QMap<K,T>中的K和T除了要求具有默認構造函數、拷貝構造函數和賦值運算符外,K還必須支持operator <,由於這樣才能實現前面提到的升序排列。
keys() & values()
QMap的特性是單值;QMultiMap<K,T>則支持同一關鍵字下多值的存在,插入操做由insertMulti()完成
QHash
QHash提供的接口和QMap很類似
QHash<K,T>中的K要符合的額外要求:支持operator ==,而且K可用全局函數qHash()來計算hash value
QHash一般是單值的,而QMultiHash則經過insertMulti()支持多值插入。
QCache<K,T> & QSet<K>
遍歷associative containr的最簡單方法是使用Java-style的迭代器
foreach syntax也可用於assocaitive container
11.3 Generic Algorithms
頭文件<QAlgorithms>中聲明瞭一組全局模板函數,用於實現做用於容器的基本算法;多數算法都經過STL-style的迭代器來完成。
STL頭文件<algorithm>中的函數,便可做用於Qt容器,也可做用於STL容器。
qFind(),qBinaryFind(),qFill(),qCopy(),qSort(),qStableSorg(),qDeleteAll(),qSwap()
須要注意的是,qDeleteAll()只對包含指針的容器有意義,該函數將釋放全部對象,但並不刪除容器中的指針。
11.4 Strings,Byte Arrays,and Variants
QString,QByteArray和QVariant這三個類和容器類有不少類似之處,在某些場合下可做爲容器類的替代品;和容器類同樣,這三個類也應用了implicit sharing 機制
QString
QString中存放的是16-bit的Unicold值。
從概念上,QString能夠當作是QVector<QChar>。
QString提供+/+=運算符以及append()函數用於合併字符串
QString提供的sprintf()函數,與C++標準庫中的sprintf支持相同的參數格式。
QString提供的arg()函數,是比sprintf()更好的選擇,由於它保證類型安全,支持unicode。
QString::number(): number->string
QString::setNum(): number->string
反向的轉換接口:toInt(),toLongLong(),toDouble()等,這些函數都有一個可選參數——bool類型的指針,若轉換失敗則將該bool變量置爲true,不然置false.
mid()函數返回指定區間內的子串;left()和right()函數則分別返回左子串和右子串
QString的indexOf()函數可用於文本匹配,返回所匹配子串的起始下標;匹配失敗時返回值爲-1。
startsWith()和endsWith()函數可用於判斷字符串的首部和尾部是否符合某種模式。
QString在進行比較時是大小寫敏感的;當所比較的字符串是用戶可見時,使用localeAwareCompare()一般是正確的選擇。
toLower() & toUpper()
replace() & remove() & insert()
trimmed() & simplified()——這兩個函數用於消去字符串中的whitesapce(spaces,tabs,newlines等)
QString::split()將一個字符串分割爲子串,並返回由這些子串們組成的一個QStringList。
QStringList中的全部元素能夠經過join()函數組成一個新的字符串,join()的參數在合併時會被插入相鄰元素中間。
在大多數狀況下,由const char * 至QString的轉換是自動的。
反方向的轉換,可經過toAscii()或toLatin1()來完成;這些函數返回一個QByteArray,其能夠經過QByteArray::data或QByteArray::constData()來轉換爲const char * ;
爲了簡化轉換,Qt提供了一個qPrintable()宏用於完成與toAscii()以及constData()相同的操做。
QByteArray
QByteArray提供的API與QString的很類似。
QByteArray的用處在於存儲原始2進制數據及8-bit編碼的字符串。
一般選擇QString而不是QByteArray來存儲文本信息,由於QString支持Unicode。
QByteArray會自動在最後一個元素以後補上‘\0',這樣使得將QByteArray傳遞給須要const char *的函數變得很容易。
QByteArray支持'\0',容許存儲任意的2進制數據。
QVariant
QVariant類可用於存放不少Qt類型的值,而且還能夠存放容器。
QVariant在 item view class,database model 和QSetting中被普遍的使用着。
利用QVariant和嵌套,能夠建立很是複雜的數據結構。
QVariant的便利性是以性能和代碼的可讀性爲代價的。
QVariant被Qt的meta-object system所使用,所以是Qtcore module的組成部分。
QVariant也能夠支持用戶自定義的數據類型,前提是該類型具備defalut constructor和copy constructor。要實現對用戶自定義類型的支持,須要使用宏Q_DECLARE來註冊該類型。
全局函數:qVariantFromValue(),qVariantValue<T>(),qVariantCanConvert<T>()
Chapter 12 Input/Output
Qt經過QIODevice類,對支持塊讀寫的設備進行了強有力的抽象和封裝,從而提供了良好的I/O支持。
Qt中提供的QIODevice的子類包括QFile,QTemperorayFile,QBuffer,QProcess,QTcpSocket和QUdpSocket.
QProcess,QTcpSocket和QUdpSocekt屬於sequential device,即數據只能被訪問一次,且只能按照順序讀取;而QFile、QTemporaryFile和QBuffer屬於random-access device,數據能夠被訪問任意次,且能夠從任意位置開始讀取;
除了以上的device class,Qt還提供了兩個可用於讀寫任何設備的高層數據流類:用於二進制數據的QDataStream和用於文本的QTextStream。這兩個類負責處理字節序和文本編碼等問題,保證運行在不一樣平臺或不一樣國家的Qt程序能正確讀取彼此的文件。這使得Qt的I/O類比起C++標準庫中的I/O類更方便——它將這些問題留給了程序員來處理。
QProcess容許程序員調用外部程序並經過標準輸入流、標準輸出流和標準錯誤流與其進行通信。默認狀況下,進程之間的通信是異步的,但也能夠在某些操做上阻塞。
12.1 Reading and Writing Binary Data
Qt中載入和保存二進制數據最簡單的方法就是使用QFile來打開文件,並經過QDataStream對象來訪問文件內容。
QDataStream對象的version number直接影響着Qt中的數據類型以何種方式表示和記錄。C++的基本類型保證老是以同一種方式表示和記錄,不受verson number的影響。
在使用QDataStream時,須要保證在讀文件和寫文件時使用相同的number version。
若QDataStream被用來單純讀寫C++基本類型的數據,那麼沒有必要調用setVersion()來設定version number
QDataStream的默認字節序是big-endian,這能夠經過調用setByteOrder()改變。
使用Qt時,一般沒有必要顯式執行關閉文件的操做,由於QFile在銷燬的時候會自動執行文件的關閉操做。
能夠調用flush()來強制完成數據的寫操做。
QDataStream存儲數據的方式能保證能夠無縫的將其再次讀出,例如,一個QByteArray對象的存放形式是一個32-bit的計數值後跟數據自己。
可使用readRawData()和writeRawData()來讀寫原始字節。
使用QDataStream讀數據時的錯誤處理是很簡單的,status()函數返回當前狀態,包括QDataStream::Ok、QDataStream::ReadPastEnd和QDataStream::ReadCorruptData。
當有錯誤發生時,>>運算符的返回值老是0值或是空值。
爲自定義類型提供>>和<<運算符有不少好處:能夠對包含該自定義類型的容器使用QDataStream;能夠將該自定義類型的數據經過QVariant保存起來,這須要先調用qRegisterMetaTypeStreamOperators<T>()宏。
若是但願一次性讀/寫文件,能夠放棄QDataStream與QIODevice子類的聯合使用,而用QIODevice提供的write()和readAll()接口來直接進行讀寫操做。
QIODevice()提供的peek()函數返回下一個待讀取的數據,而不改變讀寫指針,該接口對於random-access device 和sequentail device 都適用。
對於random-access,可使用seek()來指定讀寫指針的位置。
12.2 Reading and Wrinting Text
Qt提供了QTextStream來讀寫文本格式的文件,如plain text,HTML,XML,source code等。
QTextStream負責完成Unicode與系統本地編碼之間的轉換,並自動處理不一樣操做系統之間換行符不一樣表示方式上的轉換(如在windows下是\r\n,在linux是\n)。此外還自動完成C++基本數字類型與字符串之間的相互轉換。
QTextStream使用QChar做爲基本的數據單元。
寫文本數據很是容易,然而讀文本則可能很是具備挑戰性,由於文本數據在本質上是具備歧義性的。
默認狀況下,QTextStream使用本地編碼來進行讀寫操做;能夠調用setCodec()改變之,如stream.setCodec("UTF-8");
Qt仿照C++的I/O庫爲QTextStream也提供了stream manipulators。
同QDataStream同樣,QTextStream在一個QIODevice之上進行操做;除此以外還能夠在QString上進行操做,這種狀況下不須要爲流設置編碼格式,由於QString老是Unicode。
若是隻讀寫ASCII字符集和Latin-1字符集的文件,能夠直接使用QIODevice提供的API進行讀寫,而不用使用QTextStream。一般這並非一個好主意,由於這不利於國際化和後期維護。
若是真的須要直接向QIODevice寫文本,在使用open打開該QIODeveice時必須指定QIODevice::Text標誌;該標誌的做用在於告知QIODevice,在寫文本數據時,在windows平臺上將全部\n轉換爲\r\n;而在讀文本數據時,在全部平臺上忽略\r。
12.3 Traversing Directories
QDir提供了一種平臺無關的方法用於獲取文件信息以及遍歷目錄。
QDir::entryList() 其第二個參數代表要讀取目錄下的那些條目——文件(QDir::Files)仍是子目錄(QDIr::Dirs)等。
QDir::separator() 返回當前平臺上的目錄分割符QDir在全部平臺上都將'/'視爲目錄分割符,在windows平臺上還額外的識別'\'。
QDir::currentPath() 返回程序當前目錄的絕對路徑
QDir::homePath() 返回用戶的主目錄路徑
QFileInfo類容許程序員獲取文件的屬性信息,如大小,訪問權限,全部者及各類時間戳等。
12.4 Embedding Resources
Qt容許在程序的可執行文件中嵌入二進制或文本文件,這是經過Qt的資源機制實現的。
12.5 Inter-process Communication
QProcess類容許運行外部程序並與其通信。該類異步工做,在後臺完成相應的工做來保證UI對用戶操做的正常響應,在外部程序終止或有數據產生時emit signal來通知本程序。
QProcess::start()用於傳遞必要的參數,並啓動外部程序。
靜態函數QProcess::execute()會運行一個外部程序並在外部程序結束以前保持阻塞狀態。
QTemporaryFile::open()在無參數時以讀寫模式打開文件。
QTemporaryFile在對象生存期結束時會自動刪除臨時文件。
當QProcess以同步模式使用時,不須要創建signal-slot鏈接。
若是須要比靜態函數execute()精度更高的控制,能夠改用下面的方法:
首先建立一個QProcess對象,而後對其調用start(),以後調用QProcess::waitForStarted()強制進入阻塞狀態,直至外部程序順利啓動爲止,以後再調用QProcess::waitForFinished(),阻塞直至外部程序結束爲止。
Chapter 13 Databases
QtSql模塊提供了一個平臺無關、數據庫無關的訪問SQL數據庫的接口。
Qt中的每一個數據庫鏈接用一個QSqlDatabase對象來表示;Qt使用不一樣driver來和各類不一樣數據庫的API進行通信。
QSqlQuery提供了直接執行任意SQL語句的特性;此外還提供了兩個高層次的無需SQL命令的數據庫接口:QSqlTableModel和QSqlRelationalTableModel
13.1 Connecting and Querying
在執行SQL命令前,必須先創建好同數據庫的鏈接。
靜態函數QSqlDatabase::addDatabase()用於建立一個新的QSqlDatabase對象,函數的第一個參數指定了Qt該選擇哪一個Driver來訪問數據庫。
對QSqlDatabase對象設定好host name,database name ,username和password後,須要調用open()函數創建到數據庫的鏈接。
一旦到數據庫的連接創建好後,就能夠經過QSqlQuery::exec()來執行底層數據庫所支持的任意SQL語句了。
QSqlQuery::next()返回查詢結果集中的下一行,而QSqlQuery::value()則返回當前行中的某一項的值,以QVariant的形式返回。
可使用QSqlQuery::isActive()來檢查SQL語句的執行是否出現錯誤。
Placeholder
QSqlQuery::prepare()
QSqlQuery::bindValue() or QSqlQuery::addBindValue()
QSqlQuery::exec()
Qt支持數據庫中transaction(事務)這個概念。transaction()用於啓動transaction,而commit()或rollback()用於結束transaction。
靜態函數QSqlDatabase::database(),返回指定鏈接所對應的QSqlDatabase對象。
QSqlDatabase::driver() 返回該鏈接底層所使用的dirver
QSqlDatabase::hasFeature()可用來查詢底層數據庫是否支持某項特性。
Qt容許在一個程序中建立多個數據庫鏈接,這種狀況下在執行SQL語句時,須要爲QSqlQuery對象的構造函數傳入要執行該語句的數據庫對應的QSqlDatabase對象。
與QSqlQuery相比,QSqlTableModel提供了一個更高層次、更抽象的接口,能夠避免使用原始的SQL命令。
QSqlTableModel::record() & QSqlTableModel::value()
QSqlTableModel::insertRow() & QSqlTableModel::setData()
QSqlTableModel::submitAll() ,與其餘model不一樣,在使用QSqlTableModel時,必須調用submitAll()來強制全部的修改都寫入數據庫。
當須要處理外鍵foreign key時,須要使用QSqlRelationalTableModel而不是QSqlTableModel。
對於使用了SQL相關類的應用程序,須要在對應的.pro中添加下面一行:"QT +=sql",這樣在連接時會將QtSql庫鏈入。
Chapter 14. Networking
14.1 Wrinting FTP Client
QFtp是Qt提供的封裝了ftp協議的一個類。
Qftp的所執行的操做是異步完成的,這保證了FTP命令在執行過程當中UI處於可相應狀態。
當程序不須要鏈入QtGui庫時,能夠在main()中建立QCoreApplication對象而不是QApplication對象。
QCoreApplication::argurments() 函數以QStringList的形式返回程序的命令行參數,其中第一個參數爲程序名,而且全部與Qt相關的參數例如-style都已經被移除掉了。
QUrl是Qt提供的一個用於從url中提取各類信息的一個高層接口。
提交的FTP命令被排隊,並在Qt的event loop中被執行;QFtp對象在它處理完全部請求後會emit done(bool) 這個signal,其中類型爲bool的參數代表是否有錯誤發生。
QFtp所封裝的Ftp命令包括 connectToHost(),login(),close(),list(),cd(),get(),put(),remove(),dir(),mkdir(0,rmdir() 和rename()。 這些函數都返回一個標記命令的ID。
TransferMode()可用於改變傳輸模式,默認爲passive;TransferType()用於改變傳輸類型,默認爲binary。
此外,還能夠經過rawCommand()來執行任意標準ftp命令,如 ftp.rawCommand("SITE CHMOD 755 fortune");
QFtp在開始執行每條FTP命令時都會emit commandStarted(int)這個signal,並在每條FTP命令完成時emit commandFinished(int,bool) 這個signal,其中int 參數是命令對應的ID,而bool參數則表示是否有錯誤發生。
QFtp在每當ftp鏈接的狀態發生改變時會emit stateChanged() 這個signal,這裏的狀態包括QFtp::connecting、QFtp::connected、QFtp::LoggedIn等。
每當命令隊列變爲空時,QFtp都會emit done(bool)這個signal。
當有錯誤發生時,Qftp自動將命令隊列清空。
若要使用QFtp,須要在項目的.pro文件中添加下面一行:" Qt +=network"
listInfo(),每當QFtp執行list()命令獲得一個目錄項時,都會emit 這個signal
QFtp的get()函數在調用時能夠不給出要寫的設備,這種狀況下QFtp會在有新數據可用時emit readyRead()這個signal ,程序員能夠調用read或readAll()這兩個接口用於讀數據。
14.2 Writing HTTP Clients
與Ftp協議相對應,Qt爲Http協議提供了QHttp類。
QFtp和QHttp在接口和特性上有不少類似之處。
QHttp一樣是異步工做模式。
QHttp在開始執行request命令時emit requestStarted(int)這個signal,而在request操做結束時emit requestFinished (int,bool)這個signal,其中int參數和bool參數的含義與QFtp中的相似。 當有錯誤發生時,請求隊列被自動清空。與QFtp相同,QHttp也提供了readyRead()信號和read()、readAll()這兩個接口函數。
14.3 Writing TCP Client-Server Applications
QTcpSocket和QTcpServer這兩個類用於編寫Tcp客戶端和服務端
基於TCP的應用程序或者是line-oriented,或者是block-oriented。
QTcpSocket經過對QAbstractSocket的繼承而成爲QIODevice的子類,於是可使用QDatatStream或QTextStream來對其進行讀寫。
QTcpSocket在有數據可讀時會emit readyRead()這個signal。
Qt中提供的forever syntax等同於 for( ; ; )
QTcpServer類容許接受外來TCP鏈接,每當檢測到外來TCP鏈接請求時,會自動調用QTcpServer::incomingConnection()函數,參數爲標識socket ID的int型變量。
QTcpSocket::listen()用於完成監聽工做。
QTcpSocket提供的canReadLine()和readLine()這兩個函數對於line-oriented應用程序提供了很大的便利。
14.4 Sending and Receiving UDP Datagrams
不一樣於QTcpSocket,QUdpSocket不支持主機名而只支持主機地址。
將主機名轉換爲IP地址,有兩種選擇,一是使用靜態函數QHostInfo::fromName(),該函數是阻塞的;二是使用靜態函數QHostInfo::lookupHost(),該函數是非阻塞的。
QUdpSocket::bind()
readyRead() signal
QUdpSocket將收到的datagram排隊,並容許客戶一次訪問一個datagram。
Chapter 15 XML
Qt中的QtXml模塊提供了兩組不一樣的API用於讀取XML文檔
SAX(Simple API for XML):經過virtual function直接嚮應用程序報告"parsing event「。
DOM(Document Object Model):將XML文檔轉換爲樹型結構。
SAX接近底層,速度更快;DOM更便於使用。
15.1 Readin XML with SAX
SAX是事實上的讀取XML文檔的標準API,Qt中的SAX類仿照了Java中的SAX2的實現。
Qt提供了一個SAX-based的non-validateing類型的XML解析器QXmlSimpleReader。解析器在讀取文檔時,會調用其註冊的handler class中的virtual function。
Qt爲QXmlSimpleReader提供了若干handler class,對於大多數應用,只須要使用QXmlContentHandlerr和QXmlErrorHandler。
Qt提供了一個對程序員很方便的QXmlDefaultHandler,它對全部的handler class都進行了派生,並對全部virtual function提供了簡單的實現。
要使用QtXml庫,須要在.pro文件中加入下面一行: "QT +=xml"
15.2 Reading XML with DOM
DOM是由W3C制訂的解析XML的標準API,Qt提供了一個non-validating類型的DOM level 2級別的實現,可用於XML文檔的讀寫以及其餘操做。
DOM在內存中將XML文件表示爲樹的形式。
QDomDocument::setContent(),設定要讀取的XML文檔。
15.3 Writing XML
Qt中存在兩種生成XML文檔的方法
1). 構建一個DOM tree,並對其調用save()
2). 手動輸出XML格式
兩種方式之間的選擇獨立於讀取XML文檔時選擇SAX或是DOM
默認狀況下,QDomDocument::save()生成文檔時採用UTF-8編碼格式。
Chapter 16 Providing Help
QWidget::setToolTip()用於爲Widget設置相應的tip文本。 一樣,QAction::setToolTip()爲Action設置相應的tip文本;若沒有顯式的爲Action設置tip文本,Action會自動的使用action text。
setStatusTip(),該函數爲Widget和Action添加 status tip。
QTextBrowser類能解析大量HTML標籤,可用於顯示基於HTML的文本內容
Qt Assistant支持索引和文本搜索功能,能夠很好的用於提供在線幫助
要使用Qt Assistant,必須在程序中書寫必要的代碼讓Qt Assistant能察覺到文檔的所在。
Qt程序與Qt Assisant之間的通信是由QAssistantClient這個類來負責的;該類屬於一個單獨的類庫,要使用該類庫,須要在.pro中添加下面一行: CONFIG +=assistant
QAssitantClient類的構造函數以文件路徑做爲首個參數,用於肯定 Qt Assistant可執行文件的位置。
Chapter 17 Internationalization
Qt4爲國際化內置了不少良好的支持:
1). Qt的API接口和內部實現均是基於Unicode的
2). Qt的文本引擎支持全部的non-Latin的書寫系統,包括阿拉伯、中日韓、希伯來、印度、泰國等。
3). Qt的layout 引擎爲right-to-left風格的阿拉伯文和希伯來文提供了相應的支持。
4). 某些語言在輸入文本時須要使用專用的輸入法,QLineEdit和QTextEdit能夠和系統中安裝的任意輸入法協調工做。
Qt提供了用於文本翻譯的GUI工具:Qt Linguist,以及兩個輔助命令行程序 lupdate和lrelease。
大多數程序都是在啓動階段就根據用戶的locale setting加載合適的translation file,然而某些狀況下用戶要求可以在運行時實時的切換界面語言。
17.1 Working With Unicode
QString以Unicode來存儲字符串,QString中的每一個字符都是一個16-bit的QChar而不是8-bit的char。
對QString中的某個位置進行賦值操做,能夠經過字符方式來肯定新值,也能夠經過數值方式來肯定,例如要將類型爲QString的str的首字符設爲'A',能夠有下面兩種方式:
str[0]='A';
str[0]=QChar(0x41);
基於QChar之上的編程不一樣於基於char。要得到一個QChar變量的編碼值,對其調用unicode()函數;要得到一個QChar變量對應的 ASCII或Latin-1編碼值,對其調用toLatin1(),若原來QChar中存放的是non-latin字符,該函數返回'\0'。
Qt爲QChar類提供了基於Unicode的判斷函數,如isPrint(),isSpace(),isLetter(),isNumber()等,其工做與C++標準庫提供的isalpha(),isdigit(),isspace()相似。
Qt負責將Unicode編碼的QString正常顯示,並在須要和其餘系統進行通信時轉換爲相關的編碼格式。
默認狀況下QTextStream使用系統本地的8-bit編碼格式(可經過調用QTextCodec::codecForLocale()查看)讀寫文本文件,對於美國和西歐,這一般意味着使用Latin-1。
能夠調用QTextStream::setCodec()來自定義讀寫文件時所使用的編碼,如 stream.setCodec("UTF-16");
UTF-16格式與QString的內存表示一致,所以使用UTF-16讀寫Unicode字符串速率會很高,缺點是在存儲純ASCII數據時會有較大的開銷。
setCodec()的參數是一個合適的QTextCodec對象,由其負責完成Unicode和本地編碼之間的轉換。
QTextCodec::codecForName():根據參數中給出的編碼名稱返回對應的QTextCodec對象。
在讀文本文件時,默認狀況下QTextStream可以自動檢測Unicode編碼(依據是0xFFFE,這個Unicode byte order mark);能夠經過調用setAutoDetectUnicode(false)來關閉該特性。
默認狀況下,Qt將傳遞給函數tr()的參數視爲Latin-1編碼的字符串;能夠調用靜態函數QTextCodec::setCodecForTr()來改變該默認設置,而自定義編碼方式。注意,這必須在第一次調用tr()以前就完成。
然而即使如此,代碼中的其它顯式字符串仍會被解釋爲Latin-1字符串;解決方法之一是利用QTextCodec對象的toUnicode()函數,也能夠調用QText::setCodecForCStrings()來告知Qt在const char*與QString之間進行轉換時採用何種編碼。
17.2 Making Applications Translation-Aware
要實現程序的多語言化,須要完成兩件事情:
1). 確保程序中每一個用戶可見的字符串都被tr()處理。
2). 確保程序啓動時加載translation file(.qm)。
tr()是在QObject中定義的靜態函數,而且在每一個使用了Q_OBJECT宏的子類都被overridden。
tr()返回字符串的一個翻譯版本,若是存在的話;不然將輸入參數原樣返回。
要準備transaltion file,須要使用Qt提供的lupdate,該工具將代碼中全部出如今tr()中的可見字符串提取出來並生成待翻譯的translation file,這樣的translation將發送給翻譯者來完成翻譯工做。
調用tr()函數的通常形式爲
Context::tr(sourceText,comment);
其中Context是tr()所屬的類,comment是可選參數,用於爲翻譯者提供附加信息。
當在一個全局函數中調用tr()時,必須顯式的指明相應的Context(類)。
QApplication::translate() 函數完成與tr()函數相同的工做。
tr()和QApplication::translate()完成雙重工做:一方面供lupdate從中提取用戶可見字符串,另外一方面從translation file中提取字符串的對應翻譯。
儘管對一個字符串變量而不是字符串常值調用tr()並非個好主意,然而實際上也是能夠做到的,這須要在將字符串常值賦值給某個字符串變量時調用QT_TR_NOOP()宏,該宏不進行任何操做,只是爲lupdate提供標識。宏QT_TRANSLATE_NOOP完成一樣的工做,不一樣之處在於參數中能夠指定context,這對於初始化類外變量頗有用。
如何能確保程序員在編寫代碼時將全部用戶可見字符串用tr()包裹起來而不出現遺漏呢?能夠經過在包含任何Qt頭文件以前定義預約義符號 QT_NO_CAST_FROM_ASCII這個來告訴Qt禁止從const chat * 到QString的自動轉換;一般在.pro文件中添加下面一行:
「DEFINES +=QT_NO_CAST_FROM_ASCII"來實現這必定義。
這樣就強迫每一個字符串常量在使用時必須被tr()或QLatin1String()函數包裹從而轉換爲QString,這取決於該字符串常量是否須要被翻譯。
靜態函數QLocale::system()返回一個QLocale對象,以提供用戶的locale信息。
QTranslator對象只能一次加載一個translation file;經過使用多個QTranslator對象,Qt程序能夠安裝任意數量的translator;
QApplication在尋找合適的翻譯時會使用全部已安裝的QTranslator。
QTranslator::load()用於加載translation file (.qm)
QApplication::setLayoutDirection(),該函數能夠改變文本的書寫方向。
QLocale類提供本地化的數字和日期/時間格式。
17.3 Dynamic Language Switching
當Qt檢測到環境變量中的locale設置發生變化時,會建立一個LocalChange event;若要處理該event,須要從新實現QWidget::changeEvent()。
當QApplication上已安裝的QTranslator的內容發生變化時,Qt會建立一個LanguageChange event。
不要將LocalChange和LanguageChange兩個event混淆;前者的發生是由系統變化致使的,所通知的對象是Qt應用程序;後者的產生是Qt自身致使的,所通知的對象是程序的widgets。
17.4 Translating Applications
翻譯的三個步驟:
1). 開發人員運行lupdate程序從源代碼中提取用戶可見字符串,並建立XML格式的.ts文件
2). 翻譯人員運行Qt linguist這個GUI工具,完成文本的翻譯工做
3). 開發人員運行lrealease這個工具,生成應用程序能夠經過QTranslator加載的二進制.qm文件
lrelease的工做就是將可讀文本的.ts文件轉換爲二進制的.qm文件。
lupdate默認狀況下假設全部tr()中的字符串都採用Latin-1編碼;若實際狀況並不是如此,須要在.pro文件中添加CODECFORTR這一項;該工做和在程序中調用QTextCodec::setCodecForTr()兩者是缺一不可的。
Chapter 18 Multithreading
18.1 Creaing Threads
Qt中提供多線程的機制很簡單:建立QThread的派生類,並從新實現其保護成員函數run()。
QThread::run(),被調用來開始線程的執行,在run()結束時線程終止。
QThread::terminate(),用來終止線程的執行,非阻塞操做,並不保證線程的當即終止;能夠在調用QThread::terminate()以後調用QThread::wait()來實現同步等待。
terminate()並非值得推薦結束線程的方法,由於它強制線程終止而不給線程任何清場的機會。
18.2 Synchronizing Threads
Qt中用於線程同步的類包括QMutex,QReadWriteLock,QSemaphore和QWaitCondition
QMutex
QMutex::lock() 阻塞操做
QMutex::trylock() 非阻塞操做
QMutex::unlock()
QMutexLocker是Qt提供的用於簡化Mutex操做的一個類——QMutexLocker的構造函數以一個QMutex對象爲參數,並對其自動執行lock操做;而在析構函數則對其自動執行unlock操做。
QReadWriteLock能夠容許同時進行多個讀操做或一個寫操做。
QReadWriteLock::lockForRead()
QReadWriteLock::lockForWrite()
QReadWriteLock::unlock()
QSemaphore是對Mutex的擴展;與讀寫鎖不一樣的是,信號量能夠用來保護一批相同的資源,而不僅是一個。
QSemaphore::acquire(int n=1)
QSemaphore::release(int n=1)
QSemaphore::available()
QWaitCondition和QMutex聯合使用,能夠容許一個線程在某個條件知足時喚醒其餘線程,比起單獨使用QMutex能實現更精確的控制。
QWaitCondition::wait()的參數是一個狀態爲locked的QMutex,該函數在阻塞本線程前會將這個QMutex解鎖,並在函數返回前對其lock。
TLS(thread-local storage)
較好的實現方法是使用QThreadStorage<T>類,該類經常使用來實現cache,這樣能夠避免使用mutex時lock,unlock以及等待帶來的開銷。
因爲某些編譯器的問題,QThreadStorage<T>中只能存放指針。
QThreadStorage::hasLocalData()
QThreadStorage::setLocalData()
18.3 Communicating with MainThread
當Qt程序運行時,主線程是惟一的線程,而且是惟一容許建立QApplication或QCoreApplication對象並對其調用exec()的線程。在調用exec()以後,主線程要麼是在等待event的發生,要麼是在處理一個event。
主線程能夠經過建立QThread的子類來開始新線程。
以前介紹的mutex,read/write lock,semaphore等都可用於新線程之間的通信,可是卻不能用於和主線程的通信,由於這會致使主循環的event loop被阻塞並"凍結"UI。
解決方案是在主線程與新線程之間跨線程的使用signal-slot機制。
一般狀況下signal-slot機制是同步工做的,這意味着當signal被emit時,與之想聯繫的slot會被當即調用。
然而,當該機制用於將不一樣線程中的object鏈接起來時,則變爲異步機制。這樣的鏈接是在底層是經過建立並傳遞event來實現的;slot被signal的接收對象所在的線程的event loop所調用。
默認狀況下,一個QObject對象存在於其被建立的線程之中;這能夠在任什麼時候候調用QObject::moveToThread()被改變。
18.4 Using Qt's Classess in Secondary Threads
Thread-safe & Reentrant 注意留意這兩個概念應用在函數和類之上的不一樣。
對於類,若是它的全部成員函數均可以被不一樣線程同時調用而不相互影響——即便這些調用是針對同一個類對象,那麼該類被定義爲thread-safe。
對於類,若是其不一樣實例能夠在不一樣線程中被同時使用而不相互影響,那麼該類被定義爲reentrant;然而,不一樣線程中同時訪問同一個reentrant類對象,並非安全的,這樣的訪問須要用mutex進行保護。
在Qt的定義中,在類這個層次,thread-safe是比reentrant更嚴格的要求,這和在函數層次上的關係正好相反。
一般狀況下C++的類只要不使用全局或其它共享變量,就是reentrant的。
Qt中大多數non-GUI的類,是屬於reentrant的。QObject是reentrant的,可是須要注意如下幾點:
1). 子QObject必須在父QObject所屬的線程中被建立,這意味着在非主線程中的對象在建立時不容許以QThread做爲parent,由於後者是在主線程或另一個非主線程中被建立的。
2). 在刪除一個QThread對象前,必須將對應線程中建立的全部對象都銷燬。
3). 對象必須在其被建立的線程中被刪除。
若是須要刪除存在於另外一個線程中的對象,必須調用線程安全的QObject::deleteLater()函數,該函數會發送一個"defered delete" event。
QWidget及其子類不是reentrant的。
Chapter 19 Creating Plugins
Qt提供了Qlibrary類,用於以一種平臺無關的方式實如今程序運行時加載共享庫。
19.1 Extending Qt with Plugins
Qt自己能夠被不少類型的plugin擴展,最多見的包括database drivers,image formats,text codecs等。
對於每種類型的plugin,一般都須要兩個類:一個wrapper類實現該類通用的plugin API,以及一個或多個handler類,每一個類實現一個plugin特定的API。
在plugin的.cpp文件中,須要使用Q_EXPORT_PLUGIN()這個宏來確保plugin可以被Qt識別。
plugin真正執行的操做都是經過其handler類來實現的。
plugin的.pro文件與應用程序不一樣。默認狀況下.pro文件使用app模板,然而這裏必須使用lib模板,由於plugin屬於庫,而不是一個獨立的應用程序。
QCoreApplication::addLibraryPath( ),爲程序添加新的庫路徑。
19.2 Making Application Plugin-Aware
應用程序的plugin實際是實現了一個或多個接口(interface)的動態庫。應用程序與plugin之間的通信是經過interface的virtual table來完成的。
一個接口(interface)一般聲明一個virtual析構函數,一個返回QStringList的virtual函數,以及一個或多個其餘virtual函數。
在接口聲明的尾部,須要調用Q_DECLARE_INTREFACE2()來將該interface與某個標識符關聯起來。
QPluginLoader類用於在運行時加載plugin。
QPluginLoader::load(),一般不須要顯式調用,由於instance()函數會在必要時調用該函數完成加載。
QPluginLoader::instance(),返回一個指向plugin對象的QObject *指針。
同一個插件plugin能夠成功cast至多個interface,由於plugin能夠經過多重繼承來提供多個interface。
19.3 Writing Application Plugins
應用程序的plugin是其要提供的interface和QObject兩者的子類。
在plugin的源代碼中,須要爲其提供的每一個Interface都要使用Q_INTERFACES()宏,來保證moc和qobject_cast<T>之間的協調工做。
在.cpp文件的尾部,一樣須要調用Q_EXPORT_PLUGIN2()宏來使該plugin對於Qt可用。
Chapter 20 Platform-Specific Features
20.1 Interfacing with Native APIs
在每一個平臺上,Qt都爲QWidget提供了一個winId()函數,返回window ID或是句柄;QWidget還提供了一個靜態函數find(),返回一個特定window ID對應的widget。咱們能夠將得到的window ID傳遞給Native API來執行平臺特定的操做。
Qt定義瞭如下系統標誌:Q_WS_WIN,Q_WS_X11,Q_WS_MAC,Q_WS_QWS(Qtopia)。
QSysInfo::WindowsVersion QSysInfo::MacintoshVersion 這兩個靜態變量存儲着WIN和MAC操做系統的版本信息
20.2 Using ActiveX on Windows
ActiveX構建於Microst COM之上,它爲使用組件的應用程序定義了一套接口,爲提供組件的庫和應用程序定義了另外一套接口。
ActiveQt由兩個模塊組成:
QAxContainer模塊容許用戶使用COM object並在Qt程序中內嵌ActiveX控件。
QAxServer模塊容許用戶導出自定義的COM object以及用Qt編寫的ActiveX控件。
Q_ENUMS()宏的做用是告知moc其"宏參數"是枚舉類型。
QAxContainer模塊由三個類組成:QAXObject封裝一個COM object,QAxWidget封裝一個ActiveX控件,QAxBase爲QAxObject和QAxWidget實現核心COM功能。
QAxObject派生自QAxBase和QObject,QAxWidget派生自QAxBase和QWidget。
COM中的數據類型會被自動轉換爲合適的Qt數據類型。
QAxBase::setControl()
QObject::setProperty()可用於設置COM property和Qt property。
要連接QAxContainer庫的話,須要在.pro文件中添加下列一行:"CONFIG +=qaxcontainer"
QAxBase::dynamicCall()
注意,QAxObject和QAxWidget的子類沒法定義新的property,signal和slot。
QAxServer模塊容許將一個標準Qt程序轉換爲一個ActiveX server。該server能夠是共享庫,也能夠是獨立的應用程序。共享庫形式的server被稱爲in-process servers,而獨立應用程序形式的server被稱爲out-of-process server。
QAxBindable在widget與ActiveX client之間提供了一個接口。
在Qt中處理多重繼承中,若是基類中存在QObject的派生類,必須將這樣的類放在首位。
QAXFACTORY_DEFAULT()宏的做用是導出一個AxtiveX控件,能夠用於僅導出一個控件的ActiveX server;當server要導出多個控件時,不能使用QAXFACTORY_DEFAULT()宏。
QApplication可以識別命令行中的-activex參數,並使應用程序做爲server而運行。
Q_CLASSINFO()宏
20.3 Handling X11 Session Management
爲了使一個Qt/X11應用程序意識到session manager的存在,須要從新實現QApplication::saveState()函數,並在該函數中保存應用程序的狀態信息。
當用戶啓動shutdown操做時,程序員能夠經過從新實現QApplication::commitData()來獲取控制權,這容許應用程序保存未保存的數據,而且與用戶交互——若是可能的話;這部分session management在X11和Windows上都被支持。
QObject::setObjectName()
void QApplicatoin::saveState(QSessionManager &)——該函數在session manager但願應用程序保存其狀態時被調用,QSessionManager類型的參數容許應用程序與session manager進行通信。
discard command:是指session manager必須執行的用刪除任何存儲當前狀態信息的命令。
restart command:是指session manager必須執行的用以從新啓動應用程序的命令。
QSessionManager::setDiscardCommand(QStringList &)
QSessionManager::setDiscardCommand(QStringList &)
默認狀況下,Qt提供的restart command的格式爲: appname -session id_key
QSessionManager::release()
QSessionManager::cancel()
QApplication:isSessionRestored()