Qt Quick应用开发介绍 9

Chapter9 Interactive UI with Multiple Top-Level Windows 多个顶层窗口下的交互式UI

现在我们的程序需要添加一些方法来变得适合日常工作中的重用; 首先要有个button来退出; 其次, 要有top-level窗口来管理配置; 用户修改配置时, 程序应该检查变动, 让用户知道改动是否正确;

9.1 A Button

button用来退出程序, 打开窗口, 关闭窗口等; button应该有基本的可视化参数, 并且在点击时发送信号; button在接收到用户的输入后应该提供可视化的反馈; 一个button一般可以有多个特性; 有许多方式来实现一个button, 这里就描述一个适合我们需求的方式;

我们的button可以是一个简单的点击相应的有圆角的rectange; 前文中看到的element可以接受鼠标事件, 使得鼠标事件填充到整个元素的表面; 另外, 我们的button必须emit发送一个信号通知相关的程序部件告知点击事件; 我们需要使用QtQuick信号来实现;

之前已经看到过QtQuick的方法, 来实现一个handler处理属性变化;


1

2

3

4

5

6

7

8

9

Image
{

    id:
background

    source: "../content/resources/background.png"

    fillMode: "Tile"

    anchors.fill:
parent

    onStatusChanged: if (background.status
== Image.Error)

                         console.log("Background
image \""
 +

                         source
"\"
cannot be loaded"
)                                   

}

信号和属性变化通知很像; Signal handler工作起来也是一样, item显式地emit一个信号, 代替porperty change; Signal handler也可以接受信号参数, 和property change的handler不同, emit一个信号是一个方法的调用;

e.g. utils/Button.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

Rectangle
{

    id:
root

    property
string text: 
"Button"

    color: "transparent"

    width:
label.width + 15

    height:
label.height + 10

    border.width:
Style.borderWidth

    border.color:
pressedColor(Style.penColor)

    radius:
Style.borderRadius

    signal
clicked (variant mouse)

    signal
pressedAtXY (string coordinates)

    function pressedColor
(color) {

        return mouseArea.pressed
? Qt.darker(color, 5.0) : color

    }

    function logPresses
(mouse) {

        pressedAtXY
(mouse.x + 
"," +
mouse.y)

    }

    Component.onCompleted:
{

        mouseArea.clicked.connect(root.clicked)

    }

    Text
{

        id:
label

        anchors.centerIn:
parent

        color:
pressedColor(Style.penColor)

        text:
parent.text

        font.pixelSize:
Style.textPixelSize

    }

    MouseArea
{

        id:
mouseArea

        anchors.fill:
parent

        Connections
{

            onPressed:
logPresses(mouse)

        }

        //
this works as well instead of using Connections

        //
onPressed: logPresses(mouse)

    }

}

Button定义了两个信号: clicked和pressedAtXY; 两个信号使用不同方式来emit; pressedAtXY从JavaScript方法调用, 作为onPressed的handler; clicked直接连接到了MouseArea的clicked信号上; 两种方式都有各自的使用案例; 一个直接的signal-to-signal理解允许简单的信号转发; 这也是我们需要在Button中实现的: 在鼠标事件发生时像一个MouseArea一样反应; 一些其他案例中, 你可以在emit信号前添加一些方法,
比如logPresses;

Note 有个重点是信号的参数名称; 看看MouseArea的代码块, 你可能会好奇mouse参数是哪来的; 我们没有声明它, 它实际上属于MouseArea元素中clicked信号的定义; pressedAtXY信号也是一样, 定义了一个坐标coordinate参数; 所有使用Button和调用pressedAtXY信号的item必须使用准确的名字"coordinates"来获得这个参数: e.g.


1

2

3

4

5

6

7

Button
{

    id:
toggleStatesButton

//...

    onPressedAtXY:
{

        console.log
(
"pressed
at: "
 +
coordinates)

    }

}

Note 注意我们定义的clicked信号是这样的:


1

signal
clicked (variant mouse)

mouse是MouseEvent类型, 现在的QtQuick版本中, 信号参数只能是basic type http://qt-project.org/doc/qt-4.8/qdeclarativebasictypes.html 但是不用担心, 因为收到信号时参数类型可以转换;

关于QtQuick中使用signal的更多细节: QML Signal and Handler Event System: http://qt-project.org/doc/qt-4.8/qmlevents.html MouseArea http://qt-project.org/doc/qt-4.8/qml-mousearea.html MouseEvents http://qt-project.org/doc/qt-4.8/mouseevents.html 发现更多的可能性:
如得到其他mouse event, 追踪hovering事件, 实现drap-drop;

作为Button, 也要提供一些按键反馈; 我们可以稍稍变化下颜色, 使用JavaScript方法来修改颜色值:


1

2

3

function pressedColor
(color) {

    return mouseArea.pressed
? Qt.darker(color, 5.0) : color

}

可以在button的border上和label text上使用这个toggle颜色, 把方法的发挥值绑定到颜色属性上:


1

2

border.color:
pressedColor(Style.penColor)

color:
pressedColor(Style.penColor)

9.2 A Simple Dialog

Dialog是另一个我们需要的utility component; 我们使用它来通知用户; 我们的Dialog很简单, 它会在弹出所有元素的顶层, 显示text信息, 带有OKbutton;


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

Rectangle
{

    id:
root

    property
string message: 
"Error!
This is a long message with details"

    width:
100

    height:
40

    color:
Style.backgroundColor

    border.color:
Style.penColor

    border.width:
Style.borderWidth

    radius:
Style.borderRadius

    visible: true

    function show(text)
{

        root.message
= text;

        root.visible
true;

    }

    function hide()
{

        root.visible
false;

    }

    Text
{

        id:
messageText

        anchors.top:
parent.top

        anchors.topMargin:
Style.baseMargin

        anchors.left:
parent.left

        anchors.right:
parent.right

        horizontalAlignment:
Text.AlignHCenter

        wrapMode: "WordWrap"

        text:
root.message

        font.pixelSize:
Style.textPixelSize

        color:
Style.penColor

        onPaintedHeightChanged:
{

            root.height
= messageText.paintedHeight + okButton.height + 3*Style.baseMargin

        }

    }

    Button
{

        id:
okButton

        text:
qsTr(
"OK")

        anchors.top:
messageText.bottom

        anchors.topMargin:
Style.baseMargin

        anchors.horizontalCenter:
parent.horizontalCenter

        onClicked:
root.hide()

    }

}

Dialog作为一个child item, 从另一个元素中pop-up:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

Item
{

id:
root

...

Dialog
{

    id:
errorDialog

    width:
root.width

    anchors.centerIn:
parent

    z:
root.z+1

    visible: false

}

...

Button
{

    id:
exitButton

...

    onClicked:
{

...

        errorDialog.show
(qsTr(
"The
location cannot be empty"
));

...

    }

}

...

}

Dialog在加载的时候就初始化为可见的; 它出现在parent的顶部(前面代码中的root); z: root.z+1 完成了这个小把戏; 我们把z属性绑定到一个总是比root.z更大的值; 接下去可以调用show(), 带上信息来显示; show()使得Dialog可见, 然后把信息文字显示出来; 当用户点击OK, dialog就把自己隐藏起来;

Note TabWidget Example--Qt Document, 展示了以一种方式, 在其他element顶部动态地显示和隐藏element;

我们的Dialog还有几个有用的特性需要知道; 为了有效利用屏幕空间, 它copy了parent的宽度; 当Dialog打开一个长的text信息, 信息会根据Dialog的宽度换行wraps; 当高度由于wrapping而改变的时候, messageText元素会改变root Dialog的高度;


1

2

3

4

5

6

7

8

9

10

11

12

13

Rectangle
{

id:
root

...

Text
{

    id:
messageText

...

    onPaintedHeightChanged:
{

        root.height
= messageText.paintedHeight +

            okButton.height
+

            3
Style.baseMargin

    }

...

}

9.3 Checkbox

可以使用Text Input元素, 基于用户的输入来获取text或digit, 但我们需要一些其他的东西来处理on-off的设置类型; 通常使用的是check box UI元素; 在QtQuick(version1)里没有checkbox元素, 我们要拟写一个; 通过QtQuick我们可以方便地创建一个;


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

import
QtQuick 1.1

Item
{

    id:
root

    property
bool checked: 
true

    //
we should pre-set the size to get it working perperly in a positioner

    width:
checkBox.width

    height:
checkBox.height

    Image
{

        id:
checkBox

        source:
root.checked ?

                    "../content/resources/checkbox.png" :

                    "../content/resources/draw-rectangle.png"

        Keys.onPressed:
{

            if (event.key
== Qt.Key_Return ||

                    event.key
== Qt.Key_Enter ||

                    event.key
== Qt.Key_Space)

                root.checked
= !root.checked;

        }

        MouseArea
{

            anchors.fill:
parent

            onClicked:
{

                root.checked
= !root.checked;

           }

        }

    }

    //onValueChanged:
console.log ("value: " + root.value)

}

CheckBox基于Item, 它扩展了Item, 有一个boolean属性称为checked; 如果box被checked, checked是true, 否则为false; 整个visual实现由两个可以前后翻转的图片组成; 实现的方法是将source property(checkbox的图片)绑定到checkbox image或normal rectangle, source的值根据checked的值来变化;

下面是CheckBox在屏幕上看起来的样子, checked和unchecked:

接下去还有keyboard navigation处理的代码;

9.4 Handling Keyboard Input and Navigation 处理键盘输入和导航

用户交互的另一个重要方面是键盘输入和导航; 我们将通过了解基于新UI component的Configure模块来探索这块;

同时, 我们有多个hard-coded属性值, 但实际上应该是让用户来改变:

-天气预报的Location name

-多久的时间间歇Time interval来更新天气数据

-关闭秒和日期显示, 让时钟更紧凑

name和interval属性需要一个text input区域, 而最后一个开关可以用Checkbox实现;

Text input很直接: QtQuick提供Text Input元素; 可以用它获取天气预报位置的值, 和新的更新间隔的值; TextInput元素将键盘捕捉的输入和text属性绑定; 加载这个元素时, 根据locationTextInput和forecastUpdateInterval值预设到text属性, 我们把当前的设置显示给用户; 用户可以编辑这些, 我们也不用担心字符输入处理的细节:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

...

TextInput
{

    id:
locationTextInput

...

    width:
controlElements.width - locationTextInput.x - controlElements.spacing

    text:
locationText

    focus: true

}

...

TextInput
{

    id:
updateTextInput

...

    text:
forecastUpdateInterval

    maximumLength:
3

    //
we use IntValidator just to filter the input. onAccepted is not used here

    validator:
IntValidator{bottom: 1; top: 999;}

}

...

updateTextInput使用了一个validator来限制字符长度, 保证拿到的数字是正常范围内的;

Location的名字不需要validator, 但是还需要处理text input, 它会比updateTextInput里的数字长一点; 这个可以通过限制width达到目的, 保证长文字不会超出顶层item的边界; 如果没有设置width, TextInput会跟着输入的文字一起扩展, 超出可视化的边界;

Note 如果有一个multi-line的text给用户编辑, 你可以使用TextEdit元素;

locationTextInput会显式地接收到键盘焦点, 我们把focus设置为了true; 当Configure被加载的时候, 用户可以直接开始改变location的名字:

TextInput元素和新的CheckBox会对鼠标点击进行反应; 如果想要在鼠标之外加上键盘支持的navigation, 用户从一个input元素导航到另一个该怎么做? 如果想要在CheckBox上启动键盘输入又该怎么做?

QtQuick提供了按键navigation和raw key处理过程; 先看一眼key navigation;

为了支持key navigation, 这里对TextInput有些改变:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

TextInput
{

    id:
locationTextInput

...

    focus: true

    KeyNavigation.up:
offlineCheckBox

    KeyNavigation.down:
updateTextInput

}

...

TextInput
{

    id:
updateTextInput

...

    KeyNavigation.up:
locationTextInput

    KeyNavigation.down:
secondsCheckBox

}

locationTextInput通过设置focus属性为true来显式地拉取focus; KeyNavigation项提供了附加的属性, 监视按键和输入focus从一个element到另一个element的移动; 对我们的情况来说, KeyNavigation帮了大忙, 我们有很多元素, 需要组织好输入focus的移动;

上面的code sample中, 输入focus从一个locationTextInput移动到updateTextInput, 使用了down arrow键; 如果用户按下up键, focus从updateTextInput到locationTextInput移动回去; 我们把这些语句加到Configure组件中所有相关的元素中;

处理用户输入的时候, 有时会你需要捕获特定的键; 我们的例子中是Checkbox; 使用桌面程序的时候, 用户学到了使用space键可以toggle切换check box状态; 我们应该把这个功能做到程序中:

这里就要使用Key了; 基本上它是一种信号发射器, 对应几乎键盘上的键值; 信号有个KeyEvent参数, 包含keypress的详细信息; 在checkbox中使用Keys; 上面的代码块中使用了附加的 Keys.onPressed属性, 可以用Return, Enter和Space键切换checkbox的状态;

更多关于键盘输入处理的细节在Qt Doc: Keyboard Focus in QML

现在有了一个输入元素可以处理用户输入; 要完成Configure组件, 有个步骤还需完成-- 对要存储的新值的verification验证;

当用户点击exitButton, 我们需要检查新设置的值, 没问题的话把它们传递给应用; 这里也是放置Dialog来通知用户新值不正确的地方; 这种情况下. Configure不会关闭, 保持打开状态直到用户提供了正确的值; 查看exitButton的onClick处理代码:

components/Configure.qml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

Rectangle
{

    id:
root

    property
bool showSeconds: 
true

    property
bool showDate: 
true

    property
int forecastUpdateInterval: 5 
//
minutes

    property
string locationText: 
"Munich"

    property
bool forceOffline: 
false

    width:
320

    height:
480

    Image
{

        ...

    }

    Grid
{

        id:
controlElements

        spacing:
10

        ...

        Text
{

            id:
locationLabel

            ...   

        }

        TextInput
{

            id:
locationTextInput

            ...

            focus: true

            KeyNavigation.up:
offlineCheckBox

            KeyNavigation.down:
updateTextInput

        }

        Text
{

            id:
updateLabel

            height:
90

            text:
qsTr(
"update
interval: <br>(in min)"
)

            color:
updateTextInput.focus?

                       Qt.lighter(Style.penColor)
: Style.penColor

            font.pixelSize:
Style.textPixelSize

        }

        TextInput
{

            id:
updateTextInput

            ...

        }

        Text
{

            id:
secondsLabel

            ...

        }

        CheckBox
{

            id:
secondsCheckBox

            checked:
showSeconds

            ...

        }

        Text
{

            id:
dateLabel

            ...

        }

        CheckBox
{

            id:
dateCheckBox

            checked:
showDate

            ...

        }

        Text
{

            id:
offlineLabel

            ...

        }

        CheckBox
{

            id:
offlineCheckBox

            ...

        }

    }

    Dialog
{

        id:
errorDialog

        ...

        z:
root.z+1

        visible: false

    }

    Button
{

        id:
exitButton

        text:
qsTr(
"OK")

        ...

        onClicked:
{

            //
update interval and location cannot be empty

            //
update interval cannot be zero

            if (updateTextInput.text
== 
"" ||
updateTextInput.text == 0)

                errorDialog.show
(qsTr(
"The
update interval cannot be empty"
))

            else if (locationTextInput.text
== 
"")

                errorDialog.show
(qsTr(
"The
location cannot be empty"
))

            else {

                forecastUpdateInterval
= updateTextInput.text

                root.locationText
= locationTextInput.text

                root.visible
false

            }

            //
update check box relevant settings

            root.showSeconds
= secondsCheckBox.checked

            root.showDate
= dateCheckBox.checked

            root.forceOffline
= offlineCheckBox.checked

        }

    }

}

下一步

注意offlineCheckBox和相关的magical forceOffline设置; 这个新的设置是用来toggle应用的状态; 下一个版本的应用中我们也会看一下QtQuick的animation动画, 用它们来实现一些漂亮效果;

---9---

---YCR---

Qt Quick应用开发介绍 9

时间: 2024-11-07 17:20:05

Qt Quick应用开发介绍 9的相关文章

Qt Quick应用开发介绍 1-5

Qt Quick应用开发介绍 Introduction to Application Development with Qt Quick Release 1.0 Chapter1 Introduction 介绍 1.1 谁应该阅读这份教程 本教程解释了Qt Quick应用开发的基础以及使用示例代码帮助全面了解; 教程包含标准Qt Quick文档和基础概念, API以及详细的源码信息; 本教程是为了新接触Qt Quick的你准备的, 虽然从基础开始, 但你还是要熟悉编程的概念, 有JavaScri

Qt Quick应用开发介绍 10-12(动态界面, 实践学习, 总结和扩展)

Chapter10 UI Dynamics and Dynamic UI 动态界面 前面章节学习了在开发时添加item, 让它们invisible; 该怎么做可以让程序根据不同的数据和用户输入来有不同的显示? 这些变化可能比visibility复杂; 我们怎样才能做到让程序UI的动态变化更appealing吸引人, 甚至成为用户体验的一部分? 10.1 Using States 使用state 网络连接对于现在的版本中天气相关的部件是必须的; 它让网络数据可视化; 如果你的电脑不在线, 启动cl

Qt Quick应用开发介绍 6-8

Chapter6 Using JavaScript 使用JavaScript 在QtQuick中JavaScript可以有很多复杂和强大的用法; 实际上, QtQuick是被实现成一个JavaScript的扩展; JS基本可以在任何地方使用, 只要代码返回的值的类型和预期的一致; 此外, 使用JS是一部分处理应用逻辑和计算的代码的标准形式; 6.1 JavaScript is not JavaScript JS是从web开发产生的; 在那段时间内, JS快速成长为许多受欢迎和优秀的扩展, add

Qt Quick应用开发介绍 13 (JavaScript)

Chapter13 Annexure: JavaScript Language Overview 附录: JavaScript语言概览 Js语言总览; 提供一个Qt支持的所有语言特性的概览; 通过本文了解Js语言的基本特性; 特别是当你开始学习一个相关的技术, 如QML时, 你可以在这获得帮助; 这篇文章是对 JavaScript Language Overview http://qt-project.org/wiki/JavaScript 的轻微改动版本; 内容经过Qt4.8和QtQuick1

Qt on Android: Qt Quick 之 Hello World 图文详解

在上一篇文章,<Qt on Android:QML 语言基础>中,我们介绍了 QML 语言的语法,在最后我们遗留了一些问题没有展开,这篇呢,我们就正式开始撰写 Qt Quick 程序,而那些问题,随着本系列文章的展开也会一一被干掉. 在开始介绍 Qt Quick 应用的基本元素之前,我们先来创建一个 HelloQtQuickApp 项目,就是经典的 Hello World 了. 笔者的教程最终会面向 Qt Quick 与 C++ 混合编程,所以我们 HelloQtQuickApp 从零开始.

Qt Quick 简单介绍

Qt Quick 是 Qt 提供的一种高级用户界面技术.使用它可轻松地为移动和嵌入式设备创建流畅的用户界面. 在 Android 设备上, Qt Quick 应用默认使用 OpenGL ES ,渲染效率很高,你能够用它创建很炫很酷很迷人的界面. Qt Quick 模块是开发 QML 应用的标准库,提供了使用 QML 创建用户界面所需的一切东西,包括可视化类型.交互类型.动画.模型与视图.粒子效果与着色效果等等. Qt Quick 提供了两组 API : QML API ,它提供了使用 QML 语

QT开发(五十五)———Qt Quick Controls

QT开发(五十五)---Qt Quick Controls 一.Qt Quick Controls基础 QT5.1发布了Qt Quick的一个全新模块:Qt Quick Controls.Qt Quick Controls模块提供了大量类似Qt Widgets模块的可重用组件. 为了开发基于Qt Quick Controls的程序,需要创建一个Qt Quick Application类型的应用程序,选择组件集的时候注意选择Qt Quick Controls. 二.Qt Quick Control

Qt Quick 布局介绍

在 Qt Widgets 中,我们经常使用布局管理器来管理界面上的众多 widgets .在 Qt Quick 中也有这么一套与布局管理器类似的东西,叫作 Item Positioner .我还是沿用使用 Qt widgets 编程时的叫法,称它们为布局管理器. Qt Quick 提供这么几种常用的布局方式: anchors ,锚布局 Row ,行布局 Column ,列布局 Grid ,表格布局 Flow ,流式布局 咱们一个一个来看. 请给我的参赛文章<Qt Quick 事件处理之信号与槽>

Qt on Android: Qt Quick事件处理之鼠标、键盘、定时器

在<Qt on Android: Qt Quick 事件处理之信号与槽>中介绍了 QML 中如何使用内建类型的信号以及如何自定义信号,这次我们来看看如何处理鼠标.键盘.定时器等事件.这些时间在处理时,通常是通过信号来完成的. 广而告之:我正在参加 CSDN 博文大赛,请给我的参赛文章<Qt on Android: Qt Quick 事件处理之信号与槽>投票,谢谢. 鼠标事件处理 桌面开发的话,难免要处理鼠标事件-- 变色矩形示例 看一个简单的处理鼠标事件的例子,先看代码(handl