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目录中.

相关文章
相关标签/搜索