GettingstartqmlChinese: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
[toc align_right="yes" depth="3"] | |||
本文翻译自:"Getting Started Programming With QML":http://doc.qt.nokia.com/4.7/gettingstartedqml.html | |||
= QML编程入门 = | |||
欢迎来到QML的世界,QML是说明式界面语言。在本文里,我们用QML创建一个简单的文本编辑器。读了本文后,您就可以用QML和Qt C+''开发你自己的应用程序了。 | |||
<br />h2. 用QML来创建用户界面 | |||
<br />我们要创建的程序是一个简单的文本编辑器,它可以读,写和其他的文本操作。本教程包含两部份。第一部分是用QML设计程序界面和功能。第二部分是用Qt C来实现文件读和写。使用Qt的Meta-Object系统,我们可以把C的函数当做QML元素可以使用的属性。使用QML和Qt C,我们可以有效地把界面逻辑和程序逻辑分开。 | |||
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor5_editmenu.png|text editor]] | |||
<br />要想运行QML程序,可以运行qmlviewer再加上QML文件名作为参数。这个教程的C部分需要你拥有基本的Qt程序编译知识。 | |||
<br />教程章节 | |||
<br />1 定义按钮和菜单<br />2 实现菜单条<br />3 创建文本编辑器<br />4 装饰文本编辑器<br />5 用Qt C扩展QML | |||
<br />h2. 定义按钮和菜单 | |||
<br />h3. 基本的组件 — 按钮 | |||
<br />我们现开始创建一个按钮。从功能上来说,一个按钮有一个鼠标感应区和一个标签。当用户按按钮时,按钮执行动作。<br />i在QML里,基本的可视元素是Rectangle. Rectangle元素有属性来控制外观和位置。 | |||
[[Image:qml- | <br />首先要import Qt 4.7,<br />这样qmlviewer可以导入我们以后需要的QML元素。每一个QML文件必须有这一行。需要注意Qt的版本号要在这一个行里。<br />这个简单的rectangle有一个属性id, simplebutton,<br />Rectangle的属性和值绑定在一起,先是属性,冒号,然后是值。在例子程序里,颜色grey绑订到Rectangle的color属性。同理,我们也绑订width和height. | ||
<br />Text元素是不可编辑的区域。我们叫它buttonLabel.为了设置Text区域的字符,我们绑订一个值到text属性。为了使标签在Rectangle的中心,我们使用Text元素的anchor固定到它的parent,这里是simplebutton.Anchor也可以绑订其他元素的anchor.这样界面设计就非常简单了。 | |||
<br />我们把代码存到SimpleButton.qml.<br />运行qmlviewer,用这个文件明作为参数。就可以显示出下面的灰色矩形和文本标签。 | |||
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_simplebutton.png|simple button]] | |||
<br />为了实现按钮功能,我们可以使用QML事件处理机制。QML事件处理机制和Qt的signal-slot的机制非常像。信号被出发,连接好的slot函数被调用。 | |||
<br /><code><br />Rectangle{<br /> id:simplebutton<br /> … | |||
<br /> MouseArea{<br /> id: buttonMouseArea | |||
<br /> anchors.fill: parent //anchor all sides of the mouse area to the rectangle's anchors<br /> //onClicked handles valid mouse button clicks<br /> onClicked: console.log(buttonLabel.text'' " clicked&quot; )<br /> }<br /> }<br /></code> | |||
simplebutton中包含了MouseArea元素。MouseArea元素描述了检测鼠标移动的交互区域。对于这个按钮,我们把整个MouseArea固定在它的parent - simplebutton. anchors.fill语句可以读取属性anchors的fill属性。QML用基于anchor的布局来把几个项目固定另外一个项目上,这样可以创建稳定的布局。 | |||
如果在指定的MouseArea边界里移动鼠标,MouseArea有很多被调用的信号处理函数,其中之一是onClicked,当鼠标按钮被按下时,onClicked被调用。缺省是鼠标左键。我们可以把动作绑定到onClicked处理函数。在我们的例子里,当鼠标在MouseArea被按下的时候,console.log()输出文本。在调试和输出文本时,函数console.log()是一个在非常有用的工具。 | |||
SimpleButton.qml的代码可以在屏幕上显示按钮,并且鼠标点击时输出文本。 | |||
<code><br />Rectangle {<br /> id:Button<br /> … | |||
property color buttonColor: "lightblue&quot;<br /> property color onHoverColor: "gold&quot;<br /> property color borderColor: "white&quot; | |||
signal buttonClick()<br /> onButtonClick: {<br /> console.log(buttonLabel.text + " clicked&quot; )<br /> } | |||
MouseArea{<br /> onClicked: buttonClick()<br /> hoverEnabled: true<br /> onEntered: parent.border.color = onHoverColor<br /> onExited: parent.border.color = borderColor<br /> } | |||
//determines the color of the button by using the conditional operator<br /> color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> }<br /></code> | |||
功能完整的按钮代码在Button.qml中。本文中的代码片断有遗漏,有些代码在前面的章节里已经介绍过,有些代码根本文无关。 | 功能完整的按钮代码在Button.qml中。本文中的代码片断有遗漏,有些代码在前面的章节里已经介绍过,有些代码根本文无关。 | ||
Line 43: | Line 46: | ||
自定义的属性用property type name语句来声明。在代码中,声明了buttonColor属性,并给它赋值lightblue. buttonColor在后面会在决定按钮的填充颜色条件语句中被用到。注意除了用冒号来赋值绑定,给属性赋值可以等号=。自定义属性允许内部属性被Rectangle的外部来访问。基本的QML类型有int,string, real, 和variant. | 自定义的属性用property type name语句来声明。在代码中,声明了buttonColor属性,并给它赋值lightblue. buttonColor在后面会在决定按钮的填充颜色条件语句中被用到。注意除了用冒号来赋值绑定,给属性赋值可以等号=。自定义属性允许内部属性被Rectangle的外部来访问。基本的QML类型有int,string, real, 和variant. | ||
通过把颜色绑定到onEntered和onExited信号处理函数,鼠标移到按钮上,按钮的边界会变成黄色,当鼠标移出按钮,边界会变成原来的颜色。<br /> 通过把signal放在信号名字的前面来声明buttonClick()信号。所有信号的处理函数都会被自动创建,信号的名字都是以on开始。在结果上onButtonClick就是buttonClick的处理函数。onButtonClick被指定去执行一个动作。在我们的按钮例子里,onClicked鼠标处理函数调用onButtonClick, onButtonClick会显示文本。onButtonClick可以使外部对象很容易访问按钮的鼠标区域。例如,声明多个MouseArea和一个buttonClick信号的对象可以在几个MouseArea信号处理函数中进行更好的区分。 | 通过把颜色绑定到onEntered和onExited信号处理函数,鼠标移到按钮上,按钮的边界会变成黄色,当鼠标移出按钮,边界会变成原来的颜色。<br />通过把signal放在信号名字的前面来声明buttonClick()信号。所有信号的处理函数都会被自动创建,信号的名字都是以on开始。在结果上onButtonClick就是buttonClick的处理函数。onButtonClick被指定去执行一个动作。在我们的按钮例子里,onClicked鼠标处理函数调用onButtonClick, onButtonClick会显示文本。onButtonClick可以使外部对象很容易访问按钮的鼠标区域。例如,声明多个MouseArea和一个buttonClick信号的对象可以在几个MouseArea信号处理函数中进行更好的区分。 | ||
我们现在基本了解用QML来实现可以处理鼠标移动的对象。我在Rectangle中创建一个Text标签,定义它的属性,实现处理鼠标移动的行为。我们在整个文本编辑器中将很多次在对象中创建对象。 | 我们现在基本了解用QML来实现可以处理鼠标移动的对象。我在Rectangle中创建一个Text标签,定义它的属性,实现处理鼠标移动的行为。我们在整个文本编辑器中将很多次在对象中创建对象。 | ||
Line 49: | Line 52: | ||
如果这个按钮不想一个组件一样执行操作,那是没什么用的。在下一节里我们要创建包含几个按钮的菜单。 | 如果这个按钮不想一个组件一样执行操作,那是没什么用的。在下一节里我们要创建包含几个按钮的菜单。 | ||
[[Image:qml-texteditor1_button.png|button]] | p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_button.png|button]] | ||
===创建菜单=== | === 创建菜单 === | ||
现在我们介绍了在一个QML文件里创建元素和定制行为。在这部分,我们介绍如何导入QML元素,如何重用元素来创建新的元素。 | 现在我们介绍了在一个QML文件里创建元素和定制行为。在这部分,我们介绍如何导入QML元素,如何重用元素来创建新的元素。 | ||
菜单显示列表的内容,列表中每一项都可以执行操作。在QML中我们有几种方法来创建菜单。第一种创建包含按钮的菜单,每个按钮可以执行特定的操作。菜单的代码在FileMenu.qml中. | 菜单显示列表的内容,列表中每一项都可以执行操作。在QML中我们有几种方法来创建菜单。第一种创建包含按钮的菜单,每个按钮可以执行特定的操作。菜单的代码在FileMenu.qml中. | ||
<code><br />import Qt 4.7 import the main Qt QML module<br />import "folderName&quot; import the contents of the folder<br />import "script.js&quot; as Script import a Javascript file and name it as Script</code> | |||
上面的代码表示如何使用import关键字。这里面需要使用javascript文件或者在另外的目录下的QML文件。因为Button.qml和FileMenu.qml在同一个目录下,所以我们不用导入Button.qml.我们可以直接通过声明Button{}来创建Button元素,这跟Rectangle{}声明类似。 | 上面的代码表示如何使用import关键字。这里面需要使用javascript文件或者在另外的目录下的QML文件。因为Button.qml和FileMenu.qml在同一个目录下,所以我们不用导入Button.qml.我们可以直接通过声明Button{}来创建Button元素,这跟Rectangle{}声明类似。 | ||
<code><br />In FileMenu.qml: | |||
Row{<br /> anchors.centerIn: parent<br /> spacing: parent.width/6 | |||
Button{<br /> id: loadButton<br /> buttonColor: "lightgrey&quot;<br /> label: "Load&quot;<br /> }<br /> Button{<br /> buttonColor: "grey&quot;<br /> id: saveButton<br /> label: "Save&quot;<br /> }<br /> Button{<br /> id: exitButton<br /> label: "Exit&quot;<br /> buttonColor: "darkgrey&quot; | |||
onButtonClick: Qt.quit()<br /> }<br /> }<br /></code> | |||
在FileMenu.qml中,我们在Row元素里声明了3个Button元素。Row元素里有三个垂直的子对象。Button在我们前面介绍的Button.qml里声明。可以在新建的按钮里声明新的属性绑定,覆盖Button.qml里的属性。名字叫做exitButton被点击时,程序退出。注意除了调用exitButton里的函数onButtonClickon,还有在Button.qml里的信号处理函数onButtonClick也会被调用。 | 在FileMenu.qml中,我们在Row元素里声明了3个Button元素。Row元素里有三个垂直的子对象。Button在我们前面介绍的Button.qml里声明。可以在新建的按钮里声明新的属性绑定,覆盖Button.qml里的属性。名字叫做exitButton被点击时,程序退出。注意除了调用exitButton里的函数onButtonClickon,还有在Button.qml里的信号处理函数onButtonClick也会被调用。 | ||
[[Image:qml-texteditor1_filemenu.png|buttons]] | p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_filemenu.png|buttons]] | ||
在Rectangle里声明了Row,为一排按钮创建了一个矩形容器。这个额外的矩形间接地在菜单里组织了按钮。 | 在Rectangle里声明了Row,为一排按钮创建了一个矩形容器。这个额外的矩形间接地在菜单里组织了按钮。 | ||
Line 67: | Line 80: | ||
编辑菜单的声明也非常类似。菜单包含按钮,按钮的标签是拷贝,粘贴和全选。 | 编辑菜单的声明也非常类似。菜单包含按钮,按钮的标签是拷贝,粘贴和全选。 | ||
[[Image:qml-texteditor1_editmenu.png|edit buttons]] | p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_editmenu.png|edit buttons]] | ||
我们已经知道了如何导入,定制刚才创建的组件,我们可以把这些菜单页组合起来创建一个菜单条。菜单条包含选择菜单的按钮,下面我们看看如何用QML组织数据。 | 我们已经知道了如何导入,定制刚才创建的组件,我们可以把这些菜单页组合起来创建一个菜单条。菜单条包含选择菜单的按钮,下面我们看看如何用QML组织数据。 | ||
==实现菜单条== | == 实现菜单条 == | ||
我们的文本编辑器需要用菜单条来显示菜单。菜单条要在不同的菜单间切换,用户可以选择那个菜单来显示。菜单切换意为着菜单需要比一排菜单更复杂的结构。QML用Models和Views来组织和显示数据。 | 我们的文本编辑器需要用菜单条来显示菜单。菜单条要在不同的菜单间切换,用户可以选择那个菜单来显示。菜单切换意为着菜单需要比一排菜单更复杂的结构。QML用Models和Views来组织和显示数据。 | ||
===使用数据的Models和Views=== | === 使用数据的Models和Views === | ||
QML有不同的显示data Models的data views. 我们的菜单条在列表中显示菜单,用头来显示一排菜单的名字。在VisualItemModel里面声明菜单列表。VisualItemModel元素包含有视图的条目,比如Rectangle元素和导入的UI元素。其他的类型的Model需要一个代理来显示它们的数据。 | QML有不同的显示data Models的data views. 我们的菜单条在列表中显示菜单,用头来显示一排菜单的名字。在VisualItemModel里面声明菜单列表。VisualItemModel元素包含有视图的条目,比如Rectangle元素和导入的UI元素。其他的类型的Model需要一个代理来显示它们的数据。 | ||
我们在menuListModel声明两个可视条目,FileMenu和EditMenu。我们定制了两个菜单,使用一个ListView来显示他们。MenuBar.qml文件包含了QML声明,在EditMenu.qml里定义了一个简单的编辑菜单。 | 我们在menuListModel声明两个可视条目,FileMenu和EditMenu。我们定制了两个菜单,使用一个ListView来显示他们。MenuBar.qml文件包含了QML声明,在EditMenu.qml里定义了一个简单的编辑菜单。 | ||
<code><br /> VisualItemModel{<br /> id: menuListModel<br /> FileMenu{<br /> width: menuListView.width<br /> height: menuBar.height<br /> color: fileColor<br /> }<br /> EditMenu{<br /> color: editColor<br /> width: menuListView.width<br /> height: menuBar.height<br /> }<br /> }<br /></code> | |||
ListView元素使用代理来显示Model。代理可以声明Model的条目,然后显示一排或者显示在网格里。在menulistModel里已经有了可视项目,我们不要再声明代理了。 | ListView元素使用代理来显示Model。代理可以声明Model的条目,然后显示一排或者显示在网格里。在menulistModel里已经有了可视项目,我们不要再声明代理了。 | ||
<code><br /> ListView{<br /> id: menuListView | |||
//Anchors are set to react to window anchors<br /> anchors.fill:parent<br /> anchors.bottom: parent.bottom<br /> width:parent.width<br /> height: parent.height | |||
//the model contains the data<br /> model: menuListModel | |||
//control the movement of the menu switching<br /> snapMode: ListView.SnapOneItem<br /> orientation: ListView.Horizontal<br /> boundsBehavior: Flickable.StopAtBounds<br /> flickDeceleration: 5000<br /> highlightFollowsCurrentItem: true<br /> highlightMoveDuration:240<br /> highlightRangeMode: ListView.StrictlyEnforceRange<br /> }<br /></code> | |||
另外,ListView从Flickable派生出来,因此列表可以响应鼠标拖放和其他动作。上面代码的最后一部份设置Flickable属性来给视图创建希望的flicking动作。特别地,higlightMOveDuration属性改变flick变化的时间。更大的highlightMoveDuration值会使菜单切换变得更慢。 | 另外,ListView从Flickable派生出来,因此列表可以响应鼠标拖放和其他动作。上面代码的最后一部份设置Flickable属性来给视图创建希望的flicking动作。特别地,higlightMOveDuration属性改变flick变化的时间。更大的highlightMoveDuration值会使菜单切换变得更慢。 | ||
Line 88: | Line 111: | ||
labelList矩形的z值是1,表明它被显示在菜单条的前面。有更高z值的项目显示在低z值项目的上面。缺省的z值是0. | labelList矩形的z值是1,表明它被显示在菜单条的前面。有更高z值的项目显示在低z值项目的上面。缺省的z值是0. | ||
<code><br /> Rectangle{<br /> id: labelList<br /> …<br /> z: 1<br /> Row{<br /> anchors.centerIn: parent<br /> spacing:40<br /> Button{<br /> label: "File&quot;<br /> id: fileButton<br /> …<br /> onButtonClick: menuListView.currentIndex = 0<br /> }<br /> Button{<br /> id: editButton<br /> label: "Edit&quot;<br /> …<br /> onButtonClick: menuListView.currentIndex = 1<br /> }<br /> }<br /> }<br /></code> | |||
我们可以点击上面的菜单名字来弹出刚才创建的菜单条。切换菜单的感觉很直接,响应很快。 | 我们可以点击上面的菜单名字来弹出刚才创建的菜单条。切换菜单的感觉很直接,响应很快。 | ||
[[Image:qml-texteditor2_menubar.png|menu bar]] | p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor2_menubar.png|menu bar]] | ||
==创建文本编辑器== | == 创建文本编辑器 == | ||
===声明文本区域=== | === 声明文本区域 === | ||
我们的文本编辑器必须有文本区域。QML的TextEdit元素可以容纳多行可编辑的文本区域。TextEdit和Text元素不同,Text元素部允许编辑文本。 | 我们的文本编辑器必须有文本区域。QML的TextEdit元素可以容纳多行可编辑的文本区域。TextEdit和Text元素不同,Text元素部允许编辑文本。 | ||
<code><br /> TextEdit{<br /> id: textEditor<br /> anchors.fill:parent<br /> width:parent.width; height:parent.height<br /> color:"midnightblue&quot;<br /> focus: true | |||
wrapMode: TextEdit.Wrap | |||
onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)<br /> }<br /></code> | |||
编辑器有字体属性,可以自动换行。TextEdit区域在Flickable区域里面,如果光标出了可见区域,TextEdit可以自动滚动。ensureVisible()函数可以查看光标在边界外面,然后相应地移动文本区域。QML用Javascript的语法,Javascript文件可以被导入到QML文件里。 | 编辑器有字体属性,可以自动换行。TextEdit区域在Flickable区域里面,如果光标出了可见区域,TextEdit可以自动滚动。ensureVisible()函数可以查看光标在边界外面,然后相应地移动文本区域。QML用Javascript的语法,Javascript文件可以被导入到QML文件里。 | ||
===把组件整合成文本编辑器=== | <code><br /> function ensureVisible®{<br /> if (contentX >= r.x)<br /> contentX = r.x;<br /> else if (contentX+width <= r.x+r.width)<br /> contentX = r.x+r.width-width;<br /> if (contentY >= r.y)<br /> contentY = r.y;<br /> else if (contentY+height <= r.y+r.height)<br /> contentY = r.y+r.height-height;<br /> }<br /></code> | ||
=== 把组件整合成文本编辑器 === | |||
我们现在可以用QML来创建文本编辑器的界面。编辑器有两个组件,菜单条和文本区域。QML可以通过导入和重新定制来重用组件,使我们的代码更简单。文本编辑器把窗口分成两部份,三分之一是菜单条,三分之二是文本区域。菜单条显示在其他元素之上。 | 我们现在可以用QML来创建文本编辑器的界面。编辑器有两个组件,菜单条和文本区域。QML可以通过导入和重新定制来重用组件,使我们的代码更简单。文本编辑器把窗口分成两部份,三分之一是菜单条,三分之二是文本区域。菜单条显示在其他元素之上。 | ||
<code><br /> Rectangle{ | |||
id: screen<br /> width: 1000; height: 1000 | |||
//the screen is partitioned into the MenuBar and TextArea. 1/3 of the screen is assigned to the MenuBar<br /> property int partition: height/3 | |||
MenuBar{<br /> id:menuBar<br /> height: partition<br /> width:parent.width<br /> z: 1<br /> } | |||
TextArea{<br /> id:textArea<br /> anchors.bottom:parent.bottom<br /> y: partition<br /> color: "white&quot;<br /> height: partition*2<br /> width:parent.width<br /> }<br /> }<br /></code> | |||
通过导入重用组件,我们的TextEditor代码非常简单。我们可以定制主程序,而不比担心已经定义的属性。用种方法可以很容易创建UI界面。 | 通过导入重用组件,我们的TextEditor代码非常简单。我们可以定制主程序,而不比担心已经定义的属性。用种方法可以很容易创建UI界面。 | ||
[[Image:qml-texteditor3_texteditor.png|text editor]] | p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor3_texteditor.png|text editor]] | ||
==装饰编辑器== | == 装饰编辑器 == | ||
===实现抽屉接口=== | === 实现抽屉接口 === | ||
我们的编辑器太简单了,我们要装饰它。应用QML我们可以实现渐变和动画。菜单条占了三分之一窗口,应该只有用它的时候才显示它。 | 我们的编辑器太简单了,我们要装饰它。应用QML我们可以实现渐变和动画。菜单条占了三分之一窗口,应该只有用它的时候才显示它。 | ||
我们可以加一个抽屉接口,当点击时,可以扩展菜单条。在我们的程序中,有一个很窄的矩形来响应鼠标点击。抽屉和程序一样,有两个状态,抽屉开和抽屉关。抽屉元素是一条矩形,高度非常小。在抽屉的中心,有一个内嵌的Image元素-箭头图标。当鼠标点击鼠标区域时,抽屉用id screen指定了整个程序的状态. | 我们可以加一个抽屉接口,当点击时,可以扩展菜单条。在我们的程序中,有一个很窄的矩形来响应鼠标点击。抽屉和程序一样,有两个状态,抽屉开和抽屉关。抽屉元素是一条矩形,高度非常小。在抽屉的中心,有一个内嵌的Image元素-箭头图标。当鼠标点击鼠标区域时,抽屉用id screen指定了整个程序的状态. | ||
<code><br /> Rectangle{<br /> id:drawer<br /> height:15 | |||
Image{<br /> id: arrowIcon<br /> source: "images/arrow.png&quot;<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> } | |||
MouseArea{<br /> id: drawerMouseArea<br /> anchors.fill:parent<br /> onClicked:{<br /> if (screen.state &quot;DRAWER_CLOSED&quot;)&#123; | |||
screen.state = &quot;DRAWER_OPEN&quot; | |||
&#125; | |||
else if (screen.state "DRAWER_OPEN&quot;){<br /> screen.state = "DRAWER_CLOSED&quot;<br /> }<br /> }<br /> …<br /> }<br /> }<br /></code> | |||
状态就是配置的集合,它声明在State元素里。一系列状态可以显示和帮定在states属性里。在我们的程序里有两个状态DRAWER_CLOSED和DRAWER_OPEN.在PropertyChange元素里声明状态配置。在DRAWER_OPEN状态,有四个条目会有属性变化。第一项,menuBar的y属性变成0。类似地,当状态是DRAWER_OPEN时,textArea会向下移。TextArea, drawer, 和drawer的图标的属性都有相应的变化来符合当前的状态。 | 状态就是配置的集合,它声明在State元素里。一系列状态可以显示和帮定在states属性里。在我们的程序里有两个状态DRAWER_CLOSED和DRAWER_OPEN.在PropertyChange元素里声明状态配置。在DRAWER_OPEN状态,有四个条目会有属性变化。第一项,menuBar的y属性变成0。类似地,当状态是DRAWER_OPEN时,textArea会向下移。TextArea, drawer, 和drawer的图标的属性都有相应的变化来符合当前的状态。 | ||
<code><br /> states:[<br /> State {<br /> name: "DRAWER_OPEN&quot;<br /> PropertyChanges { target: menuBar; y: 0}<br /> PropertyChanges { target: textArea; y: partition + drawer.height}<br /> PropertyChanges { target: drawer; y: partition}<br /> PropertyChanges { target: arrowIcon; rotation: 180}<br /> },<br /> State {<br /> name: "DRAWER_CLOSED&quot;<br /> PropertyChanges { target: menuBar; y:<s>height; }<br /> PropertyChanges { target: textArea; y: drawer.height; height: screen.height</s> drawer.height }<br /> PropertyChanges { target: drawer; y: 0 }<br /> PropertyChanges { target: arrowIcon; rotation: 0 }<br /> }<br /> ]<br /></code> | |||
状态变化是不连贯的,应该是平滑的渐变。状态之间的渐变在Transition元素中定义,绑定到对象的transitions属性。文本编辑器状态变化到当DRAWER_OPEN或DRAWER_CLOSED时,状态是渐变的。要注意,渐变需要一个from状态和to状态,我们这里用*, 说明渐变适用于所有的装太变化。 | 状态变化是不连贯的,应该是平滑的渐变。状态之间的渐变在Transition元素中定义,绑定到对象的transitions属性。文本编辑器状态变化到当DRAWER_OPEN或DRAWER_CLOSED时,状态是渐变的。要注意,渐变需要一个from状态和to状态,我们这里用*, 说明渐变适用于所有的装太变化。 | ||
在渐变中,我们可以在属性变化中使用动画。menuBar从位置y:0变到y:-partition,我们可以用NumberAnimation元素来产生动画。我们定义渐变的时间和曲线缓和来定制动画。曲线缓和在状态渐变过程中来控制动画的速度和插值。我们选择Easing.OutQuint作为曲线缓和,Easing.OutQuint在动画结束时减慢速度。详情请参考QML的动画文档。 | 在渐变中,我们可以在属性变化中使用动画。menuBar从位置y:0变到y:-partition,我们可以用NumberAnimation元素来产生动画。我们定义渐变的时间和曲线缓和来定制动画。曲线缓和在状态渐变过程中来控制动画的速度和插值。我们选择Easing.OutQuint作为曲线缓和,Easing.OutQuint在动画结束时减慢速度。详情请参考QML的动画文档。 | ||
<code><br /> transitions: [<br /> Transition {<br /> to: "*"<br /> NumberAnimation { target: textArea; properties: "y, height&quot;; duration: 100; easing.type:Easing.OutExpo }<br /> NumberAnimation { target: menuBar; properties: "y&quot;; duration: 100; easing.type: Easing.OutExpo }<br /> NumberAnimation { target: drawer; properties: "y&quot;; duration: 100; easing.type: Easing.OutExpo }<br /> }<br /> ]<br /></code> | |||
另外一种属性变化动画的方法是声明Behavior元素。Transition只在属性变化时起作用,Behavior可以设定通用的属性变化动画。在文本编辑器中,箭头可以设置NumberAnimation的旋转动画属性。 | 另外一种属性变化动画的方法是声明Behavior元素。Transition只在属性变化时起作用,Behavior可以设定通用的属性变化动画。在文本编辑器中,箭头可以设置NumberAnimation的旋转动画属性。 | ||
<code><br />In TextEditor.qml: | |||
Behavior{<br /> NumberAnimation{property: "rotation&quot;;easing.type: Easing.OutExpo }<br /> }<br /></code> | |||
让我们回到有状态和动画的组件,我们可以使组件的外感更好看。在Button.qml中,我们在按钮被按下时,加上color和scale属性变化。颜色类型动画使用ColorAnimation,数字使用NumberAnimation. 下面的on propertyName语法在对单一属性时非常有用。 | |||
<code><br />In Button.qml:<br /> … | |||
color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> Behavior on color { ColorAnimation{ duration: 55} } | |||
scale: buttonMouseArea.pressed ? 1.1 : 1.00<br /> Behavior on scale { NumberAnimation{ duration: 55} }<br /></code> | |||
另外,我们可以加上颜色效果,比如梯度和透明效果。Gradient元素可以覆盖color属性。我们可以用GradientStop元素声明颜色的梯度。梯度位置的值在0.0和1.0之间。 | |||
<code><br />In MenuBar.qml<br /> gradient: Gradient {<br /> GradientStop { position: 0.0; color: "#8C8F8C&quot; }<br /> GradientStop { position: 0.17; color: "#6A6D6A&quot; }<br /> GradientStop { position: 0.98;color: "#3F3F3F&quot; }<br /> GradientStop { position: 1.0; color: "#0e1B20&quot; }<br /> }<br /></code> | |||
菜单条用梯度来显示颜色深度。第一个颜色从位置0.0开始,最后一个颜色在位置1.0结束。 | |||
=== 下一步 === | |||
对于我们的程序,我们需要创建一下东西:<br /> 1. Directory类,处理目录的操作<br /> 2. File类,从QObject派生,模拟目录的文件列表<br /> 3. plugin类,注册到QML的环境中<br /> 4. Qt项目文件,编译插件<br /> 5. qmldir文件,告诉qmlviewer工具插件的位置 | 我们完成了文本编辑器的用户界面。下一步,我们可以用Qt和C+''实现程序逻辑。QML是一个非常好原型工具,把程序逻辑和用户界面分开。 | ||
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor4_texteditor.png|text editor]] | |||
<br />h3. 用Qt C来扩展QML | |||
<br />现在我们有文本编辑器的界面,我们可以用C来实现编辑器的功能。用QML和C,我们可以用Qt创建程序。我们可以用Qt的Declarative类在C程序里创建QML环境。我们也可以创建qmlviewer工具可以读取的C插件。在我们的程序里,我们用C来实现读取和存储功能,然后导出插件。这样,我们只要直接读取QML,而不用运行程序文件。 | |||
<br />h4. 在QML中使用C的类 | |||
<br />我们用Qt和C来实现文件的读取和存储。在QML中注册C的类和函数后,可以在QML中使用他们。C类需要被编译成Qt插件,并且QML需要知道插件的位置。 | |||
<br />对于我们的程序,我们需要创建一下东西:<br />1. Directory类,处理目录的操作<br />2. File类,从QObject派生,模拟目录的文件列表<br />3. plugin类,注册到QML的环境中<br />4. Qt项目文件,编译插件<br />5. qmldir文件,告诉qmlviewer工具插件的位置 | |||
<br />h4. 创建Qt插件 | |||
<br />为了创建一个插件, 我们要在Qt项目文件里设置下列东西。首先,必须的源程序,头文件,Qt模块。所有的C代码和项目文件都在filedialg目录里。 | |||
<br /><code><br />In cppPlugins.pro: | |||
<br /> TEMPLATE = lib<br /> CONFIG''= qt plugin<br /> QT ''= declarative | |||
<br /> DESTDIR''= ../plugins<br /> OBJECTS_DIR = tmp<br /> MOC_DIR = tmp | |||
= | TARGET = FileDialog | ||
HEADERS ''= directory.h file.h dialogPlugin.h | |||
<br /> SOURCES''= directory.cpp file.cpp dialogPlugin.cpp<br /></code> | |||
特别地,我们用declarative模块编译Qt插件,所以模板是lib. 我们把编译好的插件放在父目录的plugins目录里。 | 特别地,我们用declarative模块编译Qt插件,所以模板是lib. 我们把编译好的插件放在父目录的plugins目录里。 | ||
====把类注册到QML中。==== | ==== 把类注册到QML中。 ==== | ||
<code><br />In dialogPlugin.h: | |||
#include <QtDeclarative/QDeclarativeExtensionPlugin&gt; | |||
class DialogPlugin : public QDeclarativeExtensionPlugin<br /> {<br /> Q_OBJECT | |||
public:<br /> void registerTypes(const char *uri); | |||
};<br /></code> | |||
插件类DialogPlugin是QDeclarativeExtensionPlugin的派生类。我们需要实现虚函数registerTypes(). 下面是dialogplugin.cpp: | |||
<code><br />DialogPlugin.cpp: | |||
#include "dialogPlugin.h&quot;<br /> #include "directory.h&quot;<br /> #include "file.h&quot;<br /> #include <QtDeclarative/qdeclarative.h&gt; | |||
类似地,我们还声明了其他属性。filesCount属性表示目录的文件数。filename属性就是当前所选的文件的名字,读取或存储的文件内容存在fileContent属性里。 | void DialogPlugin::registerTypes(const char '''uri){ | ||
<br /> qmlRegisterType&lt;Directory&gt;(uri, 1, 0, "Directory&quot;);<br /> qmlRegisterType&lt;File&gt;(uri, 1, 0,"File&quot;);<br /> } | |||
<br /> Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);<br /></code> | |||
<br />registerTypes()函数把File类和Diretory类注册到QML里。这个函数需要类模板,重要版本号好,次要版本号和类名字。 | |||
<br />我们用Q_EXPORT_PLUGIN2宏来导出插件。注意dialogPlugin.h文件,Q_OBJECT宏必须在类的最前面。此外,我们必须运行qmake来产生必须的元代码。 | |||
<br />h4. 在C+''类中创建QML属性 | |||
<br />我们可以用C和Qt的元对象系统来创建QML元素和属性。我们可以用slots和signal来实现属性,使Qt知道这些属性,这些属性可以用在QML里。 | |||
<br />文本编辑器需要有文件读取和存储功能。这些功能存在于文件对话框里。幸运的是,我们可以用QDir,QFile和QTextStream来实现目录读取和输入输出流。 | |||
<br /><code><br /> class Directory : public QObject{ | |||
<br /> Q_OBJECT | |||
<br /> Q_PROPERTY(int filesCount READ filesCount CONSTANT)<br /> Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)<br /> Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)<br /> Q_PROPERTY(QDeclarativeListProperty&lt;File&gt; files READ files CONSTANT ) | |||
<br /> …<br /></code> | |||
<br />Directory类用Qt的元数据系统来注册文件处理所需要的属性。把Directory类导出成插件,在QML里作为Directory元素使用。每一个用Q_PROPERTY宏定义的属性也是QML里的属性。 | |||
<br />Q_PROPERTY声明了属性,同时也声明了元对象系统的读和写的函数。例如,filename属性,是QString类型,用filename()函数来读,用setFilename()来写。另外还声明了一个与filename属性关联的信号叫filenameChanged(),当属性变化时就发出这个信号。在头文件里读和写的函数都是公有函数。 | |||
<br />类似地,我们还声明了其他属性。filesCount属性表示目录的文件数。filename属性就是当前所选的文件的名字,读取或存储的文件内容存在fileContent属性里。 | |||
<br /><code>Q_PROPERTY(QDeclarativeListProperty&lt;File&gt; files READ files CONSTANT )<code> | |||
<br />files列表属性是一个目录下所有经过过滤的文件列表。Directory类实现了滤掉无效文件的功能,只有.txt结尾的文件才是有效的。只要在C中声明为QDeclarativeListProperty,QLists可以用在QML文件中使用。模板对象需要从QObject派生出来,因此File类也要从QObject派生出来。在Directory类中,File对象的列表存储在QList中,名字是m_fileList. | |||
<br /></code><br /> class File : public QObject{ | |||
<br /> Q_OBJECT<br /> Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) | |||
<br /> …<br /> };<br /><code> | |||
<br />这些属性可以在QML中作为Directory元素的属性来被使用。注意,我们不必在C中创建id属性。 | |||
<br /></code><br /> Directory{<br /> id: directory | |||
<br /> filesCount<br /> filename<br /> fileContent<br /> files | |||
<br /> files[0].name<br /> }<br /><code> | |||
<br />因为QML使用Javascript的语法和结构,我们可以迭代列表并读取它的属性。如果要读取第一个文件的名字属性,我们用files[0].name. | |||
<br />从QML中可以访问通常的C函数。文件的读取和存储是用C''+来实现的,但需要用Q_INVOKABLE宏来声明的。我们也可以把函数声明成slot,这样也可以从QML中访问此函数。 | |||
<br /></code><br />In Directory.h: | |||
<br /> Q_INVOKABLE void saveFile&amp;#40;&#41;;<br /> Q_INVOKABLE void loadFile&amp;#40;&#41;;<br /><code> | |||
<br />Directory类还要在目录内容变化时通知其他对象。我们用signal来实现这个功能。我们前面提过,QML信号有相应的以on开始的处理函数。这个信号叫directoryChanged,当目录更新的时候就会发出这个信号。刷新就是重新读取目录内容,并更新目录的有效文件列表。通过把一个动作关联到onDirectoryChanged信号处理函数来通知QML对象。 | |||
<br />列表属性需要进一步讨论。列表属性用回调函数来读取和修改列表的内容。列表属性是QDeclarativeListProperty&lt;File&gt;类型。当列表被读取时,读取函数需要返回QDeclarativeListProperty&lt;File&gt;。File作为模板类型,需要从QObject派生出来。另外,要创建QDeclarativeListProperty,列表的读取和修改需要作为函数指针来传给构造函数。我们的QList的列表需要成为File指针的列表。 | |||
<br />下面是QDeclarativeListProperty的构造函数和Directory的实现: | |||
<br /></code><br />QDeclarativeListProperty ( QObject''' object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )<br />QDeclarativeListProperty&lt;File&gt;( this, &m_fileList, &appendFiles, &filesSize, &fileAt, &clearFilesPtr );<br /><code> | |||
构造函数把指针传递给添加列表,列表计数,通过索引来取内容和清空列表的函数。只有添加函数是必不可少的。注意函数指针必须与AppendFunction, CountFunction, AtFunction 和ClearFunction的定义匹配。 | |||
</code><br />void appendFiles(QDeclarativeListProperty&lt;File&gt; * property, File * file)<br /> File* fileAt(QDeclarativeListProperty&lt;File&gt; * property, int index)<br /> int filesSize(QDeclarativeListProperty&lt;File&gt; * property)<br /> void clearFilesPtr(QDeclarativeListProperty&lt;File&gt; *property)<code> | |||
为了简化我们的文件对话框,Directory类过滤掉了无效的文本文件,无效的文件的扩展名不是.txt的扩展名。如果一个文件名不是.txt的扩展名, 那我们的对话框就看不见。同时要确保被存储的文件名有.txt的扩展名。Directory用QTextStream来读和写文件。 | 为了简化我们的文件对话框,Directory类过滤掉了无效的文本文件,无效的文件的扩展名不是.txt的扩展名。如果一个文件名不是.txt的扩展名, 那我们的对话框就看不见。同时要确保被存储的文件名有.txt的扩展名。Directory用QTextStream来读和写文件。 | ||
Line 193: | Line 282: | ||
要编译插件,对cppPlugins.pro项目文件来运行qmake,运行make编译并把插件拷贝到plugins目录里。 | 要编译插件,对cppPlugins.pro项目文件来运行qmake,运行make编译并把插件拷贝到plugins目录里。 | ||
====在QML里导入插件==== | ==== 在QML里导入插件 ==== | ||
qmlviewer工具可以导入应用程序当前目录下的文件。我们还可以创建qmldir文件,qmldir包含要导入QML文件的位置,插件和其他资源的位置信息。 | qmlviewer工具可以导入应用程序当前目录下的文件。我们还可以创建qmldir文件,qmldir包含要导入QML文件的位置,插件和其他资源的位置信息。 | ||
</code><br />In qmldir: | |||
Button ./Button.qml<br /> FileDialog ./FileDialog.qml<br /> TextArea ./TextArea.qml<br /> TextEditor ./TextEditor.qml<br /> EditMenu ./EditMenu.qml | |||
plugin FileDialog plugins<code> | |||
我们刚才创建的插件叫FileDialog,在项目文件里的TARGET里被定义的。编译好的插件在plugins目录里。 | 我们刚才创建的插件叫FileDialog,在项目文件里的TARGET里被定义的。编译好的插件在plugins目录里。 | ||
====把文件对话框集成到文件菜单里==== | ==== 把文件对话框集成到文件菜单里 ==== | ||
FileMenu要显示FileDialog元素,FileDialog里包含目录里的文本文件列表,这样用户可以通过点击列表来选择文件。我们需要把相应的动作指定到存储,读取和创建按钮. FileMenu包含可编辑的输入文本框,让用户用键盘来输入文件名。 | FileMenu要显示FileDialog元素,FileDialog里包含目录里的文本文件列表,这样用户可以通过点击列表来选择文件。我们需要把相应的动作指定到存储,读取和创建按钮. FileMenu包含可编辑的输入文本框,让用户用键盘来输入文件名。 | ||
FileMenu.qml用Directory元素来通知FileDialog元素目录内容刷新。在信号处理函数onDirectoryChanged里发出通知。 | FileMenu.qml用Directory元素来通知FileDialog元素目录内容刷新。在信号处理函数onDirectoryChanged里发出通知。 | ||
</code><br />In FileMenu.qml: | |||
Directory{<br /> id:directory<br /> filename: textInput.text<br /> onDirectoryChanged: fileDialog.notifyRefresh()<br /> }<br /><code> | |||
为了是程序简单,文件对话框总是可见的,并且不会显示无效的文件(不是.txt扩展名)。 | 为了是程序简单,文件对话框总是可见的,并且不会显示无效的文件(不是.txt扩展名)。 | ||
</code><br />In FileDialog.qml: | |||
signal notifyRefresh()<br /> onNotifyRefresh: dirView.model = directory.files<br /><code> | |||
FileDialog元素读取files列表属性并显示目录内容。文件被当作GridView元素的文档,GridView通过代理用网格的形式来显示数据。代理来处理文档的外观,我们的文件对话框创建了文本在中间的网格。点击文件名就会出现一个矩形来选中文件名。当收到notifyRefresh信号时,FileDialog接到通知,重新读取目录的文件。 | FileDialog元素读取files列表属性并显示目录内容。文件被当作GridView元素的文档,GridView通过代理用网格的形式来显示数据。代理来处理文档的外观,我们的文件对话框创建了文本在中间的网格。点击文件名就会出现一个矩形来选中文件名。当收到notifyRefresh信号时,FileDialog接到通知,重新读取目录的文件。 | ||
</code><br />In FileMenu.qml: | |||
Button{<br /> id: newButton<br /> label: "New&quot;<br /> onButtonClick:{<br /> textArea.textContent = ""<br /> }<br /> }<br /> Button{<br /> id: loadButton<br /> label: "Load&quot;<br /> onButtonClick:{<br /> directory.filename = textInput.text<br /> directory.loadFile&amp;#40;&#41;<br /> textArea.textContent = directory.fileContent<br /> }<br /> }<br /> Button{<br /> id: saveButton<br /> label: "Save&quot;<br /> onButtonClick:{<br /> directory.fileContent = textArea.textContent<br /> directory.filename = textInput.text<br /> directory.saveFile&amp;#40;&#41;<br /> }<br /> }<br /> Button{<br /> id: exitButton<br /> label: "Exit&quot;<br /> onButtonClick:{<br /> Qt.quit()<br /> }<br /> }<br /><code> | |||
我门的FileMenu现在连接好了相应的操作。saveButton会把文本从TextEdit传到目录的fileContent属性,然后从可编辑文本框拷贝文件名。最后,这个按钮调用saveFile&amp;#40;&#41;函数来存储文件。loadButton根前面的很类似。New action会清空TextEdit. | |||
另外,EditMenu按钮与TextEdit函数拷贝,粘贴和全选连接到一起。 | 另外,EditMenu按钮与TextEdit函数拷贝,粘贴和全选连接到一起。 | ||
[[Image:qml-texteditor5_filemenu.png|text editor]] | p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor5_filemenu.png|text editor]] | ||
====文本编辑器完成==== | ==== 文本编辑器完成 ==== | ||
[[Image:qml-texteditor5_newfile.png|text editor]] | [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor5_newfile.png|text editor]] | ||
这个程序可以向简单的编辑器一样,接受文本和存储文件。文本编辑器可以读取文件并执行文件操作。 | 这个程序可以向简单的编辑器一样,接受文本和存储文件。文本编辑器可以读取文件并执行文件操作。 |
Revision as of 10:02, 24 February 2015
[toc align_right="yes" depth="3"]
本文翻译自:"Getting Started Programming With QML":http://doc.qt.nokia.com/4.7/gettingstartedqml.html
QML编程入门
欢迎来到QML的世界,QML是说明式界面语言。在本文里,我们用QML创建一个简单的文本编辑器。读了本文后,您就可以用QML和Qt C+开发你自己的应用程序了。
h2. 用QML来创建用户界面
我们要创建的程序是一个简单的文本编辑器,它可以读,写和其他的文本操作。本教程包含两部份。第一部分是用QML设计程序界面和功能。第二部分是用Qt C来实现文件读和写。使用Qt的Meta-Object系统,我们可以把C的函数当做QML元素可以使用的属性。使用QML和Qt C,我们可以有效地把界面逻辑和程序逻辑分开。
p=. text editor
要想运行QML程序,可以运行qmlviewer再加上QML文件名作为参数。这个教程的C部分需要你拥有基本的Qt程序编译知识。
教程章节
1 定义按钮和菜单
2 实现菜单条
3 创建文本编辑器
4 装饰文本编辑器
5 用Qt C扩展QML
h2. 定义按钮和菜单
h3. 基本的组件 — 按钮
我们现开始创建一个按钮。从功能上来说,一个按钮有一个鼠标感应区和一个标签。当用户按按钮时,按钮执行动作。
i在QML里,基本的可视元素是Rectangle. Rectangle元素有属性来控制外观和位置。
首先要import Qt 4.7,
这样qmlviewer可以导入我们以后需要的QML元素。每一个QML文件必须有这一行。需要注意Qt的版本号要在这一个行里。
这个简单的rectangle有一个属性id, simplebutton,
Rectangle的属性和值绑定在一起,先是属性,冒号,然后是值。在例子程序里,颜色grey绑订到Rectangle的color属性。同理,我们也绑订width和height.
Text元素是不可编辑的区域。我们叫它buttonLabel.为了设置Text区域的字符,我们绑订一个值到text属性。为了使标签在Rectangle的中心,我们使用Text元素的anchor固定到它的parent,这里是simplebutton.Anchor也可以绑订其他元素的anchor.这样界面设计就非常简单了。
我们把代码存到SimpleButton.qml.
运行qmlviewer,用这个文件明作为参数。就可以显示出下面的灰色矩形和文本标签。
p=. simple button
为了实现按钮功能,我们可以使用QML事件处理机制。QML事件处理机制和Qt的signal-slot的机制非常像。信号被出发,连接好的slot函数被调用。
<br />Rectangle{<br /> id:simplebutton<br /> …
<br /> MouseArea{<br /> id: buttonMouseArea
<br /> anchors.fill: parent //anchor all sides of the mouse area to the rectangle's anchors<br /> //onClicked handles valid mouse button clicks<br /> onClicked: console.log(buttonLabel.text'' " clicked&quot; )<br /> }<br /> }<br />
simplebutton中包含了MouseArea元素。MouseArea元素描述了检测鼠标移动的交互区域。对于这个按钮,我们把整个MouseArea固定在它的parent - simplebutton. anchors.fill语句可以读取属性anchors的fill属性。QML用基于anchor的布局来把几个项目固定另外一个项目上,这样可以创建稳定的布局。
如果在指定的MouseArea边界里移动鼠标,MouseArea有很多被调用的信号处理函数,其中之一是onClicked,当鼠标按钮被按下时,onClicked被调用。缺省是鼠标左键。我们可以把动作绑定到onClicked处理函数。在我们的例子里,当鼠标在MouseArea被按下的时候,console.log()输出文本。在调试和输出文本时,函数console.log()是一个在非常有用的工具。
SimpleButton.qml的代码可以在屏幕上显示按钮,并且鼠标点击时输出文本。
<br />Rectangle {<br /> id:Button<br /> …
property color buttonColor: "lightblue&quot;<br /> property color onHoverColor: "gold&quot;<br /> property color borderColor: "white&quot;
signal buttonClick()<br /> onButtonClick: {<br /> console.log(buttonLabel.text + " clicked&quot; )<br /> }
MouseArea{<br /> onClicked: buttonClick()<br /> hoverEnabled: true<br /> onEntered: parent.border.color = onHoverColor<br /> onExited: parent.border.color = borderColor<br /> }
//determines the color of the button by using the conditional operator<br /> color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> }<br />
功能完整的按钮代码在Button.qml中。本文中的代码片断有遗漏,有些代码在前面的章节里已经介绍过,有些代码根本文无关。
自定义的属性用property type name语句来声明。在代码中,声明了buttonColor属性,并给它赋值lightblue. buttonColor在后面会在决定按钮的填充颜色条件语句中被用到。注意除了用冒号来赋值绑定,给属性赋值可以等号=。自定义属性允许内部属性被Rectangle的外部来访问。基本的QML类型有int,string, real, 和variant.
通过把颜色绑定到onEntered和onExited信号处理函数,鼠标移到按钮上,按钮的边界会变成黄色,当鼠标移出按钮,边界会变成原来的颜色。
通过把signal放在信号名字的前面来声明buttonClick()信号。所有信号的处理函数都会被自动创建,信号的名字都是以on开始。在结果上onButtonClick就是buttonClick的处理函数。onButtonClick被指定去执行一个动作。在我们的按钮例子里,onClicked鼠标处理函数调用onButtonClick, onButtonClick会显示文本。onButtonClick可以使外部对象很容易访问按钮的鼠标区域。例如,声明多个MouseArea和一个buttonClick信号的对象可以在几个MouseArea信号处理函数中进行更好的区分。
我们现在基本了解用QML来实现可以处理鼠标移动的对象。我在Rectangle中创建一个Text标签,定义它的属性,实现处理鼠标移动的行为。我们在整个文本编辑器中将很多次在对象中创建对象。
如果这个按钮不想一个组件一样执行操作,那是没什么用的。在下一节里我们要创建包含几个按钮的菜单。
p=. button
创建菜单
现在我们介绍了在一个QML文件里创建元素和定制行为。在这部分,我们介绍如何导入QML元素,如何重用元素来创建新的元素。
菜单显示列表的内容,列表中每一项都可以执行操作。在QML中我们有几种方法来创建菜单。第一种创建包含按钮的菜单,每个按钮可以执行特定的操作。菜单的代码在FileMenu.qml中.
<br />import Qt 4.7 import the main Qt QML module<br />import "folderName&quot; import the contents of the folder<br />import "script.js&quot; as Script import a Javascript file and name it as Script
上面的代码表示如何使用import关键字。这里面需要使用javascript文件或者在另外的目录下的QML文件。因为Button.qml和FileMenu.qml在同一个目录下,所以我们不用导入Button.qml.我们可以直接通过声明Button{}来创建Button元素,这跟Rectangle{}声明类似。
<br />In FileMenu.qml:
Row{<br /> anchors.centerIn: parent<br /> spacing: parent.width/6
Button{<br /> id: loadButton<br /> buttonColor: "lightgrey&quot;<br /> label: "Load&quot;<br /> }<br /> Button{<br /> buttonColor: "grey&quot;<br /> id: saveButton<br /> label: "Save&quot;<br /> }<br /> Button{<br /> id: exitButton<br /> label: "Exit&quot;<br /> buttonColor: "darkgrey&quot;
onButtonClick: Qt.quit()<br /> }<br /> }<br />
在FileMenu.qml中,我们在Row元素里声明了3个Button元素。Row元素里有三个垂直的子对象。Button在我们前面介绍的Button.qml里声明。可以在新建的按钮里声明新的属性绑定,覆盖Button.qml里的属性。名字叫做exitButton被点击时,程序退出。注意除了调用exitButton里的函数onButtonClickon,还有在Button.qml里的信号处理函数onButtonClick也会被调用。
p=. buttons
在Rectangle里声明了Row,为一排按钮创建了一个矩形容器。这个额外的矩形间接地在菜单里组织了按钮。
编辑菜单的声明也非常类似。菜单包含按钮,按钮的标签是拷贝,粘贴和全选。
p=. edit buttons
我们已经知道了如何导入,定制刚才创建的组件,我们可以把这些菜单页组合起来创建一个菜单条。菜单条包含选择菜单的按钮,下面我们看看如何用QML组织数据。
实现菜单条
我们的文本编辑器需要用菜单条来显示菜单。菜单条要在不同的菜单间切换,用户可以选择那个菜单来显示。菜单切换意为着菜单需要比一排菜单更复杂的结构。QML用Models和Views来组织和显示数据。
使用数据的Models和Views
QML有不同的显示data Models的data views. 我们的菜单条在列表中显示菜单,用头来显示一排菜单的名字。在VisualItemModel里面声明菜单列表。VisualItemModel元素包含有视图的条目,比如Rectangle元素和导入的UI元素。其他的类型的Model需要一个代理来显示它们的数据。
我们在menuListModel声明两个可视条目,FileMenu和EditMenu。我们定制了两个菜单,使用一个ListView来显示他们。MenuBar.qml文件包含了QML声明,在EditMenu.qml里定义了一个简单的编辑菜单。
<br /> VisualItemModel{<br /> id: menuListModel<br /> FileMenu{<br /> width: menuListView.width<br /> height: menuBar.height<br /> color: fileColor<br /> }<br /> EditMenu{<br /> color: editColor<br /> width: menuListView.width<br /> height: menuBar.height<br /> }<br /> }<br />
ListView元素使用代理来显示Model。代理可以声明Model的条目,然后显示一排或者显示在网格里。在menulistModel里已经有了可视项目,我们不要再声明代理了。
<br /> ListView{<br /> id: menuListView
//Anchors are set to react to window anchors<br /> anchors.fill:parent<br /> anchors.bottom: parent.bottom<br /> width:parent.width<br /> height: parent.height
//the model contains the data<br /> model: menuListModel
//control the movement of the menu switching<br /> snapMode: ListView.SnapOneItem<br /> orientation: ListView.Horizontal<br /> boundsBehavior: Flickable.StopAtBounds<br /> flickDeceleration: 5000<br /> highlightFollowsCurrentItem: true<br /> highlightMoveDuration:240<br /> highlightRangeMode: ListView.StrictlyEnforceRange<br /> }<br />
另外,ListView从Flickable派生出来,因此列表可以响应鼠标拖放和其他动作。上面代码的最后一部份设置Flickable属性来给视图创建希望的flicking动作。特别地,higlightMOveDuration属性改变flick变化的时间。更大的highlightMoveDuration值会使菜单切换变得更慢。
ListView包含数据项目,通过index以声明顺序来访问每个项目。改变currentIndex可以改变ListView里面被选中的项目。菜单条的头就是这样。在一排里有两个按钮,当点击按钮时,当前的菜单就被改变。fileButton把当前的菜单变成文件菜单,index是0,因为FileMenu在menuListModel第一个被声明。类似地,editButton把当前的菜单变成EditMenu.
labelList矩形的z值是1,表明它被显示在菜单条的前面。有更高z值的项目显示在低z值项目的上面。缺省的z值是0.
<br /> Rectangle{<br /> id: labelList<br /> …<br /> z: 1<br /> Row{<br /> anchors.centerIn: parent<br /> spacing:40<br /> Button{<br /> label: "File&quot;<br /> id: fileButton<br /> …<br /> onButtonClick: menuListView.currentIndex = 0<br /> }<br /> Button{<br /> id: editButton<br /> label: "Edit&quot;<br /> …<br /> onButtonClick: menuListView.currentIndex = 1<br /> }<br /> }<br /> }<br />
我们可以点击上面的菜单名字来弹出刚才创建的菜单条。切换菜单的感觉很直接,响应很快。
p=. menu bar
创建文本编辑器
声明文本区域
我们的文本编辑器必须有文本区域。QML的TextEdit元素可以容纳多行可编辑的文本区域。TextEdit和Text元素不同,Text元素部允许编辑文本。
<br /> TextEdit{<br /> id: textEditor<br /> anchors.fill:parent<br /> width:parent.width; height:parent.height<br /> color:"midnightblue&quot;<br /> focus: true
wrapMode: TextEdit.Wrap
onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)<br /> }<br />
编辑器有字体属性,可以自动换行。TextEdit区域在Flickable区域里面,如果光标出了可见区域,TextEdit可以自动滚动。ensureVisible()函数可以查看光标在边界外面,然后相应地移动文本区域。QML用Javascript的语法,Javascript文件可以被导入到QML文件里。
<br /> function ensureVisible®{<br /> if (contentX >= r.x)<br /> contentX = r.x;<br /> else if (contentX+width <= r.x+r.width)<br /> contentX = r.x+r.width-width;<br /> if (contentY >= r.y)<br /> contentY = r.y;<br /> else if (contentY+height <= r.y+r.height)<br /> contentY = r.y+r.height-height;<br /> }<br />
把组件整合成文本编辑器
我们现在可以用QML来创建文本编辑器的界面。编辑器有两个组件,菜单条和文本区域。QML可以通过导入和重新定制来重用组件,使我们的代码更简单。文本编辑器把窗口分成两部份,三分之一是菜单条,三分之二是文本区域。菜单条显示在其他元素之上。
<br /> Rectangle{
id: screen<br /> width: 1000; height: 1000
//the screen is partitioned into the MenuBar and TextArea. 1/3 of the screen is assigned to the MenuBar<br /> property int partition: height/3
MenuBar{<br /> id:menuBar<br /> height: partition<br /> width:parent.width<br /> z: 1<br /> }
TextArea{<br /> id:textArea<br /> anchors.bottom:parent.bottom<br /> y: partition<br /> color: "white&quot;<br /> height: partition*2<br /> width:parent.width<br /> }<br /> }<br />
通过导入重用组件,我们的TextEditor代码非常简单。我们可以定制主程序,而不比担心已经定义的属性。用种方法可以很容易创建UI界面。
p=. text editor
装饰编辑器
实现抽屉接口
我们的编辑器太简单了,我们要装饰它。应用QML我们可以实现渐变和动画。菜单条占了三分之一窗口,应该只有用它的时候才显示它。
我们可以加一个抽屉接口,当点击时,可以扩展菜单条。在我们的程序中,有一个很窄的矩形来响应鼠标点击。抽屉和程序一样,有两个状态,抽屉开和抽屉关。抽屉元素是一条矩形,高度非常小。在抽屉的中心,有一个内嵌的Image元素-箭头图标。当鼠标点击鼠标区域时,抽屉用id screen指定了整个程序的状态.
<br /> Rectangle{<br /> id:drawer<br /> height:15
Image{<br /> id: arrowIcon<br /> source: "images/arrow.png&quot;<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> }
MouseArea{<br /> id: drawerMouseArea<br /> anchors.fill:parent<br /> onClicked:{<br /> if (screen.state &quot;DRAWER_CLOSED&quot;)&#123;
screen.state = &quot;DRAWER_OPEN&quot;
&#125;
else if (screen.state "DRAWER_OPEN&quot;){<br /> screen.state = "DRAWER_CLOSED&quot;<br /> }<br /> }<br /> …<br /> }<br /> }<br />
状态就是配置的集合,它声明在State元素里。一系列状态可以显示和帮定在states属性里。在我们的程序里有两个状态DRAWER_CLOSED和DRAWER_OPEN.在PropertyChange元素里声明状态配置。在DRAWER_OPEN状态,有四个条目会有属性变化。第一项,menuBar的y属性变成0。类似地,当状态是DRAWER_OPEN时,textArea会向下移。TextArea, drawer, 和drawer的图标的属性都有相应的变化来符合当前的状态。
<br /> states:[<br /> State {<br /> name: "DRAWER_OPEN&quot;<br /> PropertyChanges { target: menuBar; y: 0}<br /> PropertyChanges { target: textArea; y: partition + drawer.height}<br /> PropertyChanges { target: drawer; y: partition}<br /> PropertyChanges { target: arrowIcon; rotation: 180}<br /> },<br /> State {<br /> name: "DRAWER_CLOSED&quot;<br /> PropertyChanges { target: menuBar; y:<s>height; }<br /> PropertyChanges { target: textArea; y: drawer.height; height: screen.height</s> drawer.height }<br /> PropertyChanges { target: drawer; y: 0 }<br /> PropertyChanges { target: arrowIcon; rotation: 0 }<br /> }<br /> ]<br />
状态变化是不连贯的,应该是平滑的渐变。状态之间的渐变在Transition元素中定义,绑定到对象的transitions属性。文本编辑器状态变化到当DRAWER_OPEN或DRAWER_CLOSED时,状态是渐变的。要注意,渐变需要一个from状态和to状态,我们这里用*, 说明渐变适用于所有的装太变化。
在渐变中,我们可以在属性变化中使用动画。menuBar从位置y:0变到y:-partition,我们可以用NumberAnimation元素来产生动画。我们定义渐变的时间和曲线缓和来定制动画。曲线缓和在状态渐变过程中来控制动画的速度和插值。我们选择Easing.OutQuint作为曲线缓和,Easing.OutQuint在动画结束时减慢速度。详情请参考QML的动画文档。
<br /> transitions: [<br /> Transition {<br /> to: "*"<br /> NumberAnimation { target: textArea; properties: "y, height&quot;; duration: 100; easing.type:Easing.OutExpo }<br /> NumberAnimation { target: menuBar; properties: "y&quot;; duration: 100; easing.type: Easing.OutExpo }<br /> NumberAnimation { target: drawer; properties: "y&quot;; duration: 100; easing.type: Easing.OutExpo }<br /> }<br /> ]<br />
另外一种属性变化动画的方法是声明Behavior元素。Transition只在属性变化时起作用,Behavior可以设定通用的属性变化动画。在文本编辑器中,箭头可以设置NumberAnimation的旋转动画属性。
<br />In TextEditor.qml:
Behavior{<br /> NumberAnimation{property: "rotation&quot;;easing.type: Easing.OutExpo }<br /> }<br />
让我们回到有状态和动画的组件,我们可以使组件的外感更好看。在Button.qml中,我们在按钮被按下时,加上color和scale属性变化。颜色类型动画使用ColorAnimation,数字使用NumberAnimation. 下面的on propertyName语法在对单一属性时非常有用。
<br />In Button.qml:<br /> …
color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> Behavior on color { ColorAnimation{ duration: 55} }
scale: buttonMouseArea.pressed ? 1.1 : 1.00<br /> Behavior on scale { NumberAnimation{ duration: 55} }<br />
另外,我们可以加上颜色效果,比如梯度和透明效果。Gradient元素可以覆盖color属性。我们可以用GradientStop元素声明颜色的梯度。梯度位置的值在0.0和1.0之间。
<br />In MenuBar.qml<br /> gradient: Gradient {<br /> GradientStop { position: 0.0; color: "#8C8F8C&quot; }<br /> GradientStop { position: 0.17; color: "#6A6D6A&quot; }<br /> GradientStop { position: 0.98;color: "#3F3F3F&quot; }<br /> GradientStop { position: 1.0; color: "#0e1B20&quot; }<br /> }<br />
菜单条用梯度来显示颜色深度。第一个颜色从位置0.0开始,最后一个颜色在位置1.0结束。
下一步
我们完成了文本编辑器的用户界面。下一步,我们可以用Qt和C+实现程序逻辑。QML是一个非常好原型工具,把程序逻辑和用户界面分开。
p=. text editor
h3. 用Qt C来扩展QML
现在我们有文本编辑器的界面,我们可以用C来实现编辑器的功能。用QML和C,我们可以用Qt创建程序。我们可以用Qt的Declarative类在C程序里创建QML环境。我们也可以创建qmlviewer工具可以读取的C插件。在我们的程序里,我们用C来实现读取和存储功能,然后导出插件。这样,我们只要直接读取QML,而不用运行程序文件。
h4. 在QML中使用C的类
我们用Qt和C来实现文件的读取和存储。在QML中注册C的类和函数后,可以在QML中使用他们。C类需要被编译成Qt插件,并且QML需要知道插件的位置。
对于我们的程序,我们需要创建一下东西:
1. Directory类,处理目录的操作
2. File类,从QObject派生,模拟目录的文件列表
3. plugin类,注册到QML的环境中
4. Qt项目文件,编译插件
5. qmldir文件,告诉qmlviewer工具插件的位置
h4. 创建Qt插件
为了创建一个插件, 我们要在Qt项目文件里设置下列东西。首先,必须的源程序,头文件,Qt模块。所有的C代码和项目文件都在filedialg目录里。
<br />In cppPlugins.pro:
<br /> TEMPLATE = lib<br /> CONFIG''= qt plugin<br /> QT ''= declarative
<br /> DESTDIR''= ../plugins<br /> OBJECTS_DIR = tmp<br /> MOC_DIR = tmp
TARGET = FileDialog
HEADERS ''= directory.h file.h dialogPlugin.h
<br /> SOURCES''= directory.cpp file.cpp dialogPlugin.cpp<br />
特别地,我们用declarative模块编译Qt插件,所以模板是lib. 我们把编译好的插件放在父目录的plugins目录里。
把类注册到QML中。
<br />In dialogPlugin.h:
#include <QtDeclarative/QDeclarativeExtensionPlugin&gt;
class DialogPlugin : public QDeclarativeExtensionPlugin<br /> {<br /> Q_OBJECT
public:<br /> void registerTypes(const char *uri);
};<br />
插件类DialogPlugin是QDeclarativeExtensionPlugin的派生类。我们需要实现虚函数registerTypes(). 下面是dialogplugin.cpp:
<br />DialogPlugin.cpp:
#include "dialogPlugin.h&quot;<br /> #include "directory.h&quot;<br /> #include "file.h&quot;<br /> #include <QtDeclarative/qdeclarative.h&gt;
void DialogPlugin::registerTypes(const char '''uri){
<br /> qmlRegisterType&lt;Directory&gt;(uri, 1, 0, "Directory&quot;);<br /> qmlRegisterType&lt;File&gt;(uri, 1, 0,"File&quot;);<br /> }
<br /> Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);<br />
registerTypes()函数把File类和Diretory类注册到QML里。这个函数需要类模板,重要版本号好,次要版本号和类名字。
我们用Q_EXPORT_PLUGIN2宏来导出插件。注意dialogPlugin.h文件,Q_OBJECT宏必须在类的最前面。此外,我们必须运行qmake来产生必须的元代码。
h4. 在C+类中创建QML属性
我们可以用C和Qt的元对象系统来创建QML元素和属性。我们可以用slots和signal来实现属性,使Qt知道这些属性,这些属性可以用在QML里。
文本编辑器需要有文件读取和存储功能。这些功能存在于文件对话框里。幸运的是,我们可以用QDir,QFile和QTextStream来实现目录读取和输入输出流。
<br /> class Directory : public QObject{
<br /> Q_OBJECT
<br /> Q_PROPERTY(int filesCount READ filesCount CONSTANT)<br /> Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)<br /> Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)<br /> Q_PROPERTY(QDeclarativeListProperty&lt;File&gt; files READ files CONSTANT )
<br /> …<br />
Directory类用Qt的元数据系统来注册文件处理所需要的属性。把Directory类导出成插件,在QML里作为Directory元素使用。每一个用Q_PROPERTY宏定义的属性也是QML里的属性。
Q_PROPERTY声明了属性,同时也声明了元对象系统的读和写的函数。例如,filename属性,是QString类型,用filename()函数来读,用setFilename()来写。另外还声明了一个与filename属性关联的信号叫filenameChanged(),当属性变化时就发出这个信号。在头文件里读和写的函数都是公有函数。
类似地,我们还声明了其他属性。filesCount属性表示目录的文件数。filename属性就是当前所选的文件的名字,读取或存储的文件内容存在fileContent属性里。
Q_PROPERTY(QDeclarativeListProperty&lt;File&gt; files READ files CONSTANT )<code>
<br />files列表属性是一个目录下所有经过过滤的文件列表。Directory类实现了滤掉无效文件的功能,只有.txt结尾的文件才是有效的。只要在C中声明为QDeclarativeListProperty,QLists可以用在QML文件中使用。模板对象需要从QObject派生出来,因此File类也要从QObject派生出来。在Directory类中,File对象的列表存储在QList中,名字是m_fileList.
<br />
class File : public QObject{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
…
};
<br />这些属性可以在QML中作为Directory元素的属性来被使用。注意,我们不必在C中创建id属性。
<br />
Directory{
id: directory
filesCount
filename
fileContent
files
files[0].name
}
<br />因为QML使用Javascript的语法和结构,我们可以迭代列表并读取它的属性。如果要读取第一个文件的名字属性,我们用files[0].name.
<br />从QML中可以访问通常的C函数。文件的读取和存储是用C''+来实现的,但需要用Q_INVOKABLE宏来声明的。我们也可以把函数声明成slot,这样也可以从QML中访问此函数。
<br />
In Directory.h:
Q_INVOKABLE void saveFile&#40;);
Q_INVOKABLE void loadFile&#40;);
<br />Directory类还要在目录内容变化时通知其他对象。我们用signal来实现这个功能。我们前面提过,QML信号有相应的以on开始的处理函数。这个信号叫directoryChanged,当目录更新的时候就会发出这个信号。刷新就是重新读取目录内容,并更新目录的有效文件列表。通过把一个动作关联到onDirectoryChanged信号处理函数来通知QML对象。
<br />列表属性需要进一步讨论。列表属性用回调函数来读取和修改列表的内容。列表属性是QDeclarativeListProperty&lt;File&gt;类型。当列表被读取时,读取函数需要返回QDeclarativeListProperty&lt;File&gt;。File作为模板类型,需要从QObject派生出来。另外,要创建QDeclarativeListProperty,列表的读取和修改需要作为函数指针来传给构造函数。我们的QList的列表需要成为File指针的列表。
<br />下面是QDeclarativeListProperty的构造函数和Directory的实现:
<br />
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 和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的扩展名, 那我们的对话框就看不见。同时要确保被存储的文件名有.txt的扩展名。Directory用QTextStream来读和写文件。
使用Directory元素,我们可以读取文件列表,在目录里文本文件的数目,读取文件名和内容到字符串里,如果目录内容发生变化会接到通知。
要编译插件,对cppPlugins.pro项目文件来运行qmake,运行make编译并把插件拷贝到plugins目录里。
==== 在QML里导入插件 ====
qmlviewer工具可以导入应用程序当前目录下的文件。我们还可以创建qmldir文件,qmldir包含要导入QML文件的位置,插件和其他资源的位置信息。
In qmldir:
Button ./Button.qml
FileDialog ./FileDialog.qml
TextArea ./TextArea.qml
TextEditor ./TextEditor.qml
EditMenu ./EditMenu.qml
plugin FileDialog plugins
我们刚才创建的插件叫FileDialog,在项目文件里的TARGET里被定义的。编译好的插件在plugins目录里。
==== 把文件对话框集成到文件菜单里 ====
FileMenu要显示FileDialog元素,FileDialog里包含目录里的文本文件列表,这样用户可以通过点击列表来选择文件。我们需要把相应的动作指定到存储,读取和创建按钮. FileMenu包含可编辑的输入文本框,让用户用键盘来输入文件名。
FileMenu.qml用Directory元素来通知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列表属性并显示目录内容。文件被当作GridView元素的文档,GridView通过代理用网格的形式来显示数据。代理来处理文档的外观,我们的文件对话框创建了文本在中间的网格。点击文件名就会出现一个矩形来选中文件名。当收到notifyRefresh信号时,FileDialog接到通知,重新读取目录的文件。
In FileMenu.qml:
Button{
id: newButton
label: "New"
onButtonClick:{
textArea.textContent = ""
}
}
Button{
id: loadButton
label: "Load"
onButtonClick:{
directory.filename = textInput.text
directory.loadFile&#40;)
textArea.textContent = directory.fileContent
}
}
Button{
id: saveButton
label: "Save"
onButtonClick:{
directory.fileContent = textArea.textContent
directory.filename = textInput.text
directory.saveFile&#40;)
}
}
Button{
id: exitButton
label: "Exit"
onButtonClick:{
Qt.quit()
}
}
我门的FileMenu现在连接好了相应的操作。saveButton会把文本从TextEdit传到目录的fileContent属性,然后从可编辑文本框拷贝文件名。最后,这个按钮调用saveFile&#40;)函数来存储文件。loadButton根前面的很类似。New action会清空TextEdit.
另外,EditMenu按钮与TextEdit函数拷贝,粘贴和全选连接到一起。
p=. text editor
文本编辑器完成
这个程序可以向简单的编辑器一样,接受文本和存储文件。文本编辑器可以读取文件并执行文件操作。