qml界面设计(转)


QML编程入门

欢迎来到QML的世界!QML是一款齐新的陈述性UI编程语言。在本手册中,我们将用QML建立一个简单的文本编辑顺序。在阅读完本手册后,你将能够用QML和Qt C++开发自己的顺序。

  1. Directory {
  2.   id :directory
  3.   filename : textInput. text
  4.   onDirectoryChanged : fileDialog. notifyRefresh ( )
  5. }

通过运行qmlviewer工具,并将QML文件路径作为参数传递给她,你就能够运行QML的实例代码了。我们假设你对Qt的编译环节有最基本的了解,以便运行本手册中的C++代码。

  1. Behavior {
  2.   NumberAnimation {property :  "rotation" ;easing. type : Easing. OutExpo  }
  3. }

定义按钮和菜单

基础组件 – 按钮

我们从建立按钮开始文本编辑器的工作。功能上讲,按钮是一个能够接受鼠标事件的区域和一个标签。在用户点击按钮后,按钮才开始工作。

你的浏览器不支持图片表现

  1. import  Qt  4.7
  2. Rectangle  {
  3.   id : simpleb京东utton
  4.   color :  "grey"
  5.   width :  150 ; height :  75
  6.  
  7.   Text {
  8.     id : buttonLabel
  9.     anchors. centerIn : parent
  10.     text :  "button label"
  11.    }
  12. }

首先,我们使用“import Qt 4.7”导入预定义的QML元素,以便进一步使用。所有的QML文件中都必须使用该行。需要注意的是,所使用的Qt模块的版本号也必须在该声明中提及。

这里用到的简单Rectangle的标识(即id属性)为simplebutton。在QML中,元素的属性定义体式格局为“属性名:值”。这里,值grey被赋给Rectangle的color属性。类似的,我们也给定了Rectangle的width和height值。

Text元素是不成编辑的文本域。我们将其命名为buttonLabel,并通过设置其text属性来定义其表现的内容。通过设置该标签的anchors.centerIn属性值为parent,我们将标签定位于Rectangle的中心位置。锚可以和其他元素的锚相绑定,从而简化了布局。

我们将这段代码保存为SimpleButton.qml文件,然后运行qmlviewer,并将文件路径作为参数传递,就能看到如下所示的灰色矩形和文本标签。

Q_PROPERTY宏声明了属性,使其能够通过Qt的元工具系统被读写。例如QString类型的filename属性,可以通过filename()函数进行读取,并通过setFilename()函数进行写操纵。此外,还有一个名为filenameChanged()的信号与其绑定,会在该属性被改变时触发。读写的函数都在头文件中被定义为public函数。

  1. #include <QtDeclarative/QDeclarativeExtensionPlugin>
  2.  
  3. class DialogPlugin  :  public QDeclarativeExtensionPlugin
  4. {
  5.    Q_OBJECT
  6.  
  7. public :
  8.    void registerTypes ( const  char  *uri ) ;
  9.  
  10. } ;

  1. Rectangle {
  2.   id :simplebutton
  3.   ...
  4.  
  5.    MouseArea {
  6.     id : buttonMouseArea
  7.  
  8.     anchors. fill : parent  // 将鼠标区域设置为该Rectangle所在的区域
  9.      // onClicked处置责罚鼠标点击事件
  10.     onClicked : console. log (buttonLabel. text  +  " clicked"  )
  11.    }
  12. }

我们在simplebutton内包含了一个MouseArea元素,其描述了能够接受鼠标事件的区域。这里,我们将鼠标的区域设置为整个Rectangle所在的区域。anchors.fill供给了一种用以访问属性anchors所具有的属性fill的方式。QML使用基于锚的布局,以便元素之间相互绑定,从而建立稳定的布局。

MouseArea元素预定义了多个信号处置责罚方式,会在相应事件被触发时自动调用。例如,onClicked会在鼠标点击事件(默认为左键)发生时被调用。我们能够将需要进行的任务绑定到onClicked上。这里,我们调用console.log()来输出相应信息。值得一提的是,console.log()能够在调试时作为文本输出工具而大显身手。

这段在SimpleButton.qml中的代码已经充足被用于在屏幕上表现按钮,并在按钮被点击时输出一段文本信息。

The Directory element is used in the FileMenu.qml file and it notifies the FileDialog element that the directory refreshed its contents. This notification is performed in the signal handler, onDirectoryChanged.

Button.qml中的代码展示了一个功能完整的按钮。这里展示的代码中则省略了一部分,由于在前面已经介绍过,或是和当前的讨论没有直接关系。

我们可以通过“property 类型 名称”来自定义新的属性。这里,名为buttonColor的新属性就是一例。其被定义为color类型,并赋值为lightblue。buttonColor被进一步用在条件操纵符中,以决定按钮的填充色。除使用冒号“:”外,属性的赋值也可以用“=”进行。自定义的属性使得外部元素也能够访问内部组件。在QML中,基本类型包括int、string、real等,以及一个variant类型。

The list properties need to be explored further. This is because list properties use callbacks to access and modify the list contents. The list property is of type QDeclarativeListProperty<File>. Whenever the list is accessed, the accessor function needs to return a QDeclarativeListProperty<File>. The template type, File, needs to be a QObject derivative. Further, to create the QDeclarativeListProperty, the list’s accessor and modifiers need to be passed to the consructor as function pointers. The list, a QList in our case, also needs to be a list of File pointers.

buttonClick()信号在Button.qml文件中被声明,以“取代”onClicked信号。所有的信号的处置责罚器都被自动建立,其名称为on加上信号名。这里,onButtonClick是buttonClick的处置责罚器。这里,onClicked处置责罚器会调用onButtonClick处置责罚器,以输出一段文本。onButtonClick则使得外部工具能够访问Button的鼠标区域。例如,某些元素可能定义了多个MouseArea,而使用buttonClick信号则能够更好的区分不同MouseArea发出的信号。

现在,我们已经拥有了充足的知识来使用QML建立元素,并处置责罚基本的鼠标事件。我们也在Rectangle内建立了一个Text标签,自定义了其属性,并实现了对相关信号的处置责罚。在元素内建立元素的概念会在这个文本编辑器中重复出现。

除非能够执行一定的功能,否则按钮是没用的。在下一节中,我们将建立一个包含了若干个按钮的菜单。

你的浏览器不支持图片表现

另一个对属性改变加加动画的方式是声明Behavior元素。转换只在状态变化的过程中有效,并且Behavior能够为属性的变化设置动画。在这个文本编辑器中,该箭头使用了NumberAnimation来为其rotation属性设置动画。

你的浏览器不支持图片表现

装饰文本编辑器

实现绘图接口

我们的文本编辑器看上去特别很是简单,所以我们需要装饰一下。通过使用QML,我们能够给文本编辑器加加转换和动画效果。考虑到菜单栏就占据了1/3的空间,所以有必要使他仅仅在我们需要时才出现。

我们可以通过点击菜单名来访问相应的菜单项。菜单的交换也是直观和有动感的。

  1. Q_PROPERTY (QDeclarativeListProperty <File > files READ files CONSTANT  )

  1. Row {
  2.   anchors. centerIn : parent
  3.   spacing : parent. width / 6
  4.  
  5.   Button {
  6.     id : loadButton
  7.     buttonColor :  "lightgrey"
  8.     label :  "Load"
  9.    }
  10.  
  11.   Button {
  12.     buttonColor :  "grey"
  13.     id : saveButton
  14.     label :  "Save"
  15.    }
  16.  
  17.   Button {
  18.     id : exitButton
  19.     label :  "Exit"
  20.     buttonColor :  "darkgrey"
  21.  
  22.     onButtonClick :  Qt. quit ( )
  23.    }
  24. }

我们定义了三个Button元素。他们都在Row元素内被定义,Row元素能够自动将其子元素放置在同一行中。Button的定义则位于同一目录下的Button.qml文件中,在前一节中已经讨论和使用过。我们可以在这些新建立的按钮中加加新的属性,从而覆盖在Button.qml中定义的那些属性。点击名为exitButton的按钮能够关闭当前关闭当前窗口。而在Button.qml中定义的onButtonClick也同样会被调用。

状态就是各种配置的一个简单集合,用State元素表示。一系列的状态能够被一一列出,并绑定到状态属性上。在我们的顺序中,有两个被称为DRAWER_CLOSED和DRAWER_OPEN的状态。配置通过PropertyChanges元素被声明。在DRAWER_OPEN状态上,有四个项能够接受属性的变化。第一个目标,menuBar,会将其y属性改为0。类似的,textArea会在DRAWER_OPEN状态时降低到一个新的位置。而textArea、绘图器及其图标都邑改变属性以实现当前状态。

Row元素声明在Rectangle的内部,即建立了一个矩形容器来存放按钮行。这个额外的矩形供给了一个间接的体式格局来管理菜单内的按钮行。

编辑菜单的声明和这里提到的方式类似,用到的按钮包含以下标签:Copy、Paste和Select All。

你的浏览器不支持图片表现

  1. function ensureVisible (r ) {
  2.    if  (contentX  >= r. x )
  3.     contentX  = r. x ;
  4.    else  if  (contentX +width  <= r. x +r. width )
  5.     contentX  = r. x +r. width -width ;
  6.    if  (contentY  >= r. y )
  7.     contentY  = r. y ;
  8.    else  if  (contentY +height  <= r. y +r. height )
  9.     contentY  = r. y +r. height -height ;
  10. }

实现菜单栏

我们的文本编辑顺序需要能够在菜单栏上表现菜单。菜单栏需要在用户选择需要表现的菜单后表现其中的菜单项。菜单的交换意味着菜单需要比简单在一行中表现更多的布局。QML使用模型和视图布局来处置责罚数据。

使用数据模型和视图

QML能够使用不同的视图来揭示数据模型。我们的菜单栏将以列表的形式揭示菜单,并将菜单名在一列中表现。菜单的列表被定义在VisualItemModel中。VisualItemModel元素包含了若干已经拥有视图的项,例如Rectangle元素和导入的其他UI元素。其他如ListModel模型则需要一个delegate来表现其中的数据。

我们在menuListModel中声明了两个可视化项,FileMenu和EditMenu。我们自定义了这两个菜单,并用ListView来揭示它们。MenuBar.qml文件中包含了相应的QML声明,而编辑菜单则在EditMenu.qml中定义。

  1. VisualItemModel {
  2.   id : menuListModel
  3.   FileMenu {
  4.     width : menuListView. width
  5.     height : menuBar. height
  6.     color : fileColor
  7.    }
  8.   EditMenu {
  9.     color : editColor
  10.     width :  menuListView. width
  11.     height : menuBar. height
  12.    }
  13. }

ListView元素能够通过delegate来揭示模型的数据。而delegate能够通过声明模型项来在Row元素或格子中揭示数据。这里的menuListModel已经具有了可视化项,因此我们不再需要声明delegate。

  1. QDeclarativeListProperty   (  QObject  * object ,  void  * data , AppendFunction append , CountFunction count  =  0 , AtFunction at  =  0 ,ClearFunction clear  =  0  )
  2. QDeclarativeListProperty <File > (  this ,  &m_fileList ,  &appendFiles ,  &filesSize ,  &fileAt ,   &clearFilesPtr  ) ;

此外,ListView担当自Flickable,使得其能够相应鼠标的拖拽和其他姿势。上面这段代码的最后一部分设置了Flickable的属性,以便建立所需的效果。特别的,highlightMoveDuration属性改变了按键转换。更高的highlightMoveDuration值意味着更慢的菜单转换。

labelList矩形的z值为1,意味着它表现在菜单栏之前。z值越高的项表现得越靠前,默认值为0。

  1. Rectangle {
  2.   id : labelList
  3.   ...
  4.    z :  1
  5.   Row {
  6.     anchors. centerIn : parent
  7.     spacing : 40
  8.     Button {
  9.       label :  "File"
  10.       id : fileButton
  11.       ...
  12.        onButtonClick : menuListView. currentIndex  =  0
  13.      }
  14.     Button {
  15.       id : editButton
  16.       label :  "Edit"
  17.       ...
  18.        onButtonClick :    menuListView. currentIndex  =  1
  19.      }
  20.    }
  21. }

在FileMenu.qml文件中:

你的浏览器不支持图片表现

建立文本编辑器

声明TextArea

如果没有可编辑的文本域,我们的顺序就不能被称为文本编辑器了。而QML就恰好供给了一个TextEdit元素来进行多行的文本编辑。TextEdit和Text元素不同,后者不允许用户直接编辑其中的文本。

  1. TextEdit {
  2.   id : textEditor
  3.   anchors. fill :parent
  4.   width :parent. width ; height :parent. height
  5.   color : "midnightblue"
  6.   focus :  true
  7.  
  8.   wrapMode : TextEdit. Wrap
  9.  
  10.   onCursorRectangleChanged : flickArea. ensureVisible (cursorRectangle )
  11. }

  1. signal notifyRefresh ( )
  2. onNotifyRefresh : dirView. model  = directory. files

ListView通过索引维护模型项,并能够通过索引访问模型中的每一个可视项。改变currentIndex能够有效的改变ListView中的高亮项。比如这里菜单项的头部就是如此。在同一行中有两个按钮,在点击后都能够当先表现的菜单。点击fileButton能够将当前菜单设置为文件菜单,而FileMenu的索引为0,由于其是在menuListModel中第一个定义的。类似的,点击editButton也能够将当前菜单改变为EditMenu。

通过使用导入和自定义先前定义的组件,我们能够通过组合这些菜单页来建立菜单栏。

为文本编辑器组合组件

我们现在可以用QML建立文本编辑器的布局了。这个文本编辑器有两个组件:先前建立的菜单栏和文本区域。QML允许我们重用组件,因此我们能够通过导入组件和在必要时自定义来简化代码。我们的文本编辑器的窗口包含两个部分:三分之一为菜单栏,剩下三分之二为文本域。菜单栏在其他元素前表现。

用QML构建用户界面

我们要建立的顺序是一个简单的文本编辑器,能够加载、保存,并对文本文件进行简单操纵。本手册包括两部分:第一部分将使用QML设想顺序布局和行为,第二部分则用Qt C++实现文件的加载和保存。通过使用Qt的Meta-Object系统,我们能够将C++函数以属性的形式表露给QML元素。通过组合QML和Qt C++,我们能够有效的别离顺序的界面和逻辑。

  1. Rectangle {
  2.   id : screen
  3.   width :  1000 ; height :  1000
  4.  
  5.    // 屏幕被分为MenuBar和TextArea两部分。MenuBar占据1/3的空间
  6.   property  int partition : height / 3
  7.  
  8.   MenuBar {
  9.     id :menuBar
  10.     height : partition
  11.     width :parent. width
  12.     z :  1
  13.    }
  14.  
  15.   TextArea {
  16.     id :textArea
  17.     anchors. bottom :parent. bottom
  18.     y : partition
  19.     color :  "white"
  20.     height : partition * 2
  21.     width :parent. width
  22.    }
  23. }

状态改变需要辅以更加流畅的转换。状态间的转换通过Transition元素定义,并且能够绑定到该项的transition属性上。这里,我们的文本编辑器在其状态转换到DRAWER_OPEN或者DRAWER_CLOSED时都邑触发一个转换。重要的是,转换需要设置一个from和一个to属性,定义在何时被触发。我们也可以用*使其在任何状态转换发生时被触发。

  1. Directory {
  2.   id : directory
  3.  
  4.   filesCount
  5.   filename
  6.   fileContent
  7.   files
  8.  
  9.   files [ 0 ]. name
  10. }

我们需要加加一个绘图接口,在被点击时表现或收回菜单栏。在我们的实现中,我们有一个矩形来响应鼠标点击事件。和顺序一样,这个绘图接口有“打开”和“关闭”两个状态。该绘图项是一个高度不大的矩形。其内部有一个Image元素来表现箭头图标。该绘图接口向整个顺序赋予了一个状态,以响应用户的鼠标点击事件。

  1. Rectangle {
  2.   id :drawer
  3.   height : 15
  4.  
  5.   Image {
  6.     id : arrowIcon
  7.     source :  "images/arrow.png"
  8.     anchors. horizontalCenter : parent. horizontalCenter
  9.    }
  10.  
  11.   MouseArea {
  12.     id : drawerMouseArea
  13.     anchors. fill :parent
  14.     onClicked : {
  15.        if  (screen. state  ==  "DRAWER_CLOSED" ) {
  16.         screen. state  =  "DRAWER_OPEN"
  17.        }
  18.        else  if  (screen. state  ==  "DRAWER_OPEN" ) {
  19.         screen. state  =  "DRAWER_CLOSED"
  20.        }
  21.      }
  22.     ...
  23.    }
  24. }

你的浏览器没法表现图片

  1. states : [
  2.   State  {
  3.     name :  "DRAWER_OPEN"
  4.     PropertyChanges  { target : menuBar ; y :  0 }
  5.     PropertyChanges  { target : textArea ; y : partition  + drawer. height }
  6.     PropertyChanges  { target : drawer ; y : partition }
  7.     PropertyChanges  { target : arrowIcon ; rotation :  180 }
  8.    } ,
  9.   State  {
  10.     name :  "DRAWER_CLOSED"
  11.     PropertyChanges  { target : menuBar ; y :-height ;  }
  12.     PropertyChanges  { target : textArea ; y : drawer. height ; height : screen. height  - drawer. height  }
  13.     PropertyChanges  { target : drawer ; y :  0  }
  14.     PropertyChanges  { target : arrowIcon ; rotation :  0  }
  15.    }
  16. ]

通过导入可重用的组件,我们的TextEditor的代码显得相当简单。我们能够自定义主顺序,而不用担忧已经被定义好的属性等。通过该途径,顺序布局和UI组件都能够很容易的被建立。

在转换过程中,我们可以将动画和属性改变相绑定。我们能够用NumberAnimation元素将menuBar从y:0到y:-partition的位置改变加加动画效果。我们声明目标的属性会以一个特定的曲线在一段时间内改变。这个曲线(easing curve)负责控制动画的速度,以及在状态转化间的行为。我们在这里选择的曲线是Easing.OutQuint,能够使移动在快结束时放慢。请参考QML的相关文章获取更多的动画信息。

  1. transitions :  [
  2.   Transition  {
  3.     to :  "*"
  4.     NumberAnimation  { target : textArea ; properties :  "y, height" ; duration :  100 ; easing. type :Easing. OutExpo  }
  5.     NumberAnimation  { target : menuBar ; properties :  "y" ; duration :  100 ; easing. type : Easing. OutExpo  }
  6.     NumberAnimation  { target : drawer ; properties :  "y" ; duration :  100 ; easing. type : Easing. OutExpo  }
  7.    }
  8. ]

建立菜单页

到目前为止,我们已经讨论了若何在单个UML文件中建立元素,并设定要执行的动作。现在,我们将讨论若何引入和重用其他文件中定义的QML元素。

In TextEditor.qml:

用Qt C++扩展QML

现在,我们已经拥有了文本编辑器的界面,接下来就要用C++来实现其功能。通过QML和C++的殽杂使用,我们能够用Qt开发顺序的逻辑。我们可以在C++顺序中通过Qt的Declarative类来建立QML上下文,并在Graphics Scene中表现QML元素。此外,我们也能将C++代码编译成插件,以便在qmlviewer中被调用。这里,我们会将开发后的顺序编译成插件。如许,我们就能直接(用qmlviewer)加载QML文件,而不必运行一个可执行文件了。

回想那些有状态和动画的组件,我们能够改良其外观。在Button.qml中,我们能够定义按钮被点击时的颜色和scale属性,还能通过ColorAnimation来为颜色改变加加动画,也能用NumberAnimation为数字属性的改变加加动画效果。下面的“on 属性名“语法在为单个属性加加动画时特别很是有用。

在Button.qml中:

此外,我们能够通过加加例如gradients和透明度等颜色效果来改良QML组件的外观。声明一个Gradient元素能够覆盖元素的颜色属性。你可以在gradient中用GradientStop元素来声明颜色。gradient取值为0.0到1.0之间。

在MenuBar.qml中:

  1. gradient : Gradient  {
  2.   GradientStop  { position :  0.0 ; color :  "#8C8F8C"  }
  3.   GradientStop  { position :  0.17 ; color :  "#6A6D6A"  }
  4.   GradientStop  { position :  0.98 ;color :  "#3F3F3F"  }
  5.   GradientStop  { position :  1.0 ; color :  "#0e1B20"  }
  6. }

这里的gradient被用到菜单栏上来表现渐变的深度。第一个颜色被0.0位置定义,最后一个则在1.0处。

接下来做什么

我们已经完成了对这个简单的文本编辑器的用户界面的构建。现在,我们可以用普通的Qt和C++来实现顺序的逻辑了。QML能够很好的被用作构建原型的工具,将顺序的逻辑和UI设想相别离。

你的浏览器不支持表现图片

  1. TEMPLATE  = lib
  2. CONFIG  +=  qt plugin
  3. QT  += declarative
  4.  
  5. DESTDIR  +=  .. /plugins
  6. OBJECTS_DIR  = tmp
  7. MOC_DIR  = tmp
  8.  
  9. TARGET  = FileDialog
  10.  
  11. HEADERS  +=     directory. h \
  12.         file. h \
  13.         dialogPlugin. h
  14.  
  15. SOURCES  +=    directory. cpp \
  16.         file. cpp \
  17.         dialogPlugin. cpp

将C++类表露给QML

我们会用Qt和C++实现文件的加载和保存。通过注册,我们可以在QML中使用C++类和函数。然后将C++代码编译称Qt的插件,以便QML文件能够加载。

在我们的顺序中,我们需要建立以下项:

  1. Directory类已负责处置责罚目录相关的操纵
  2. File类是QObject,揭示目录里的文件列表
  3. 插件类以便将C++类注册到QML上下文
  4. Qt工程文件,以便编译插件
  5. 一个qmldir文件,告诉qmlviewer何处加载插件

建立Qt plugin

我们需要在Qt工程文件中加加如下代码以编译插件,包括必要的源文件、头文件,以及Qt模块等。所有的C++代码和工程文件都存放在filedialog目录下。

通过在onEntered和onExited上加加相应代码,我们能够在鼠标移动进、出按钮时改变其颜色。

章节:
定义按钮和菜单
实现菜单栏
建立文本编辑器
装饰文本编辑器
用Qt C++扩展QML

特别的,我们加加了Qt的declarative模块,将工程设置为插件类型,并把编译好的插件放到上层目录的plugins目录下。

向QML注册类

在dialogPlugin.h文件中:

The properties can then be used in QML as part of the Directory element’s properties. Note that we do not have to create an identifier id property in our C++ code.

我们的插件类DialogPlugin担当自QDeclarativeExtensionPlugin,需要实现其registerTypes()函数。而相应的源文件dialogPlugin.cpp如下所示:

DialogPlugin.cpp:

  1. #include "dialogPlugin.h"
  2. #include "directory.h"
  3. #include "file.h"
  4. #include <QtDeclarative/qdeclarative.h>
  5.  
  6. void DialogPlugin :: registerTypes ( const  char  *uri ) {
  7.  
  8.   qmlRegisterType <Directory > (uri ,  1 ,  0 ,  "Directory" ) ;
  9.   qmlRegisterType <File > (uri ,  1 ,  0 , "File" ) ;
  10. }
  11.  
  12. Q_EXPORT_PLUGIN2 (FileDialog , DialogPlugin ) ;

这里的registerTypes()函数将我们的File和Directory两个类注册到QML上下文中。注册用的函数qmlRegisterType需要相应的类名作为其模板,一个主版本号、副版本号,以及QML中展示的类名作为参数。

最后,我们需要用Q_EXPORT_PLUGIN2宏来导出插件。注意,我们在头文件的类声明中也加加了Q_OBJECT宏,需要运行qmake来自动建立相应的元工具代码。

在C++类中建立QML属性

我们能够使用C++,通过Qt的元工具系统来建立QML元素。我们能够通过信号和槽机制来实现属性,使其能被Qt用到QML上。

我们需要为文本编辑器供给加载和保存文件的功能。典型的,该功能包含在文件对话框中。幸运的是,我们可以使用QDir、QFile和QTextStream来实现目录的读取和输入输出流。

  1. color : buttonMouseArea. pressed ?  Qt. darker (buttonColor ,  1.5 )  : buttonColor
  2. Behavior on color  { ColorAnimation { duration :  55 }  }
  3.  
  4. scale : buttonMouseArea. pressed ?  1.1  :  1.00
  5. Behavior on scale  { NumberAnimation { duration :  55 }  }

  1. class Directory  :  public  QObject {
  2.  
  3.    Q_OBJECT
  4.  
  5.   Q_PROPERTY ( int filesCount READ filesCount CONSTANT )
  6.   Q_PROPERTY ( QString filename READ filename WRITE setFilename NOTIFY filenameChanged )
  7.   Q_PROPERTY ( QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged )
  8.   Q_PROPERTY (QDeclarativeListProperty <File > files READ files CONSTANT  )
  9.  
  10.   ...

该Directory类使用了Qt的元工具系统来注册其为文件操纵而需要的属性,并以插件形式导出到QML中作为Directory元素。这里用Q_PROPERTY宏定义的每一个属性都被导出称一个QML属性。

菜单能够表现多个菜单项,每一个菜单项都能够执行一个特定的动作。在QML中,我们能用不同的方式建立菜单。首先,我们将建立建立一个包含多个按钮的菜单,点击这些按钮将触发不同的动作。菜单的源代码在FileMenu.qml文件中。

The FileDialog element will display the contents of a directory by reading its list property called files. The files are used as the model of a GridView element, which displays data items in a grid according to a delegate. The delegate handles the appearance of the model and our file dialog will simply create a grid with text centered in the middle. Clicking on the file name will result in the appearance of a rectangle to highlight the file name. The FileDialog is notified whenever the notifyRefresh signal is emitted, reloading the files in the directory.

  1. ListView {
  2.   id : menuListView
  3.  
  4.    // 锚被定义来反应窗口的锚
  5.   anchors. fill :parent
  6.   anchors. bottom : parent. bottom
  7.   width :parent. width
  8.   height : parent. height
  9.  
  10.    // 该模型包含了数据
  11.   model : menuListModel
  12.  
  13.    // 控制菜单交换时的移动
  14.   snapMode : ListView. SnapOneItem
  15.   orientation : ListView. Horizontal
  16.   boundsBehavior : Flickable. StopAtBounds
  17.   flickDeceleration :  5000
  18.   highlightFollowsCurrentItem :  true
  19.   highlightMoveDuration : 240
  20.   highlightRangeMode : ListView. StrictlyEnforceRange
  21. }

在cppPlugins.pro文件中:

The files list property is a list of all the filtered files in a directory. The Directory class is implemented to filter out invalid text files; only files with a .txt extension are valid. Further, QLists can be used in QML files by declaring them as a QDeclarativeListProperty in C++. The templated object needs to inherit from a QObject, therefore, the File class must also inherit from QObject. In the Directory class, the list of File objects is stored in a QList called m_fileList.

  1. class File  :  public  QObject {
  2.  
  3.    Q_OBJECT
  4.   Q_PROPERTY ( QString name READ name WRITE setName NOTIFY nameChanged )
  5.  
  6.   ...
  7. } ;

为了实现按钮的点击功能,我们将用到QML的事件处置责罚。在QML中,事件处置责罚与Qt的信号/槽机制类似:当信号被触发时,与其连接的槽被调用。

上面用到的语法展示了若何使用“import”关头字。这在使用不在同一目录下的JavaScript或者QML文件来说是必须的。由于Button.qml文件和FileMenu.qml文件位于同一目录,我们并不需要导入Button.qml就能够直接使用它。我们能够通过声明“Button {}”直接建立Button元素,就像使用“Rectangle{}”声明一样。

你的浏览器不支持图片表现

Because QML uses Javascript’s syntax and structure, we can iterate through the list of files and retrieve its properties. To retrieve the first file’s name property, we can call files0.name.

Regular C++ functions are also accessible from QML. The file loading and saving functions are implemented in C++ and declared using the Q_INVOKABLE macro. Alternatively, we can declare the functions as a slot and the functions will be accessible from QML.

In Directory.h:

  1. Q_INVOKABLE  void saveFile ( ) ;
  2. Q_INVOKABLE  void loadFile ( ) ;

The Directory class also has to notify other objects whenever the directory contents change. This feature is performed using a signal. As previously mentioned, QML signals have a corresponding handler with their names prepended with on. The signal is called directoryChanged and it is emitted whenever there is a directory refresh. The refresh simply reloads the directory contents and updates the list of valid files in the directory. QML items can then be notified by attaching an action to the onDirectoryChanged signal handler.

  1. import  Qt  4.7   // 导入Qt QML模块
  2. import  "folderName"   //导入该目录的内容
  3. import  "script.js" as Script   // 导入一个名为script.js的JavaScript文件,并将其命名为Script

The constructor of QDeclarativeListProperty constructor and the Directory implementation:

在QML中,基本的可视化组件是Rectangle元素。Rectangle元素拥有能够控制元素外观和位置的属性。

The constructor passes pointers to functions that will append the list, count the list, retrieve the item using an index, and empty the list. Only the append function is mandatory. Note that the function pointers must match the definition of AppendFunction, CountFunction, AtFunction, or ClearFunction.

  1. void appendFiles (QDeclarativeListProperty <File >  * property , File  * file )
  2. File * fileAt (QDeclarativeListProperty <File >  * property ,  int index )
  3. int filesSize (QDeclarativeListProperty <File >  * property )
  4. void clearFilesPtr (QDeclarativeListProperty <File >  *property )

To simplify our file dialog, the Directory class filters out invalid text files, which are files that do not have a .txt extension. If a file name doesn’t have the .txt extension, then it won’t be seen in our file dialog. Also, the implementation makes sure that saved files have a .txt extension in the file name. Directory uses QTextStream to read the file and to output the file contents to a file.

With our Directory element, we can retrieve the files as a list, know how many text files is in the application directory, get the file’s name and content as a string, and be notified whenever there are changes in the directory contents.

To build the plugin, run qmake on the cppPlugins.pro project file, then run make to build and transfer the plugin to the plugins directory.

在QML中导入plugin

The qmlviewer tool imports files that are in the same directory as the application. We can also create a qmldir file containing the locations of QML files we wish to import. The qmldir file can also store locations of plugins and other resources.

In qmldir:

  1. Button . /Button. qml
  2. FileDialog . /FileDialog. qml
  3. TextArea . /TextArea. qml
  4. TextEditor . /TextEditor. qml
  5. EditMenu . /EditMenu. qml
  6.  
  7. plugin FileDialog plugins

The plugin we just created is called FileDialog, as indicated by the TARGET field in the project file. The compiled plugin is in the plugins directory.

将文件对话框集成到文件菜单中

Our FileMenu needs to display the FileDialog element, containing a list of the text files in a directory thus allowing the user to select the file by clicking on the list. We also need to assign the save, load, and new buttons to their respective actions. The FileMenu contains an editable text input to allow the user to type a file name using the keyboard.

  1. Rectangle  {
  2.   id :Button
  3.   ...
  4.  
  5.    property color buttonColor :  "lightblue"
  6.   property color onHoverColor :  "gold"
  7.   property color borderColor :  "white"
  8.  
  9.    signal buttonClick ( )
  10.   onButtonClick :  {
  11.     console. log (buttonLabel. text  +  " clicked"  )
  12.    }
  13.  
  14.   MouseArea {
  15.     onClicked : buttonClick ( )
  16.     hoverEnabled :  true
  17.     onEntered : parent. border. color  = onHoverColor
  18.     onExited :  parent. border. color  = borderColor
  19.    }
  20.  
  21.    // 通过条件操纵符来决定按钮的颜色
  22.   color : buttonMouseArea. pressed ?  Qt. darker (buttonColor ,  1.5 )  : buttonColor
  23. }

In FileMenu.qml:

Keeping with the simplicity of our application, the file dialog will always be visible and will not display invalid text files, which do not have a .txt extension to their filenames.

In FileDialog.qml:

编辑器能够设置其中文本的字体颜色属性。这里,TextEdit所在的区域位于一个flickable区域之内。如果文本过长,则能够供给滚动条的功能。函数ensureVisible()会自动检查光标所在位置,并移动文本到相应位置。QML使用JavaScript作为其剧本语言。正如前面提及的那样,JavaScript文件能够导入到QML文件中。

Similarly, we have the other properties declared according to their uses. The filesCount property indicates the number of files in a directory. The filename property is set to the currently selected file’s name and the loaded/saved file content is stored in fileContent property.

In FileMenu.qml:

  1. Button {
  2.   id : newButton
  3.   label :  "New"
  4.   onButtonClick : {
  5.     textArea. textContent  =  ""
  6.    }
  7. }
  8. Button {
  9.   id : loadButton
  10.   label :  "Load"
  11.   onButtonClick : {
  12.     directory. filename  = textInput. text
  13.     directory. loadFile ( )
  14.     textArea. textContent  = directory. fileContent
  15.    }
  16. }
  17. Button {
  18.   id : saveButton
  19.   label :  "Save"
  20.   onButtonClick : {
  21.     directory. fileContent  = textArea. textContent
  22.     directory. filename  = textInput. text
  23.     directory. saveFile ( )
  24.    }
  25. }
  26. Button {
  27.   id : exitButton
  28.   label :  "Exit"
  29.   onButtonClick : {
  30.      Qt. quit ( )
  31.    }
  32. }

Our FileMenu can now connect to their respective actions. The saveButton will transfer the text from the TextEdit onto the directory’s fileContent property, then copy its file name from the editable text input. Finally, the button calls the saveFile() function, saving the file. The sloadButton has a similar execution. Also, the New action will empty the contents of the TextEdit.

Further, the EditMenu buttons are connected to the TextEdit functions to copy, paste, and select all the text in the text editor.

你的浏览器没法表现图片

完成的文本编辑器

你的浏览器不支持图片表现

这个顺序能被用作简单的文本编辑器,可以接收文本编辑并保存到文件中。它也能从文件中读取文本。

Categories:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值