讀書摘要--《C++.GUI.Programming.with.Qt.4》chapter4~10

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::EditRoleQt::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_OBJECTQ_PROPERTY()這兩個宏都是必備的。

      QImage類以一種硬件無關的方式存儲圖像信息。

      Qt中提供了兩個類型用於存儲色彩信息:QRgbQColor

      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 approachplugin 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包括:QHBoxLayoutQVBoxLayoutQGridLayoutQStackLayout

      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::DisplayRoleQt::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進行初始化。

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