欢迎来到声明式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目录中.