QtQuick桌面应用开发指导 3)实现UI和功能_B 4)动态管理Note对象_A
来源:互联网 发布:a算法八数码 编辑:程序博客网 时间:2024/06/06 14:11
3.2 把Page Item和Marker Item绑定
之前我们实现了PagePanel组件, 使用了三个state来切换Page组件的opacity属性; 这一步我们会使用Marker和MarkerPanel组件来实现页面导航;
在原型阶段, MarkerPanel组件十分简单, 没有任何功能; 它使用了Repeater类型来产生三个QML Item以及Marker组件作为delegate;
MarkerPanel应该存储当前激活的marker(标记), 即那个被用户点击的marker; 基于MarkerPanel中激活的marker, PagePanel会更新它的state属性; 我们需要将PagePanel的state属性和MarkerPanel新的属性--持有当前激活marker的属性绑定起来;
在MarkerPanel中定义一个string属性--activeMarker;
// MarkerPanel.qml
Item {
id: root
width: 150; height: 450
// a property of type string to hold
// the value of the current active marker
property string activeMarker:
"personal"
//...
我们可以把一个markerid值存储起来, 用来唯一地识别marker item; 这样, activeMarker会持有用户所点击的marker item的markerid的值,
根据model, Repeater元素可以产生三个marker item, 因此我们可以使用一个model来存储markerid值, 然后在Repeater中使用;
// MarkerPanel.qml
Item {
id: root
width: 150; height: 450
// a property of type string to hold
// the value of the current active marker
property string activeMarker:
"personal"
// a list for holding respective data for a Marker item.
property variant markerData: [
{ markerid:
"personal"
},
{ markerid:
"fun"
},
{ markerid:
"work"
}
]
Column {
id: layout
anchors.fill: parent
spacing: 5
Repeater {
// using the defined list as our model
model: markerData
delegate: Marker {
id: marker
// handling the clicked signal of the Marker item,
// setting the currentMarker property
// of MarkerPanel based on the clicked Marker
//MouseArea {
//anchors.fill: parent
onClicked: root.activeMarker = modelData.markerid
//}
}
}
}
}
上述代码中我们在onClicked signal handler中设置了 activeMarker属性; 这意味着我们已经在Marker组件中定义了一个clicked() signal来通知用户的鼠标点击事件;
// Marker.qml
Item {
id: root
width: 50; height: 90
signal clicked()
MouseArea {
id: mouseArea
anchors.fill: parent
// emitting the clicked() signal Marker item
onClicked: root.clicked()
}
}
目前我们以后有了PagePanel组件使用state属性来管理page, 让MarkPanel组件可以识别激活的marker, 因此, 切换各个page的可见性可以通过改变page的opacity属性来做到;
来看看怎样使用 activeMarker属性来对应地更新PagePanel的state;
在main.qml里面, 已经有了Page item和 MarkerPanel定位好了, 我们会创建以及使用PagePanel item而不是各自使用anchor定位;
// creating a MarkerPanel item
MarkerPanel {
id: markerPanel
width: 50
anchors.topMargin: 20
anchors {
right: window.right
top: window.top
bottom: window.bottom
}
}
//...
// creating a PagePanel item
PagePanel {
id: pagePanel
// binding the state of PagePanel to the
// activeMarker property of MarkerPanel
state: markerPanel.activeMarker
anchors {
right: markerPanel.left
left: toolbar.right
top: parent.top
bottom: parent.bottom
leftMargin: 1
rightMargin: -50
topMargin: 3
bottomMargin: 15
}
}
上面代码中, 我们可以看到QML的 property binding特性, 可以把state属性和activeMarker属性绑定起来; 这样不论activeMarker通过用户操作获得了什么值, 相同的值会被分配给PagePanel的state属性, 这样就能开关各个page的可见性了;
下一步
给出怎样使用使用图形来强化UI的细节;
3.3 添加graphics(图形)
因为QML的特性, 开发和设计完全可以一起紧密工作, 贯彻整个开发生命期; 如今, 使用graphics让用户体验有了很大的不同, 这也是程序让用户感受到的地方;
QML鼓励在UI实现过程中尽可能地使用graphics; 使用QML让图形设计和开发之间的协作更容易, 设计可以立刻在基本的UI元素上测试graphics; 这帮助设计来理解在开发新的组件时, 程序员会需要什么, 这也让程序的UI更有吸引力而且某种程度上更易维护;
3.3.1 给组件设置背景图片
BorderImage类型推荐使用的情况是: 在你想要把一个图片scale(按比例拉伸), 但它的border(边界)保持不变的时候; 这种类型的一个好的用例(use case)是在QML item上有阴影效果的背景图片; 你的item可能会在某些时刻scale但是需要保持corners(四角)不变;
来看下怎样在组件中将BorderImage设置成背景;
// PagePanel.qml
//...
BorderImage {
id: background
// filling the entire PagePanel
anchors.fill: parent
source:
"images/page.png"
// specifying the border margins for each corner,
// this info should be given by the designer
border.left: 68; border.top: 69
border.right: 40; border.bottom: 80
}
// Note.qml
BorderImage {
id: noteImage
anchors { fill: parent}
source:
"images/personal_note.png"
border.left: 20; border.top: 20
border.right: 20; border.bottom: 20
}
Warning: 注意BorderImage类型要以正确的次序在组件里面使用, 因为实现的次序定义了显示的顺序; 具有相同 z值的item显示的次序是按它们被声明的次序决定的; 更多细节参考stack ordering of items-- z property;
当这些item都在MarkerPanel组件中创建的时候, 怎样才是给Marker item设置背景的最佳方案--这个方案已经在MarkerPanel中展现了;
这里有个markerData list, 把它作为model给Repeater来创建Marker item, 当一个marker item被点击的时候, 设置markerid作为activeMarker; 我们可以扩展markerData, 存储一个图像的的url路径, 使用Image类型作为Marker组件的顶层类型;
// Marker.qml
// The Image type as top level is convenient
// as the Marker component simply is a graphical
// UI with a clicked() signal.
Image {
id: root
// declaring the clicked() signal to be used in the MarkerPanel
signal clicked()
// creating a MouseArea type to intercept the mouse click
MouseArea {
id: mouseArea
anchors.fill: parent
// emitting the clicked() signal Marker item
onClicked: root.clicked()
}
}
这样就可以增强MarkerPanel组件;
// MarkerPanel.qml
//...
// for the markerData, we add the img value pointing to the image url
property variant markerData: [
{ img:
"images/personalmarker.png"
, markerid:
"personal"
},
{ img:
"images/funmarker.png"
, markerid:
"fun"
},
{ img:
"images/workmarker.png"
, markerid:
"work"
}
]
Column {
id: layout
anchors.fill: parent
spacing: 5
Repeater {
// using the defined list as our model
model: markerData
delegate: Marker {
id: marker
// binding the source property of Marker to that
// of the modelData' s img value.
// note that the Marker is an Image element
source: modelData.img
// handling the clicked signal of the Marker item,
// setting the currentMarker property
// of MarkerPanel based on the clicked Marker
onClicked: root.activeMarker = modelData.markerid
}
}
}
上述代码中, 可以看到Marker item的source属性是如何绑定到markerData model的image值的;
我们使用了BorderImage类型来为NoteToolbar组件设置背景, 也作为main.qml的顶层类型;
Note 关于图像的border margins, 以及图像的如何anchor和align(对齐), 要和graphics设计讨论清楚;
MarkerPanel组件看起来是这样的:
然后来看看怎样在原型阶段使用graphics按照设计来增强toolbar;
3.3.2 创建Tool组件
基于代码重用考虑, 定义一个新组件给toolbar中的New Note和Clear All工具使用; 这是为什么我们已经实现了一个Tool组件, 使用Image类型作为顶层类型, 处理鼠标点击事件;
Image类型经常用作UI元素自身, 不论是静态的或是动画图像; 它会按像素布局, 可以很好地按照设计需求来定义;
// Tool.qml
// Use Image as the top level type
Image {
id: root
// defining the clicked signal
signal clicked()
// using a MouseArea type to capture
// the mouse click of the user
MouseArea {
anchors.fill: parent
// emitting the clicked() signal of the root item
onClicked: root.clicked()
}
}
现在用Tool组件来创建toolbar; 我们从原型阶段修改代码, 用Tool item代替Rectangle元素;
//main.qml
//...
// toolbar background
Rectangle {
anchors.fill: toolbar
color:
"white"
opacity: 0.15
radius: 16
border { color:
"#600"
; width: 4 }
}
// using a Column element to layout
// the Tool items vertically
Column {
// sidebar toolbar
id: toolbar
spacing: 16
anchors {
top: window.top
left: window.left
bottom: window.bottom
topMargin: 50
bottomMargin: 50
leftMargin: 8
}
// new note tool
Tool {
id: newNoteTool
source:
"images/add.png"
}
// clear page tool
Tool {
id: clearAllTool
source:
"images/clear.png"
}
}
现在我们给我们的组件设置了所有的graphics, 程序应该有了更吸引人的外观和更多定义好的UI了;
下一步
下一章详细介绍如何动态地创建和管理Note item以及如何在本地数据库存储它们;
---3End---
CHAPTER4 动态管理Note对象
我们目前看到的QML是一个非常强大的声明性(declarative)语言, 和JavaScript组合使用让它更强大; QML不仅提供了 inline JavaScript, 而且还可以把整个JavaScript库导入到文件中;
NoteApp的核心功能是可以让用户去创建, 修改和删除note, 但是程序应该也要自动存储note, 无需提示;
这一章会指导如何使用JavaScript来给QML代码添加逻辑, 实现本地存储--Qt Quick Local Storage
本章主要主题:
- 使用JavaScript实现动态对象管理的功能;
- 如何使用 Qt Quick Database API 进行本地数据存储;
4.1 创建和管理Note Item
用户应该随时可以创建和删除note, 这意味着我们的代码应该可以动态地创建和删除Note item; 有多种方式创建和管理QML对象; 事实上, 我们已经使用一种--Repeater类型; 创建一个QML对象意味着在创建组件的实例之前, 组件必须要被创建和加载起来;
QML对象可以通过 createObject(Item parent, object properties) JavaScript方法在组件上创建; 更多细节参考 Dynamic Object Management in QML http://qt-project.org/doc/qt-5/qtqml-javascript-dynamicobjectcreation.html
4.1.1 Note对象的动态创建
我们知道一个Note item是属于Page组件的, 它负责note对象的创建, 也会从数据库中读取笔记;
如前面所说, 首先加载Note组件:
// Page.qml
//...
// loading the Note Component
Component {
id: noteComponent
Note { }
}
现在我们来定义一个Javascript方法, 创建QML Note对象; 创建QML对象的时候, 必须保证一个参数是这个对象的parent; 在Page组件中持有一个Note item容器(container)是个管理note对象的好主意, 这样我们可以在数据库中保存这些note;
// Page.qml
//...
// creating an Item element that will be used as a note container
Item { id: container }
// a Javascript helper function for creating QML Note objects
function
newNoteObject(args) {
// calling the createObject() function on noteComponent item
// and the container item will be the parent of the new
// object and args as the set of arguments
var
note = noteComponent.createObject(container, args)
if
(note ==
null
)
console.log(
"note object failed to be created!"
)
}
在前面显示的代码中, 我们看到一个新的 note item对象是怎样在 newNoteObject()方法中创建的; 新建的 note对象隶属于container item;
现在我们要在toolbar上的new note tool被按下的时候调用这个方法, toolbar在main.qml中; 由于PagePanel组件知道当前可见的page item, 我们可以在PagePanel中创建一个新的属性来存储那个page;
// PagePanel.qml
//...
// this property holds the current visible page
property Page currentPage: personalpage
// creating the list of states
states: [
// creating a state item with its corresponding name
State {
name:
"personal"
PropertyChanges {
target: personalpage
opacity:1.0
restoreEntryValues:
true
}
PropertyChanges {
target: root
currentPage: personalpage
explicit:
true
}
},
State {
name:
"fun"
PropertyChanges {
target: funpage
opacity:1.0
restoreEntryValues:
true
}
PropertyChanges {
target: root
currentPage: funpage
explicit:
true
}
},
State {
name:
"work"
PropertyChanges {
target: workpage
opacity:1.0
restoreEntryValues:
true
}
PropertyChanges {
target: root
currentPage: workpage
explicit:
true
}
}
]
我们修改了三个state来给currentPage属性设置合适的值;
在main.qml中, 看看在new note tool被点击的时候怎样调用方法来创建新的note对象;
// main.qml
// using a Column element to layout the Tool items vertically
Column {
id: toolbar
spacing: 16
anchors {
top: window.top; left: window.left; bottom: window.bottom
topMargin: 50; bottomMargin: 50; leftMargin: 8
}
// new note tool, also known as the plus icon
Tool {
id: newNoteTool
source:
"images/add.png"
// using the currentPage property of PagePanel and
// calling newNoteObject() function without any arguments.
onClicked: pagePanel.currentPage.newNoteObject()
}
4.1.2 删除Note对象
删除Note对象是个更直接的过程, 因为QML item类型提供了一个JavaScipt方法--destroy() http://qt-project.org/doc/qt-5/qtqml-javascript-dynamicobjectcreation.html#deleting-objects-dynamically ; 因为我们已经有一个container item的children是Note item, 我们可以简单地对children逐个地调用 destroy;
在Page组件上, 定义一个方法来为我们执行操作:
// Page.qml
//...
// a JavaScript helper function for iterating through the children elements of the
// container item and calls destroy() for deleting them
function
clear() {
for
(
var
i=0; i<container.children.length; ++i)
container.children[i].destroy()
}
在main.qml文件中, 我们在clear tool被按下时调用 clear()方法:
//...
// the clear tool
Tool {
id: clearAllTool
source:
"images/clear.png"
onClicked: pagePanel.currentPage.clear()
}
为了让用户可以独立地删除每一个note, 我们在NoteToolbar组件中为Note组件添加了一个tool; 可以使用签名实现的Tool组件:
// Note.qml
//...
// creating a NoteToolbar that will be anchored to its parent
NoteToolbar {
id: toolbar
height: 40
anchors { top: root.top; left: root.left; right: root.right }
// using the drag property alias to set the drag.target to our Note item.
drag.target: root
// creating the delete tool for deleting the note item
Tool {
id: deleteItem
source:
"images/delete.png"
onClicked: root.destroy()
}
}
下一步
关于如何在本地数据库存储note item的详细步骤;
4.2 从数据库存储和读取数据
目前我们实现了NoteApp的功能: 实时地创建和管理note item;
这里我们会了解在本地数据库存储note的详细实现; QML提供了一个简单的 Qt Quick Local Storage API, 使用SQLite数据库来实现我们想要的功能;
首选的方式是在NoteApp程序启动的时候从数据库读取note, 然后在程序关闭的是保存它们; 用户不会收到提示;
4.2.1 定义数据库
NoteApp的数据库很简单; 它只有一个table--note table, 包含我们所保存的note的信息;
看一下Table的定义, 让我们了解下Note组件的哪些属性或哪些新的数据应该被引入:
x和y是每个QML item都有的几何属性; 从Note item获得这些值很简单; 这些值会用来粗糙你note在page中的位置; noteText是note的实际文字, 我们可以从Note组件中的Text元素中获取它们, 我们应该定义一个alias(别名_)--noteText; noteId和markerId是每个note item都该有的标识符; noteId是一个唯一的标识符, 数据库需要用到, markerId用来标识note item属于哪一个page; 因此我们会在Note组件里面添加两个新的属性;
// Note.qml
Item {
id: root
width: 200; height: 200
property string markerId;
property int noteId;
property alias noteText: editArea.text
//...
// creating a TextEdit
TextEdit {
id: editArea
//...
考虑到Page组件负责创建Note item, 它应该也知道Note和哪个markerId相关联; 当一个新的Note item创建出来(不是从数据库读取), Page应该设置好Note的markerId属性;
使用一个JavaScript的 helper方法:
// Page.qml
Item {
id: root
// this property is held for helping store
// the note items in the database
property string markerId
// this Javascript helper function is used to create,
// Note items not loaded from database so that it will set
// the markerId property of the note.
function
newNote() {
// calling the newNoteObject and passing the a set of
// arguments where the markerId is set.
newNoteObject( {
"markerId"
: root.markerId } )
}
//...
先前在main.qml中, 我们使用了 newNoteObject()方法, 但就如前面解释的, 我们需要用newNote()方法代替它来达到目的;
现在每个Page组件有个markerId属性, 可以在Note item被创建的时候设置markerId; 我们必须保证page的markerId属性在Page item在PagePanel组件中创建的时候就被设置了;
// PagePanel.qml
//...
// creating three Page items that are anchored to fill the parent
Page { id: personalpage; anchors.fill: parent; markerId:
"personal"
}
Page { id: funpage; anchors.fill: parent; markerId:
"fun"
}
Page { id: workpage; anchors.fill: parent; markerId:
"work"
}
目前我们保证的:
- 从对应的关系数据库[relational database http://en.wikipedia.org/wiki/Relational_database ]得到的note和page之间的关系是正确的;
- 每个Note item有一个唯一的ID, ID属于page, 可以识别marker ID;
- 这些属性值要被正确设置;
下面来读取和存储笔记;
---TBC---
- QtQuick桌面应用开发指导 3)实现UI和功能_B 4)动态管理Note对象_A
- QtQuick桌面应用开发指导 4)动态管理Note对象_B 5)外观加强 6)更多改进
- QtQuick桌面应用开发指导 1)关于教程 2)原型和设计 3)实现UI和功能_A
- QtQuick桌面应用开发指导 7)创建应用 8)扩展
- Qt4.8使用QtQuick开发桌面应用程序
- 利用QtQuick实现UI的一个Demo
- iOS开发-UI-应用管理实现九宫格界面
- QtQuick实现文本编辑和显示文本行号
- QtQuick UI 问答
- 实现桌面应用图标的动态隐藏和显示,触发事件为SIM卡的状态
- QtQuick动态创建组件
- 基于wpa_supplicant库的WIFI连接功能实现--应用层碎片式对象内存管理算法
- 基于node webkit 开发的桌面笔记管理应用。
- android积累----桌面UI开发
- qtquick创建UI界面文件
- 【Android 开发】:UI控件之 ImageView 实现适屏和裁剪图片的功能
- 【Android 开发】:UI控件之 ImageView 实现图片旋转和缩放功能
- QtQuick实现文本编辑和显示文本行号-改进版
- 飘逸的python - __get__ vs __getattr__ vs __getattribute__以及属性的搜索策略
- Jaxb处理java对象和xml之间转换常用的annotation
- 运行vs编译出的exe文件
- <objc/runtime.h>中Associative机制(在button以及block传值)
- JDBC总结
- QtQuick桌面应用开发指导 3)实现UI和功能_B 4)动态管理Note对象_A
- 如何在 AutoLayout 中使用 UIScrollView (多个ContentView)
- 解决webBrowser 访问出现"为了保护您的安全" 的警告 和 开启IIS目录浏览功能
- Android Studio 快捷键整理分享
- 写个汉诺塔程序
- 路径问题
- opengl绘制花环(点的个数可以手动输入) c 实现代码
- HotSpot虚拟机对象探秘
- Oracle中的分区表