您的位置:首页 > 其它

QML入门教程

2015-02-07 10:34 363 查看
欢迎来到声明式UI语言QML的世界.在本入门教程中,我们使用QML创建一个简单的文本编辑器.阅读这个教程后,就可以使用QML和Qt
C++开发应用程序了.

安装

首先需要安装包含Qt Quick的Qt最新版本,现在是Qt4.7.安装教程包括安装说明书和不同平台的需求.

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编译过程.

教程章节:

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目录中.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: