歡迎來到聲明式UI語言QML的世界.在本入門教程中,咱們使用QML建立一個簡單的文本編輯器.閱讀這個教程後,就可使用QML和Qt C++開發應用程序了.html
安裝react
首先須要安裝包含Qt Quick的Qt最新版本,如今是Qt4.7.安裝教程包括安裝說明書和不一樣平臺的需求.app
Qt Quick包含一個叫作QML的聲明式語言,Qt Declarative Module,和 QML Viewer.編輯器
QML構造用戶界面函數
咱們要構造的應用程序是一個簡單的文本編輯器,能夠加載,保存,以及執行一些文本處理.本教程包括兩個部分.第一個部分使用QML設計應用程序佈局和行爲.第二個部分中使用Qt C++實現加載和保存文本.應用Qt元對象系統(Qt's Meta-Object System)能夠將C++中的函數導入做爲QML元素的屬性進行訪問.利用QML和Qt 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屬性.一樣設置了矩形的width和height屬性.
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_CLOSED 和DRAWER_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_OPEN 或DRAWER_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目錄中.