QML編程入門

歡迎來到聲明式UI語言QML的世界.在本入門教程中,咱們使用QML建立一個簡單的文本編輯器.閱讀這個教程後,就可使用QMLQt C++開發應用程序了.html

安裝react

首先須要安裝包含Qt QuickQt最新版本,如今是Qt4.7.安裝教程包括安裝說明書和不一樣平臺的需求.app

Qt Quick包含一個叫作QML聲明式語言,Qt Declarative Module, QML Viewer.編輯器

QML構造用戶界面函數

咱們要構造的應用程序是一個簡單的文本編輯器,能夠加載,保存,以及執行一些文本處理.本教程包括兩個部分.第一個部分使用QML設計應用程序佈局和行爲.第二個部分中使用Qt C++實現加載和保存文本.應用Qt元對象系統(Qt's Meta-Object System)能夠將C++中的函數導入做爲QML元素的屬性進行訪問.利用QMLQt C++,可高效的將界面邏輯與應用程序邏輯解耦.工具

最終代碼見 examples/tutorials/gettingStarted/gsQml目錄.首先須要在examples/tutorials/gettingStarted/gsQml/編譯C++插件.將C++插件生成到QML文件可訪問的目錄中.佈局

要啓動文本編輯器,僅須要使用qmlviewer工具,幷包含一個QML文件名稱爲參數.本教程的C++部分假設讀者瞭解基本的Qt編譯過程.post

教程章節:字體

1.   定義按鈕和菜單Defining a Button and a Menu動畫

2.   實現菜單欄Implementing a Menu Bar

3.   建立文本編輯器Building a Text Editor

4.   美化文本編輯器Decorating the Text Editor

5.   使用Qt C++擴展QMLExtending QML using Qt C++

定義按鈕和菜單

基本組件按鈕

咱們構建一個按鈕做爲文本編輯器程序的開始.功能上,按鈕具備鼠標敏感區域和一個標籤(label).用戶點擊按鈕後執行一個動做.

QML,基本的可視項是Rectangle 元素. Rectangle 元素擁有控制外觀和位置的屬性.

import QtQuick 1.0

 

 Rectangle {

     id: simplebutton

     color: "grey"

     width: 150; height: 75

 

     Text{

         id: buttonLabel

         anchors.centerIn: parent

         text: "button label"

     }

 }

首先,  import QtQuick 1.0使qmlviewer工具導入咱們稍後須要的QML元素.這行代碼在每一個QML文件中都是必須的.注意導入語句中包含Qt模塊的版本號.

這個矩形包含一個惟一標識simplebutton,綁定到id屬性上. Rectangle 元素設置屬性值的方式爲:屬性名稱,後跟冒號,然後是值.本例中,顏色grey賦給了矩形的color屬性.一樣設置了矩形的widthheight屬性.

Text元 素爲不可編輯的文本框.將Text元素命名爲buttonLabel.要給Text元素設置字符串內容須要給其text屬性賦值.標籤包含在 Rectangle中,爲了讓其居中,設置Text元素的相對於父元素(simplebutton)的描點屬性.爲了讓佈局更加簡單,描點可與其餘項的描 點綁定.

將上面的代碼保存爲SimpleButton.qml. 使用這個文件名作參數啓動qmlviewer將看到帶有文本標籤的灰色矩形.

爲了實現按鈕的點擊功能,咱們能夠處理QML的事件.QML事件與Qt的信號槽機制相似.觸發信號時會調用與其鏈接的槽.

 Rectangle{

     id:simplebutton

     ...

 

     MouseArea{

         id: buttonMouseArea

 

         anchors.fill: parent //在矩形區域內描定Mouse Area的全部邊

                 //onClicked處理按鈕點擊事件

         onClicked: console.log(buttonLabel.text + " clicked" )

     }

 }

在simplebutton中包含一個MouseArea元素.MouseArea元素描述一個可檢測鼠標移動的交互區域.在按鈕中咱們將MouseArea徹底平鋪到其父對象simplebutton上.anchors.fill語法用來訪問叫作anchors的組合屬性中的fill屬性.QMl使用基於描點的佈局(anchor-based layouts)可將項描定到其餘項上,建立出強健的佈局.

當鼠標在MouseArea區域內移動時會觸發不少信號.其中當 用戶點擊被許可的鼠標按鈕(默認是左按鈕)時會調用onClicked信號.能夠設置onClicked的處理事件.本例中,當在MouseArea中點 擊鼠標時會調用console.log()輸出文本.這個函數可用於在調試時輸出文本信息.

SimpleButton.qml中的代碼實如今屏幕上顯示一個按鈕,並在鼠標點擊時輸出文本.

 Rectangle {

     id: button

     ...

 

     property color buttonColor: "lightblue"

     property color onHoverColor: "gold"

     property color borderColor: "white"

 

     signal buttonClick()

     onButtonClick: {

         console.log(buttonLabel.text + " clicked" )

     }

 

     MouseArea{

         onClicked: buttonClick()

         hoverEnabled: true

         onEntered: parent.border.color = onHoverColor

         onExited:  parent.border.color = borderColor

     }

 

     //determines the color of the button by using the conditional operator

     color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor

 }

完整功能的按鈕代碼在Button.qml中.上述的代碼片斷有些被省略,由於有些已經在上節中介紹過或與當前討論無關.

使用帶有屬性類型名的語法來自定義屬性.代碼 中,buttonColor屬性,是color類型的,聲明並賦值爲"lightblue".buttonColor稍後用在肯定按鈕填充顏色的條件操做 中.注意屬性賦值可能使用等號(=)操做符,而屬性綁定使用冒號(:)操做符.自定義屬性使內部項可與外部交互.QML基本類型(QML types)包括int,string,real,以及variant類型.

綁定onEntered和onExisted信號處理按鈕邊框顏色,鼠標懸停在按鈕上時爲黃色,鼠標移出時恢復顏色.

Button.qml中定義了一個buttonClick()信號,將signal關鍵字放在信號名稱前面.所 有信號的事件處理器會被自動建立,名稱前以on作前綴.例如,onButtonClick是buttonClick的處理器.onButtonClick 被賦予一個可執行的動做.在這個按鈕範例中,onClick按鈕事件中調用了onButtonClick,簡單的輸出一行文 本.onButtonClick信號使外部對象可處理按鈕的鼠標區域事件.例如,若是項中含有多個MouseArea聲明,buttonClick信號可 以更好的區分多個MouseArea的信號處理代碼.

如今咱們瞭解瞭如何定義一個可處理鼠標移動的QML元素.在Rectangle中定義了一個文本標籤,自定義其屬性,處理鼠標的移動.在元素內部建立子元素的概念會貫穿整個文本編輯器應用程序.

按鈕必須做爲組件來執行動做纔有使用價值.下節中將建立一個包含這種按鈕的菜單.

建立菜單頁

上節中闡述瞭如何建立元素並在單獨的QML文件中設置行爲.本節將說明如何導入QML元素,如何重用已有組件構建其餘組件.

菜單顯示一列內容,其中的每一個項均可以執行一個動做.在QML中,有不少種方式建立菜單.首先,咱們建立包含可執行不一樣動做按鈕的菜單.菜單代碼在FileMenu.qml中.

 import QtQuick 1.0                \\import the main Qt QML module

 import "folderName"            \\import the contents of the folder

 import "script.js" as Script        \\import a Javascript file and name it as Script

上述語法展現如何使用import關鍵字.這裏須要使用不在同一 目錄中的JavaScript文件或QML文件.因爲Button.qml與FileMenu.qml在同一目錄中,沒必要導入Button.qml就可直 接使用.可直接使用Button{}聲明一個按鈕元素,與Rectangle{}的聲明同樣.

FileMenu.qml:

 

     Row{

         anchors.centerIn: parent

         spacing: parent.width/6

 

         Button{

             id: loadButton

             buttonColor: "lightgrey"

             label: "Load"

         }

         Button{

             buttonColor: "grey"

             id: saveButton

             label: "Save"

         }

         Button{

             id: exitButton

             label: "Exit"

             buttonColor: "darkgrey"

 

             onButtonClick: Qt.quit()

         }

     }

FileMenu.qml中, 聲明瞭三個按鈕元素.他們都在一個Row元素中聲明的,這是一個定位器,將其子元素按行定位.Button聲明在Button.qml中,與上節定義的 Button.qml一致.新建立的按鈕可設置屬性綁定,在exitButton上增長了onButtonClick處理函數,由Button.qml中 定義的onButtonClick來觸發調用.

Row定義在Rectangle中,建立了包含一行按鈕的矩形容器.這個額外的矩形採用間接的方式在菜單中組織了一行按鈕.

這個階段定義的編輯菜單很是簡單.菜單按鈕具備的標籤爲:Copy,Paste,Select All.

結合前面介紹的知識和定義的組件,咱們已經能夠組合這些菜單頁生成一個菜單欄了,包括選擇菜單的按鈕,下面看看如何在QML中組織這些數據.

實現菜單欄

咱們的文本編輯器程序須要使用菜單欄顯示菜單.菜單欄能夠切換不一樣的菜單,用戶可選擇顯示哪一個菜單.菜單間的切換比僅僅顯示在一行中須要更多組織信息.QML使用模型和視圖來組織數據和顯示數據.

使用數據模型和視圖

QML有用來顯示數據模型(data models)的不一樣的數據視圖(data views ).咱們的菜單欄將顯示列表中的菜單,頭部顯示一行菜單名稱.菜單列表定義在VisualItemModel中.VisualItemModel元素中包含須要顯示的元素,如Rectangle元素和導入的UI元素.其餘模型類型如 ListModel 元素則須要顯示其數據的代理(視圖).

咱們在menuListModel中定義兩個可視化項,一個FileMenu一個EditMenu.並自定義這兩個菜單,顯示在ListView中.MenuBar.qml文件包含了簡單的QML定義,編輯菜單定義在EditMenu.qml中.

     VisualItemModel{

         id: menuListModel

         FileMenu{

             width: menuListView.width

             height: menuBar.height

             color: fileColor

         }

         EditMenu{

             color: editColor

             width:  menuListView.width

             height: menuBar.height

         }

     }

ListView元素用來顯示其代理模型.代理聲明可在Row元素中顯示模型元素,也可在Grid中顯示模型元素.menuListModel已經定義須要顯示的項了,所以,咱們再也不須要聲明模型了.

     ListView{

         id: menuListView

 

         //Anchors are set to react to window anchors

         anchors.fill:parent

         anchors.bottom: parent.bottom

         width:parent.width

         height: parent.height

 

         //the model contains the data

         model: menuListModel

 

         //控制菜單開關的移動

         snapMode: ListView.SnapOneItem

         orientation: ListView.Horizontal

         boundsBehavior: Flickable.StopAtBounds

         flickDeceleration: 5000

         highlightFollowsCurrentItem: true

         highlightMoveDuration:240

         highlightRangeMode: ListView.StrictlyEnforceRange

     }

另外,ListView從Flickable繼承,使列表可響應鼠標拖拽和手勢.上述代碼的最後部分設置flick屬性可在視圖中建立出指望的flick移動效果.特別是highlightMoveDuration屬性設置flick的延續時間.較高的highlightMoveDuration值使菜單切換變慢.

ListView經過索引維護模型項目,模型中的可視項都是經過 索引進行訪問的,索引順序與聲明順序相同.修改當前索引值會改變ListView中的高亮項.菜單欄的頭部分證明了這個效果.這裏一行中有兩個按鈕,點擊 後會改變當前菜單.點擊fileButton將當前菜單切換爲file菜單,因爲FileMenu在menuListModel中第一個聲明,所以其索引 爲0.一樣,點擊editButton將當前菜單切換爲EditMenu.

labelList矩形的z屬性值爲1,強調其顯示在菜單欄的最前面.項的z屬性值越高越靠前顯示.z屬性值默認爲0.

     Rectangle{

         id: labelList

         ...

         z: 1

         Row{

             anchors.centerIn: parent

             spacing:40

             Button{

                 label: "File"

                 id: fileButton

                 ...

                 onButtonClick: menuListView.currentIndex = 0

             }

             Button{

                 id: editButton

                 label: "Edit"

                 ...

                 onButtonClick:    menuListView.currentIndex = 1

             }

         }

     }

菜單欄能夠經過flick操做或點擊上面的菜單名稱按鈕.切換菜單效果很直觀生動.

構造文本編輯區

聲明TextArea

若是不包含文本編輯區域應用程序是不完整的.QML的TextEdit元素是多行可編輯的文本區域.TextEdit與Text元素不一樣,Text不容許用戶直接編輯文字.

     TextEdit{

         id: textEditor

         anchors.fill:parent

         width:parent.width; height:parent.height

         color:"midnightblue"

         focus: true

 

         wrapMode: TextEdit.Wrap

 

         onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)

     }

編輯器有一系列字體顏色屬性集和文字範圍集.TextEdit區 域放在一個flick區域上,若是文字光標在可視範圍以外則可滾動文字.ensureVisible()函數用來檢查光標是否在可視區域以外,並適當的移 動文本區域.QML使用JavaScript做爲其腳本語言,如前所述,JavaScript文件能夠導入到QML文件中.

     function ensureVisible(r){

         if (contentX >= r.x)

             contentX = r.x;

         else if (contentX+width <= r.x+r.width)

             contentX = r.x+r.width-width;

         if (contentY >= r.y)

             contentY = r.y;

         else if (contentY+height <= r.y+r.height)

             contentY = r.y+r.height-height;

     }

爲文本編輯器組合組件

如今已經使用QML建立了文本編輯器佈局.文本編輯器有兩個組 件,已經建立的菜單欄和文本區域.QML中能夠重用組件,所以導入組件並在必要時自定義組件,可以使咱們的代碼更加簡潔.文本編輯器將窗口分爲兩個部分:屏 幕的三分之一用於菜單欄,三分之二顯示文本區域.菜單欄顯示在其餘元素以前.

     Rectangle{

 

         id: screen

         width: 1000; height: 1000

 

         //the screen is partitioned into the MenuBar and TextArea. 1/3 of the screen is assigned to the MenuBar

         property int partition: height/3

 

         MenuBar{

             id:menuBar

             height: partition

             width:parent.width

             z: 1

         }

 

         TextArea{

             id:textArea

             anchors.bottom:parent.bottom

             y: partition

             color: "white"

             height: partition*2

             width:parent.width

         }

     }

經過導入可重用組件,咱們的文本編輯器代碼看起來很簡單.能夠自定義主應用程序,沒必要再過多考慮已經定義行爲的屬性.使用這種方式,很容易建立應用程序佈局和UI組件.

美化文本編輯器Decorating the Text Editor

實現彈性界面(Drawer Interface)

如今文本編輯器看起來很簡單,咱們須要美化一下.使用QML,可爲文本編輯器聲明轉換(transition)和動畫.菜單欄佔用了三分之一的屏幕區域,最好能在咱們須要的時候才顯示.

咱們能夠添加一個彈性界面(drawer interface), 當點擊的時候可彈出或收縮菜單欄.在咱們的實現中,只有一個小矩形響應鼠標點擊.應用程序具備兩個狀態:菜單欄打開和菜單欄關閉狀態.彈出項是一個具備很 小高度的矩形.其中嵌入了一個Image元素,具中顯示一個箭頭圖標.彈出信息保存在應用程序中標識屏幕狀態,當鼠標區域點擊時對其賦值.

     Rectangle{

         id:drawer

         height:15

 

         Image{

             id: arrowIcon

             source: "images/arrow.png"

             anchors.horizontalCenter: parent.horizontalCenter

         }

 

         MouseArea{

             id: drawerMouseArea

             anchors.fill:parent

             onClicked:{

                 if (screen.state == "DRAWER_CLOSED"){

                     screen.state = "DRAWER_OPEN"

                 }

                 else if (screen.state == "DRAWER_OPEN"){

                     screen.state = "DRAWER_CLOSED"

                 }

             }

             ...

         }

     }

狀態用於收集配置信息,使用State元素聲明.多個狀態可組織在states屬性中.本程序中,定義了兩個狀態,爲DRAWER_CLOSEDDRAWER_OPEN. 項目配置聲明在PropertyChanges元素中.在DRAWER_OPEN狀態中,有四個項目的屬性被改變.第一個是菜單欄,修改其y值爲0.一樣,textArea的位置變低.textArea,drawer和drawer中的圖標,屬性都會變化.

     states:[

         State {

             name: "DRAWER_OPEN"

             PropertyChanges { target: menuBar; y: 0}

             PropertyChanges { target: textArea; y: partition + drawer.height}

             PropertyChanges { target: drawer; y: partition}

             PropertyChanges { target: arrowIcon; rotation: 180}

         },

         State {

             name: "DRAWER_CLOSED"

             PropertyChanges { target: menuBar; y:-height; }

             PropertyChanges { target: textArea; y: drawer.height; height: screen.height - drawer.height }

             PropertyChanges { target: drawer; y: 0 }

             PropertyChanges { target: arrowIcon; rotation: 0 }

         }

     ]

狀態改變有些唐突須要更加平滑的轉換(transition).狀態間的轉換使用Transition元素定義,可綁定項目的轉換屬性.文本編輯器的狀態可能轉換到 DRAWER_OPENDRAWER_CLOSED.注意,轉換須要一個from和一個to狀態,但本例中使用*號表示轉換應用到全部狀態變化.

轉換中,能夠設計屬性變化的動畫.菜單欄切換位置從y:0到y:-pertition,咱們可使用NumberAnimation元素驅動變換.咱們聲明瞭動畫的目標屬性並設置動畫延續時間及趨勢曲線.趨勢曲線控制動畫速度和狀態轉換的差值行爲.這裏使用 Easing.OutQuint做爲趨勢曲線,在動畫結束時移動速度變慢.請見QML's Animation文檔.

     transitions: [

         Transition {

             to: "*"

             NumberAnimation { target: textArea; properties: "y, height"; duration: 100; easing.type:Easing.OutExpo }

             NumberAnimation { target: menuBar; properties: "y"; duration: 100; easing.type: Easing.OutExpo }

             NumberAnimation { target: drawer; properties: "y"; duration: 100; easing.type: Easing.OutExpo }

         }

     ]

讓屬性變化具備動畫效果的另一種方式是使用Behavior元素.轉換(transition)只能用於狀態(state)見得變化,Behavior能夠設置一個通常的屬性變化實現動畫.在文本編輯器中,箭頭使用NumberAnimating動畫來設置器旋轉屬性.

 In TextEditor.qml:

 

     Behavior{

         NumberAnimation{property: "rotation";easing.type: Easing.OutExpo }

     }

回顧組件的states和動畫知識,有助於改善組件的外觀.在 Button.qml中,可在按鈕點擊時增長顏色和縮放屬性變化.Color類型的動畫效果使用ColorAnimation,數值類型動畫效果使用 NumberAnimation.下面的範例中,屬性名稱用於設置惟一的屬性.

 In Button.qml:

     ...

 

     color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor

     Behavior on color { ColorAnimation{ duration: 55} }

 

     scale: buttonMouseArea.pressed ? 1.1 : 1.00

     Behavior on scale { NumberAnimation{ duration: 55} }

另外,給QML組件添加顏色效果,如漸變和透明效果能夠加強外觀效果.定義Gradient元素會覆蓋元素的顏色屬性.可以使用GradientStop元素在漸變中聲明一個顏色.漸變使用比例進行定位,值在0.0到1.0之間.

 In MenuBar.qml

     gradient: Gradient {

         GradientStop { position: 0.0; color: "#8C8F8C" }

         GradientStop { position: 0.17; color: "#6A6D6A" }

         GradientStop { position: 0.98;color: "#3F3F3F" }

         GradientStop { position: 1.0; color: "#0e1B20" }

     }

這個漸變用於菜單欄顯示一個模擬的漸變深度.第一個顏色值從0.0開始,最後的顏色值在1.0結束.

繼續另外的內容

最後咱們定義了一個簡單的文本編輯器用戶界面.接着咱們可使用Qt和C++來實現應用程序的邏輯了.QML是一個很棒的原型工具,將UI設計與應用程序邏輯分離.

使用C++擴展QML

如今完成了文本編輯器的佈局,須要在C++中實現文本編輯器的功能了.將QML與C++結合咱們可在Qt中建立應用程序的邏輯.在C++應用程序中可以使用 Qt's Declarative類建立QML上下文,並在圖像場景中顯示QML元素.或者導出C++代碼生成qmlviewer工具可讀的插件.本例中,要在C++中實現導入和保存功能,並導出一個插件.這樣咱們只需直接加載QML文件,而不是執行可執行文件.

向QML導出C++類

咱們使用Qt和C++來實現加載和保存功能.C++類和函數在註冊後可用於QML中.類須要編譯爲Qt插件,並且QML文件須要知道插件的位置.

在咱們的應用程序中,須要建立以下項目:

1.   處理目錄相關操做的Directory類

2.   模擬目錄中文件列表的文件類,繼承於QObject

3.   要註冊到QML上下文中的插件類

4.   編譯插件的Qt項目文件

5.   告訴qmlviewer工具從哪裏可發現插件的qmldir文件

生成Qt插件

要生成插件,須要在Qt項目文件中作以下設置.首先,將必要的源文件,頭文件,和Qt模塊添加到項目文件中.全部的C++代碼和項目文件都在filedialog目錄中.

在filedialog.pro中:

 

     TEMPLATE = lib

     CONFIG += qt plugin

     QT += declarative

 

     DESTDIR +=  ../plugins

     OBJECTS_DIR = tmp

     MOC_DIR = tmp

 

     TARGET = FileDialog

 

     HEADERS +=     directory.h \

             file.h \

             dialogPlugin.h

 

     SOURCES +=    directory.cpp \

             file.cpp \

             dialogPlugin.cpp

特別的使用declarative模塊並配置爲插件使用庫(lib)模版來編譯Qt.將編譯後的插件放在上級plugins目錄中.

向QML註冊類

dialogPlugin.h中:

 

     #include <QtDeclarative/QDeclarativeExtensionPlugin>

 

     class DialogPlugin : public QDeclarativeExtensionPlugin

     {

         Q_OBJECT

 

         public:

         void registerTypes(const char *uri);

 

     };

在咱們的插件類中,DialogPlugin繼承於QDeclarativeExtensionPlugin.咱們須要實現繼承的函數 registerTypes().dialogPlugin.cpp代碼以下:

 DialogPlugin.cpp:

 

     #include "dialogPlugin.h"

     #include "directory.h"

     #include "file.h"

     #include <QtDeclarative/qdeclarative.h>

 

     void DialogPlugin::registerTypes(const char *uri){

 

         qmlRegisterType<Directory>(uri, 1, 0, "Directory");

         qmlRegisterType<File>(uri, 1, 0,"File");

     }

 

     Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);

registerTypes()函數向QML中註冊咱們的File和Directory類.這個函數須要一個類名稱做爲模版參數的類別,一個主版本號,一個次版本號,和咱們的類的名稱.

咱們須要使用Q_EXPORT_PLUGIN2宏導出插件.注意dialogPlugin.h文件中,在類聲明的最前面加入了Q_OBJECT宏.同時須要在項目文件上使用qmake生成必要的元對象代碼.

在C++類中建立QML屬性

咱們可使用C++和Qt的元對象系統(Qt's Meta-Object System)建立QML元素和屬性.可以使用信號和槽實現屬性,使這些屬性在Qt中有效.這些屬性可用於QML.

在文本編輯器中,咱們須要加載和保存文件.一般,這個功能須要一個文件對話框.幸運的是咱們可使用QDir,QFile和QTextStream來實現讀取目錄和輸入輸出流.

     class Directory : public QObject{

 

         Q_OBJECT

 

         Q_PROPERTY(int filesCount READ filesCount CONSTANT)

         Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)

         Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)

         Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

 

         ...

Directory類使用Qt的元對象系統註冊完成文件操做的屬性.Directory類做爲插件,在QML中爲Directory元素.屬性列表中使用Q_PROPERTY宏聲明的都是QML屬性.
Q_PROPERTY連 同讀寫函數將屬性聲明到Qt的元對象系統.例如,QString類型的filename屬性,可以使用filename()函數讀取,使用 setFilename()函數設置.另外還有個與filename屬性有關的信號叫作filenameChanged(),屬性修改時被觸發.讀寫函數 在頭文件中的public段中聲明.

一樣,咱們還聲明瞭其餘屬性.filesCount屬性指示目錄中的文件數量.filename屬性設置當前選中的文件名稱,加載或保存的文件內容存儲在fileContent屬性中.

     Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

files屬性是目錄中被過濾的文件列表.Directory類 中過濾掉非文本文件.只有後綴爲.txt的文件有效.並且,在C++中聲明的QDeclarativeListProperty屬性在QML中可做爲 QList使用.這個模版對象須要從QObject繼承,所以,File類必須從QObject繼承.在Directory類中,文件對象列表存儲在叫作 m_fileList的QList中.

     class File : public QObject{

 

         Q_OBJECT

         Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

 

         ...

     };

這些屬性可在QML中做爲Directory元素的屬性.注意咱們沒有在C++中建立惟一標識id屬性.

     Directory{

         id: directory

 

         filesCount

         filename

         fileContent

         files

 

         files[0].name

     }

由於QML使用Javascript語法和結構體,咱們能夠遍歷文件列表並獲取其屬性.如獲取第一個文件屬性,能夠調用files[0].name.

一般C++函數也可在QML中訪問.文件加載和保存的函數在C++中使用Q_INVOKABLE 宏定義.並且,咱們能夠講函數聲明爲槽,函數可在QML中直接訪問.

 In Directory.h:

 

     Q_INVOKABLE void saveFile();

     Q_INVOKABLE void loadFile();

Directory類也會在文件內容發生變化的時候通知其餘類. 這個特性是使用信號實現的.如前面提到的,QML信號有一個相應的處理器,名稱以on爲前綴.信號叫作directoryChanged,在目錄更新時觸 發.更新操做簡單的重新加載目錄內容並更新目錄中有效的文件列表.QML項目設置onDirectoryChanged信號的處理槽函數就可獲得通知.

列表屬性須要特殊對待.由於列表屬性使用回調來方位和修改列表內容.列表屬性是QDeclarativeListProperty<File>類型.當訪問列表時,訪問器函數返回一個QDeclarativeListProperty<File>對象.模版類型File必須從QObject繼承.並且,要建立QDeclarativeListProperty,列表的訪問器和修改器須要傳遞構造函數做爲函數指針.本例中列表爲QList,須要存放File指針.

QDeclarativeListProperty的構造函數和Directory實現:

     QDeclarativeListProperty  ( QObject * object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )

     QDeclarativeListProperty<File>( this, &m_fileList, &appendFiles, &filesSize, &fileAt,  &clearFilesPtr );

構造函數傳遞一個可添加列表,返回列表數量,使用索引獲取項,狀況列表的函數指針.只有添加函數式必須的.注意函數指針必須與 AppendFunction, CountFunction, AtFunction, or ClearFunction匹配.

     void appendFiles(QDeclarativeListProperty<File> * property, File * file)

     File* fileAt(QDeclarativeListProperty<File> * property, int index)

     int filesSize(QDeclarativeListProperty<File> * property)

     void clearFilesPtr(QDeclarativeListProperty<File> *property)

爲簡化文件對話框,Directory類將不以.txt爲後綴的文件過濾掉.若是文件名稱不以.txt爲後綴,就不能在file對話框中可見.一樣保存文件的實現確保在文件名後加上.txt後綴.Directory使用QTextStream讀取文件並向文件中輸出內容.

使用Directory元素,能夠獲取文件列表,知道應用程序目錄中的文本文件數量,獲取文件名稱和文件內容字符串,當目錄內容發生變化時獲得通知.

生成插件,在filedialog.pro上運行qmake,而後運行make生成插件並拷貝到plugins目錄.

在QML中導入插件

qmlviewer工具導入與應用程序同目錄的文件.也可建立qmldir文件來指定但願導入的QML文件位置.qmldir文件可也指定插件位置和其餘資源.

qmldir文件內容:

 

     Button ./Button.qml

     FileDialog ./FileDialog.qml

     TextArea ./TextArea.qml

     TextEditor ./TextEditor.qml

     EditMenu ./EditMenu.qml

 

     plugin FileDialog plugins

插件正是咱們上面建立的FileDialog,在項目文件中指定了TARGET域.插件編譯後的文件在plugins目錄中.

向文件菜單整合文件對話框

文件菜單FileMenu須要顯示一個FileDialog元素,其中顯示了目錄中含有的文件列表,用戶可在列表中點擊選擇文件.同時也要設置保存,加載和新建按鈕的響應事件.FileMenu中包含一個可編輯的文本輸入框,讓用戶使用鍵盤輸入文件名稱.

Directory元素在FileMenu.qml文件中使用,並在目錄中內容刷新時通知FileDialog元素.通知是使用onDirectoryChanged信號實現的.

 In FileMenu.qml:

 

     Directory{

         id:directory

         filename: textInput.text

         onDirectoryChanged: fileDialog.notifyRefresh()

     }

爲了簡化應用程序,文件對話框老是顯示,並過濾掉不已.txt爲後綴的文件.

 In FileDialog.qml:

 

     signal notifyRefresh()

     onNotifyRefresh: dirView.model = directory.files

FileDialog元素顯示從files屬性中讀取的目錄內容.files做爲GridView(可將代理內容顯示在網格中)元素的模型.由代理處理模型的外觀,而文件對話框只簡單的在中間生成一個帶有文字的網格.點擊文件名稱使文件名稱矩形高亮.FileDialog在notifyRefresh信號觸發時獲得通知,從新加載目錄中的文件.

 FileMenu.qml:

 

     Button{

         id: newButton

         label: "New"

         onButtonClick:{

             textArea.textContent = ""

         }

     }

     Button{

         id: loadButton

         label: "Load"

         onButtonClick:{

             directory.filename = textInput.text

             directory.loadFile()

             textArea.textContent = directory.fileContent

         }

     }

     Button{

         id: saveButton

         label: "Save"

         onButtonClick:{

             directory.fileContent = textArea.textContent

             directory.filename = textInput.text

             directory.saveFile()

         }

     }

     Button{

         id: exitButton

         label: "Exit"

         onButtonClick:{

             Qt.quit()

         }

     }

FileMenu如今具有了各類功能.saveButton將 TextEdit中的文本傳遞給目錄文件的Content屬性,並從可編輯的文本輸入元素中複製文件名稱.最後,按鈕調用saveFile()函數,保存 文件.loadButton執行方式同理.新建動做清空TextEdit的內容.

EditMenu中的按鈕調用TextEdit的函數來拷貝,粘貼,全選文本編輯器中的內容.

完成文本編輯器

應用程序已經能夠做爲簡單的文本編輯器了,可輸入文本並保存到文件中.也可從文件中加載並手動修改文本.

運行文本編輯器

在運行文本編輯器前須要編譯C++插件(文件對話框).要編譯,進入gsQml目錄,運行qmake,依據你使用的平臺在使用make或nmake進行編譯.啓動qmlviewer,打開texteditor.qml文件.

源碼在examples/tutorials/gettingStarted/gsQml目錄中.

相關文章
相關標籤/搜索