开发停车应用程序
提供逐步说明,说明如何为 Neptune 3 UI 开发停车应用程序。
简介
本教程向您展示如何通过显示指定区域内可用停车场的数量来逐步构建停车应用程序,并使用一些静态数据。
本教程分为几个章节
第1章:设计和实现应用程序
我们从 Main.qml
文件开始,其中我们导入所需的模块。除了 Qt Quick 之外,我们还需要一些强制性导入
application.windows
- 对于 ApplicationCCWindow 是必需的shared.Sizes
- 是一个附加属性,其中包含一些用于 Neptune 3 UI 的大小值shared.animations
- 对于 Neptune 3 UI 中的某些动画是必需的
import application.windows 1.0 import shared.Sizes 1.0 import shared.Style 1.0 import shared.controls 1.0 ApplicationCCWindow { id: root property bool parkingStarted: false Item { x: root.exposedRect.x y: root.exposedRect.y width: root.exposedRect.width height: root.exposedRect.height
我们使用 ApplicationCCWindow 作为停车应用程序的根元素,因为应用程序在中央控制台上显示。在 ApplicationCCWindow 之上有一个项目,用于承载内容。当应用程序启动时,我们使用一些专用 API 在中央控制台上保留一个矩形区域。
exposedRect
属性保存暴露给用户的窗口区域。这是不占用其他 UI 元素的区域。
一旦应用程序已保留此区域,让我们开始工作 UI。
设置 UI
您可以从现有附加的属性中使用一些属性,以及 Neptune 3 UI 动画。其中之一是
属性 | 描述 |
---|---|
Sizes | Sizes.dp() 是一个函数,用于在 Neptune 3 UI 的窗口正在调整大小时保留 UI 像素密度。此函数将像素值从参考像素密度转换为当前密度。此外,Sizes.dp() 会对给定的像素值应用当前缩放因子,有效地将其转换为设备像素(dp)。有时,此函数还可以将像素向上舍入到最接近的整数,以最大限度地减少锯齿伪影。注意:一些字体大小,如 |
样式 | 样式 是一个附加属性,提供了与UI样式相关的值,如当前选定的主题、颜色和不透明度级别。 |
动画 | 有几个默认动画和平滑动画,当您需要在UI上应用此行为时可以使用它们。它们包含预定义的值,以保持Neptune 3 UI中任何移动对象的动画一致性。 |
通常,Neptune 3 UI应用程序分为两部分:上部内容和下部内容。这是我们用于音乐应用和日历应用的设计理念。现在,对于停车应用
- 上部内容是用于停车票
- 下部内容用于显示详细信息
填写上部内容
由于上部内容有背景,我们使用Image
作为它的根元素并设置背景源。为了返回正确的图像源,我们使用Style.image("app-fullscreen-top-bg", Style.theme)
。 Style.image()
是一个函数,需要图像文件名和当前选中主题。在Neptune 3 UI中,我们支持两种主题:暗色(默认)和亮色。对于我们使用的每个资产,我们必须提供两个文件;每个主题一个。
Image { id: topContent width: parent.width height: Sizes.dp(500) source: Style.image("app-fullscreen-top-bg", Style.theme) Label { text: qsTr("No active parking tickets") anchors.centerIn: parent font.weight: Font.Light opacity: !root.parkingStarted ? 1.0 : 0.0 Behavior on opacity { DefaultNumberAnimation {} } } Image { width: root.width * 0.8 height: topContent.height source: "assets/ticket_bg.png" anchors.top: parent.top anchors.right: parent.right anchors.rightMargin: root.parkingStarted ? 0 : - width * 0.85 Behavior on anchors.rightMargin { DefaultNumberAnimation {} } Column { anchors.left: parent.left anchors.leftMargin: Sizes.dp(130) anchors.verticalCenter: parent.verticalCenter spacing: Sizes.dp(80) opacity: root.parkingStarted ? 1.0 : 0.0 Behavior on opacity { DefaultNumberAnimation {} } Label { text: qsTr("Zone \nParking Olympia") font.weight: Font.Light color: "black" } Label { text: "1275" opacity: Style.opacityLow font.weight: Font.Bold font.pixelSize: Sizes.fontSizeXXL color: "black" } } Rectangle { id: ticketContent property date currentTime: new Date() width: parent.width / 2 height: Sizes.dp(425) anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.verticalCenterOffset: Sizes.dp(-12) color: Style.accentColor opacity: root.parkingStarted ? 1.0 : 0.0 Behavior on opacity { DefaultNumberAnimation {} } onOpacityChanged: { if (opacity === 1.0) { ticketContent.currentTime = new Date() } } Column { anchors.left: parent.left anchors.leftMargin: Sizes.dp(60) anchors.top: parent.top anchors.topMargin: Sizes.dp(80) spacing: Sizes.dp(45) Label { text: "Started: \ntoday " + Qt.formatDateTime(ticketContent.currentTime, "hh:mm") font.weight: Font.Light opacity: Style.opacityHigh color: "black" } Label { text: qsTr("2h, 14 minutes") font.weight: Font.Light opacity: Style.opacityHigh color: "black" } Label { text: "2.29 $" font.weight: Font.Light opacity: Style.opacityHigh color: "black" } } } } }
然后,我们添加一些详细信息以指示没有购买活动停车票的情况。如果有活动的停车票,我们可以显示该停车票资产。基于Neptune 3 UI的设计,我们需要通过从右向左移动来为活动的停车票添加动画。这是通过以下代码实现的
anchors.rightMargin: root.parkingStarted ? 0 : - width * 0.85 Behavior on anchors.rightMargin { DefaultNumberAnimation {} }
我们使用默认数字动画{},这是一个预定义的动画,支持我们的需求。当停车票活动时,启用parkingStarted
属性。此属性将停车票的边距及其行为应用于停车票,我们也在该组件中定义了这种行为。
填写下部内容
下部内容显示停车票的详细信息,如停车区、价格、位置,以及启动按钮以开始停车票。我们使用行和列组件来放置所有要求的标签。
Item { width: parent.width height: parent.height - topContent.height anchors.top: topContent.bottom Row { anchors.top: parent.top anchors.topMargin: Sizes.dp(60) anchors.left: parent.left anchors.leftMargin: Sizes.dp(50) spacing: Sizes.dp(200) Column { spacing: Sizes.dp(50) Label { text: qsTr("Zone") font.weight: Font.Light opacity: Style.opacityMedium font.pixelSize: Sizes.fontSizeL } Row { spacing: Sizes.dp(60) Column { Label { text: qsTr("Every day 12 - 22") font.weight: Font.Light font.pixelSize: Sizes.fontSizeS opacity: Style.opacityMedium } Label { text: qsTr("Other times") font.weight: Font.Light font.pixelSize: Sizes.fontSizeS opacity: Style.opacityMedium } Label { text: qsTr("Service fee") font.weight: Font.Light font.pixelSize: Sizes.fontSizeS opacity: Style.opacityMedium } } Column { Label { text: qsTr("1.5 $ / started hour") font.weight: Font.Light font.pixelSize: Sizes.fontSizeS opacity: Style.opacityMedium } Label { text: qsTr("1 $ / started hour") font.weight: Font.Light font.pixelSize: Sizes.fontSizeS opacity: Style.opacityMedium } Label { text: "0.29 $" font.weight: Font.Light font.pixelSize: Sizes.fontSizeS opacity: Style.opacityMedium } } } } Column { spacing: Sizes.dp(250) Label { anchors.right: parent.right text: qsTr("1275, Parking Olympia") font.weight: Font.Light opacity: Style.opacityMedium } Button { id: startButton implicitWidth: Sizes.dp(250) implicitHeight: Sizes.dp(70) font.pixelSize: Sizes.fontSizeM checkable: true checked: root.parkingStarted text: !root.parkingStarted ? qsTr("Start") : qsTr("End (2.29 $)") background: Rectangle { color: { if (startButton.checked) { return "red"; } else { return "green"; } } opacity: { if (startButton.pressed) { return 0.1; } else if (startButton.checked) { return 0.3; } else { return 0.3; } } Behavior on opacity { DefaultNumberAnimation {} } Behavior on color { ColorAnimation { duration: 200 } } radius: width / 2 } onClicked: root.parkingStarted = !root.parkingStarted } } } }
在上面的代码片段中,启动按钮
是一个有趣的例子。由于Neptune 3 UI主要使用QtQuickControls 2,我们可以为所有按钮预先定义一个默认样式。然而,在这个停车应用中,我们自定义了我们的按钮并使用具有不同颜色和行为的背景。
注意:要查看可用的按钮类型,请在Neptune 3 UI中启动表格应用。
添加清单文件
当我们准备好运行应用程序时,我们需要添加一个包含以下行的info.yaml
清单文件
formatVersion: 1 formatType: am-application --- id: 'chapter1-basics' icon: 'icon.png' code: 'Main.qml' runtime: 'qml' name: en: 'Parking'
我们需要为停车应用指定一个图标,用于在系统UI应用程序启动器中显示,一旦安装在不同应用程序中。有关info.yaml
的更多信息,请参阅Neptune 3 UI - 应用开发和清单定义。
添加项目文件
接下来,我们还需要创建一个项目文件.pro
,它指定停车应用项目如下
TEMPLATE = aux FILES += info.yaml \ icon.png \ Main.qml assets.files += assets/* assets.path = $$[QT_INSTALL_EXAMPLES]/neptune3-ui/chapter1-basics/assets app.files = $$FILES app.path = $$[QT_INSTALL_EXAMPLES]/neptune3-ui/chapter1-basics INSTALLS += app assets AM_MANIFEST = info.yaml AM_PACKAGE_DIR = $$app.path load(am-app)
如果您使用Qt Creator并安装了Qt Creator插件用于Qt应用管理器,您可以直接在Neptune 3 UI的系统UI中部署和运行应用。为此,请按照以下步骤操作
- 在Qt Creator中打开您的
.pro
文件。 - 在项目视图中,在构建和运行下,选择运行。确认您的配置值与下面显示的值匹配。
当项目准备就绪后,按Ctrl+R键在Neptune 3 UI中运行停车场应用。
注意:在部署和运行停车场应用之前,请确保Neptune 3 UI正在运行。
第二章:扩展停车场应用并集成意图和通知
在本章中,我们将学习如何扩展我们的停车场应用并集成意图和通知。目前,该应用仅显示静态数据,并允许您以最少的动画开始和停止停车会话。
从Qt应用管理器集成意图
Qt应用管理器能够通过发送信号并期望得到响应值(信息)的方式,使一个应用能够与另一个应用或系统UI进行通信。
假设我们需要能够调用一个假想的Neptune支持团队,该团队管理停车票服务。我们可以添加一个按钮来进行这样的调用。记住,在Neptune 3 UI中,有一个内置的电话应用。我们可以向电话应用发送命令来发起这个电话。
让我们先添加一个新的电话按钮
Button { implicitWidth: Sizes.dp(250) implicitHeight: Sizes.dp(70) anchors.left: parent.left anchors.leftMargin: Sizes.dp(100) anchors.top: parent.top anchors.topMargin: Sizes.dp(340) font.pixelSize: Sizes.fontSizeM text: qsTr("Call for support") onClicked: sendIntent(); function sendIntent() { var appId = "com.pelagicore.phone"; var request = IntentClient.sendIntentRequest("call-support", appId, {}); request.onReplyReceived.connect(function() { if (request.succeeded) { var result = request.result console.log(Logging.apps, "Intent result: " + result.done) } else { console.log(Logging.apps, "Intent request failed: " + request.errorMessage) } }); } }
点击此按钮时,它会向电话应用发送一个call-support
请求,并调用Neptune支持团队。由于我们期望收到回复消息,电话应用会发送一个回复,指出命令是否成功接收。
为了使电话应用能够接收意图请求,需要有一个意图处理器可用。
此外,此"call-support"意图必须在info.yaml
文件中注册。
formatVersion: 1 formatType: am-package --- id: 'com.pelagicore.phone' icon: 'icon.png' name: en: 'Phone' de: 'Telefon' cs: 'Telefon' ru: 'Телефон' zh: '电话' ja: '電話' ko: '전화' applications: - id: 'com.pelagicore.phone' code: 'Main.qml' runtime: 'qml' applicationProperties: { private: { squishPort: 7728 } } intents: - id: call-support - id: activate-app categories: [ 'phone', 'widget' ]
如上所示的电话应用info.yaml文件所示,已注册了"call-support"。然后您需要在其存储中添加Intent处理器。
readonly property IntentHandler intentHandler: IntentHandler { intentIds: ["call-support", "activate-app"] onRequestReceived: { switch (request.intentId) { case "call-support": root.startCall("neptunesupport"); request.sendReply({ "done": true }); break; case "activate-app": root.requestRaiseAppReceived() request.sendReply({ "done": true }) break; } } }
上面的代码运行了startCall()
函数,并在接收到来自我们停车场应用的意图时调用Neptune支持团队。此函数还会发送回复,指示请求的操作是否完成。此外,确保您导入了QtApplicationManager.Application 2.0以使用IntentHandler。
创建通知
Qt应用管理器允许应用创建要发送并在系统UI中显示的通知。通常,系统UI有一个通知中心,存储所有创建的通知。在Neptune 3 UI中,有两种类型的通知:粘性和非粘性。当创建通知时,它会在UI顶部显示几秒钟。如果该通知是粘性的,则会在通知中心中保存。用户可以决定保留这些通知或删除每一个。
要创建通知,首先,您需要导入QtApplicationManager 2.0。然后,您可以在停车场应用中创建一个Notification对象。假设您希望通知用户停车时间将在5分钟后结束。您可以使用以下方式创建具有一些信息的Notification对象
Notification { id: parkingNotification summary: qsTr("Your parking period is about to end") body: qsTr("Your parking period will be ended in 5 minutes. Please extend your parking ticket or move your car.") sticky: true }
创建此通知对象后,您需要添加一个条件,当停车时间超过5分钟后触发。由于我们目前只有静态数据,您可以使用计时器来模拟这种行为。
Timer { interval: 10000; running: root.parkingStarted; onTriggered: { root.parkingStarted = false; parkingNotification.show(); } }
当用户按下开始按钮时,此计时器模拟停车券的持续时间。10秒后,计时器被触发,并显示通知。它还将重置parkingStarted
属性。
第三章:通过中间件API和模拟扩展停车场应用
在前几章中,我们已学过了UI以及与Neptune 3 UI良好集成的必要组件。
在本章中,我们将学习如何通过中间件API扩展停车应用程序,并提供一个模拟,显示当前可用的停车场地数量。
虽然本章介绍了中间件集成、其工作原理以及如何正确打包,但完整深入探讨超出了本章范围。有关如何开发中间件API的更详细信息,请参阅Qt IVI生成器教程。
注意:此应用程序需要一种多进程环境。
定义中间件API
为了定义我们的中间件API,我们使用QtIvi模块中的IVI生成器。此生成器使用接口定义语言(IDL)来生成代码,大大减少了我们需要编写的代码量。
QFace
QtIvi使用QFace IDL来描述需要生成的内容。对于本示例,我们在一个名为Parking
的模块中定义了一个简单的接口ParkingInfo
,其中包含一个名为freeLots
的只读属性。
@config_simulator: { simulationFile: "qrc:/simulation.qml" } module Example.Parking 1.0; interface ParkingInfo { @config_simulator: { default: 42 } readonly int freeLots }
自动生成
现在,我们的IDL文件已准备好第一版,是时候使用IVI生成器工具从它中自动生成API了。类似于moc,此自动生成过程已集成到qmake构建系统,并在编译时完成。
以下.pro
文件基于我们的IDL文件构建一个C++库
TARGET = $$qtLibraryTarget(Parking) TEMPLATE = lib DESTDIR = .. QT += ivicore ivicore-private qml quick CONFIG += unversioned_libname unversioned_soname DEFINES += QT_BUILD_EXAMPLE_PARKING_LIB CONFIG += ivigenerator QFACE_SOURCES = ../parking.qface macos: QMAKE_SONAME_PREFIX = @rpath target.path = $$[QT_INSTALL_EXAMPLES]/neptune3-ui/chapter3-middleware/ INSTALLS += target
通过将ivigenerator
添加到CONFIG
变量中,qmake的IVI生成器集成被加载,并且它期望在Create
QFACE_SOURCES变量中有QFace IDL文件。设置DEFINE
确保库导出其符号,这在Windows系统中是必要的。
哪些文件被自动生成
IVI生成器基于生成模板工作--它们定义了从QFace文件生成什么内容。如果没有定义QFACE_FORMAT
,则使用默认模板frontend
。有关这些模板的更多详细信息,请参阅使用生成器。
此frontend
模板生成以下内容
- 对于QFace文件中的每个接口,从QIviAbstractFeature派生出一个C++类
- 一个模块类,帮助将所有接口注册到QML中,并存储全局类型和函数
这些文件位于您库的构建文件夹中,如果您想检查C++代码。
QML插件
除了包含我们的中间件API的库之外,我们还需要一个QML插件,才能在QML中使用该API。
IVI生成器可以帮助我们使用不同的生成模板生成此类插件。以下.pro
文件生成了一个将API导出到QML的QML插件
TEMPLATE = lib CONFIG += plugin QT += ivicore LIBS += -L$$OUT_PWD/../ -l$$qtLibraryTarget(Parking) INCLUDEPATH += $$OUT_PWD/../frontend QMAKE_RPATHDIR += $$QMAKE_REL_RPATH_BASE/../../../ QFACE_FORMAT = qmlplugin QFACE_SOURCES = ../parking.qface load(ivigenerator) DESTDIR = $$OUT_PWD/$$replace(URI, \\., /) exists($$OUT_PWD/qmldir) { cpqmldir.files = $$OUT_PWD/qmldir \ $$OUT_PWD/plugins.qmltypes cpqmldir.path = $$DESTDIR cpqmldir.CONFIG = no_check_exist COPIES += cpqmldir installPath = $$[QT_INSTALL_EXAMPLES]/neptune3-ui/chapter3-middleware/imports/$$replace(URI, \\., /) qmldir.files = $$OUT_PWD/qmldir \ $$OUT_PWD/plugins.qmltypes qmldir.path = $$installPath target.path = $$installPath INSTALLS += target qmldir }
我们使用CONFIG
构建一个插件,然后定义链接器的设置,以便将其与我们的前端库链接。接下来,我们使用QFACE_FORMAT
选择qmlplugin
作为生成模板。这次我们没有将ivigenerator
添加到CONFIG
中,而是使用qmake的load()函数显式加载该功能。这样,我们可以使用URI
变量,它是qmlplugin
生成模板的一部分。此变量可以定义一个DESTDIR
,通过将所有点替换为斜杠。
除了文件夹结构外,QmlEngine还需要一个qmldir
文件,该文件指示哪些文件是插件的一部分,以及它们属于哪个URI
。更多详细信息,请参阅模块定义qmldir文件。
这两个文件(qmldir
和plugins.qmltypes
)都由IVI生成器自动生成,提供了关于代码补全的信息;但它们需要放置在库旁边。为此,我们把这些文件添加到类似于INSTALL
目标的作用域中,但将其添加到COPIES
变量中。这样确保在构建插件时文件被复制。
QML集成
在生成了我们的中间件API及其相应的QML插件后,就到了将我们的新API集成到停车应用中的时候了。
对于QML插件,我们的IDL文件中的模块名称用作导入URI;默认导入版本是1.0
。我们的main.qml
文件的导入语句如下所示:
import Example.Parking 1.0
默认情况下,QML API使用与我们的IDL文件中接口相同的名称。有关如何使用自定义名称或导入URI
的更多信息,请参阅使用生成器。
现在我们可以实例化我们的接口,并给它设置一个ID,就像对任何其他QML元素一样
ParkingInfo { id: parkingInfo }
要显示当前可用的停车场,我们需要创建一个QML绑定,使用我们新添加的ParkingInfo
QML元素中的freeLots
属性
text: parkingInfo.freeLots + qsTr(", Parking Olympia")
打包所需的调整
对于一个普通的Qt QML应用,这些步骤现在就足以启动应用并查看空闲停车位的数量为0
,因为它是初始化为默认值。但因为我们正在开发Neptune 3 UI的应用程序并打算在Neptune 3 UI运行时打包和安装它,所以需要一些额外的步骤。
不要创建库符号链接
通常在构建库时,会创建两个符号链接以允许在不重新编译其他应用程序的情况下进行版本升级。但在ApplicationManager包中,出于安全原因不允许符号链接。因此,以下qmake CONFIG
需要设置以确保不创建这些符号链接;在链接到库时也不确认它们
CONFIG += unversioned_libname unversioned_soname
在清单文件中定义导入路径和相关设置
为了我们的QML插件能够正确运行,我们需要将一个额外的导入路径设置到qmlengine
。通常,这是通过使用QML2_IMPORT_PATH
环境变量,将其传递给qmlscene
,或在您的main.cpp
中使用QQmlEngine::addImportPath()来完成的。但是,因为ApplicationManager在安装后启动应用程序,而且我们没有打包自己的main.cpp
文件,所以我们需要在包清单info.yaml
中定义这些设置。对于导入路径,我们添加以下行
runtimeParameters: importPaths: [ 'imports' ]
设置了这些设置后,该应用就可以部署了。它应该显示0
个空闲停车位
定义一个模拟行为
为了模拟我们中间件API的一些值,首先我们需要更好地理解QtIvi的架构。正如我们在生成库时所学的,IVI生成器使用了一个名为frontend
的模板。为了定义一些模拟值或连接到真实的API,我们还需要相应的backend
。这个backend
以一种插件的格式提供,而QtIvi负责将frontend
连接到backend
。有关此概念的更多信息,请参阅动态后端架构。
具有静态值的后端插件
下一步是使用IVI生成器生成这样的后端,并使用注释定义模拟应该执行的操作。
让我们从.pro
开始,生成和构建我们的后端
TEMPLATE = lib TARGET = $$qtLibraryTarget(parking_simulation) DESTDIR = ../qtivi QT += core ivicore CONFIG += ivigenerator plugin LIBS += -L$$OUT_PWD/../ -l$$qtLibraryTarget(Parking) INCLUDEPATH += $$OUT_PWD/../frontend QMAKE_RPATHDIR += $$QMAKE_REL_RPATH_BASE/../ QFACE_FORMAT = backend_simulator QFACE_SOURCES = ../parking.qface PLUGIN_TYPE = qtivi # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = $$OUT_PWD/../frontend/qml target.path = $$[QT_INSTALL_EXAMPLES]/neptune3-ui/chapter3-middleware/qtivi INSTALLS += target RESOURCES += \ simulation.qrc
要构建一个插件,我们需要在CONFIG
变量中添加plugin
并将其更改QFACE_FORMAT
为使用backend_simulator
生成模板。与QML插件类似,后端也需要链接到我们的前端库,因为它使用那里定义的类型。
为了确保QtIvi可以找到后端,它需要放置在qtivi
文件夹中。反过来,这个文件夹需要成为Qt插件搜索路径的一部分。
就像导入路径一样,附加的插件路径需要在包清单中进行设置
runtimeParameters: pluginPaths: [ '.' ]
现在,我们已经创建了一个模拟后端,但是没有额外的信息,IVI生成器无法创建真正有用的东西。
首先,我们定义一个静态默认值,该默认值由模拟后端提供。最简单的方法是在我们的QFace IDL文件中使用注释。注释是一种特殊的注释类型,它为生成模板提供了额外信息,说明应该生成什么。为了定义默认值,我们这样更改IDL文件
module Example.Parking 1.0; interface ParkingInfo { @config_simulator: { default: 42 } readonly int freeLots }
由于IDL文件的变化,现在IVI生成器重新创建了后端插件。当运行更新后的应用程序时,我们应该看到免费停车位的数量为42
。
模拟QML
虽然注释定义默认值和提供静态模拟是有用的,但生成的模拟后端可以做更多。它还可以允许您定义更动态的模拟行为。
为了实现这一点,我们将在QFace IDL文件中添加另一个注释,并定义一个simulationFile
。此文件包含我们的模拟行为,而QIviSimulationEngine则加载它。类似于其他QML文件,最佳方法是将此文件嵌入到Qt资源中。
我们的simulation.qml
看起来是这样的
import QtQuick 2.10 import Example.Parking.simulation 1.0 QtObject { property var settings : IviSimulator.findData(IviSimulator.simulationData, "ParkingInfo") property bool defaultInitialized: false property LoggingCategory qLcParkingInfo: LoggingCategory { name: "example.parking.simulation.parkinginfobackend" } property var backend : ParkingInfoBackend { function initialize() { console.log(qLcParkingInfo, "INITIALIZE") if (!defaultInitialized) { IviSimulator.initializeDefault(settings, backend) defaultInitialized = true } Base.initialize() } property var timer: Timer { interval: 5000 running: true repeat: true onTriggered: { var min = Math.ceil(-5) var max = Math.floor(5) var delta = Math.floor(Math.random() * (max - min +1)) + min; var newValue = Math.max(0, backend.freeLots + delta); backend.freeLots = newValue; } } } }
首先,有一个settings
属性,它使用了IviSimulator.findData方法的返回值进行初始化,该方法接收IviSimulator.simulationData和一个字符串作为输入。The simulationData
是作为JavaScript对象的JSON文件。
findData
方法帮助我们从这种界面中提取有关仪器的数据,InstrumentCluster
。随后的属性帮助该界面了解是否设置了默认值。The LoggingCategory
用于识别来自此模拟文件的日志输出。
之后,实际的行为是通过实例化一个InstrumentClusterBackend
项目并通过更多功能对其进行扩展而定义的。The InstrumentClusterBackend
是我们InstrumentCluster
QML前端类的接口。除了前端之外,这些属性也是可写的,以便可以将它们更改以提供有用的模拟。
每次前端实例连接到后端时,都会调用initialize()
函数。这同样适用于QML模拟:因为initialize()
C++函数将这个调用转发给QML实例。这种行为也适用于所有其他函数,例如getter和setter。有关更多详细信息,请参见QIviSimulationEngine。
在QML的initialize()
函数内部,我们调用IviSimulator.initializeDefault()
,从simulationData
对象中读取默认值并初始化所有属性。这仅一次
进行,因为我们不希望属性在被下一个前端实例连接到后端时重置为默认值。最后,调用基础实现以确保向前端发送initializeationDone
信号。
接下来,我们通过创建一个每5秒触发一次的定时器元素来定义实际的模拟行为。在触发信号的绑定中,我们使用Math.random()
函数获取介于-5和5之间的随机值,并将其添加到后端的可用停车位中,使用我们后端的freeLots
属性。此值的更改会自动填充到前端,从而模拟真实的车位。
文件
- neptune3ui/parking-app-tutorial/chapter1-basics/Main.qml
- neptune3ui/parking-app-tutorial/chapter1-basics/chapter1-basics.pro
- neptune3ui/parking-app-tutorial/chapter1-basics/info.yaml
- neptune3ui/parking-app-tutorial/chapter2-extend/Main.qml
- neptune3ui/parking-app-tutorial/chapter2-extend/chapter2-extend.pro
- neptune3ui/parking-app-tutorial/chapter2-extend/info.yaml
- neptune3ui/parking-app-tutorial/chapter3-middleware/app/Main.qml
- neptune3ui/parking-app-tutorial/chapter3-middleware/app/app.pro
- neptune3ui/parking-app-tutorial/chapter3-middleware/app/info.yaml
- neptune3ui/parking-app-tutorial/chapter3-middleware/backend_simulator/backend_simulator.pro
- neptune3ui/parking-app-tutorial/chapter3-middleware/backend_simulator/simulation.qml
- neptune3ui/parking-app-tutorial/chapter3-middleware/backend_simulator/simulation.qrc
- neptune3ui/parking-app-tutorial/chapter3-middleware/chapter3-middleware.pro
- neptune3ui/parking-app-tutorial/chapter3-middleware/frontend/frontend.pro
- neptune3ui/parking-app-tutorial/chapter3-middleware/imports/imports.pro
- neptune3ui/parking-app-tutorial/parking-app-tutorial.pro
图像
- neptune3ui/parking-app-tutorial/chapter1-basics/assets/ticket_bg.png
- neptune3ui/parking-app-tutorial/chapter1-basics/icon.png
- neptune3ui/parking-app-tutorial/chapter2-extend/assets/ticket_bg.png
- neptune3ui/parking-app-tutorial/chapter2-extend/icon.png
- neptune3ui/parking-app-tutorial/chapter3-middleware/app/assets/ticket_bg.png
- neptune3ui/parking-app-tutorial/chapter3-middleware/app/icon.png
©2019 Luxoft Sweden AB。此处包含的文档贡献是各自所有者的版权。
此处提供的文档根据Free Software Foundation发布的GNU自由文档许可版1.3项下的条款进行许可。
Qt和相应的标志是芬兰Qt Company Ltd.和其他国家和地区商标。