包安装示例

学习如何实现动态包安装和卸载。

The Package Installation example installing a package.

注意: 如果您想在一个Linux机器上构建此示例,请阅读此内容

介绍

此示例演示了如何使用appman-package-server动态安装和卸载包。系统UI附带一个内置应用程序(Builtin蓝色),但允许您动态安装尽可能多的应用程序。左下角的“包”按钮打开一个对话框,您可以在其中首先连接到包服务器,然后安装或卸载包。

您需要运行实际的包服务器来测试此示例。有关如何执行的详细说明,请参阅appman-package-server文档。

为了方便,此示例附带两个预包装的应用程序(从installable-apps文件夹中的hello-world.redhello-world.green>)。实际的打包在CMakeLists.txt中完成,使用qt6_am_create_installable_package函数。请注意,此函数目前处于技术预览状态,将来可能更改。

此外,此示例还将打印出启动命令,以在这些包上运行包服务器,并在应用程序输出中显示该命令:您必须将其复制并粘贴到shell中,以运行服务器。

入门指南

基本UI

main.qml文件包含一个非常基本的组合UI,正如在其他示例中所见:左边的图标列表(显示已安装的应用程序)和右边的窗口组合区域。

此外,它还创建并初始化一个PackageServerInterface组件实例(以下将提供更多信息)。每当服务器提供的包列表更改时,packagesDialog中的packageModel将相应更新。

包服务器接口

appman-package-server是一个独立进程,通过HTTP REST接口与系统UI通信。我们使用QML的 XMLHttpRequest机制与服务器通信,但我们还使用一个单独的PackageServerInterface组件来封装通信,以便更方便地使用。

此组件期望设置以下一些必需属性,以便连接到服务器实例

    required property string url
    required property string projectId
    required property string architecture

为了与服务器通信,以下简单的QML API可用。实际上,该packages属性是一个将填充服务器上可用的包描述的列表。

    property string statusText
    property bool isCompatible
    property var packages

    function reload() { _serverHello() }

    function install(id) {
        let dlurl = url + "/package/download?id=" + id + "&architecture=" + architecture
        let taskId = PackageManager.startPackageInstallation(dlurl)
        return taskId
    }

    function remove(id) {
        let taskId = PackageManager.removePackage(id, true)
        return taskId
    }
包对话框

当用户在主窗口中点击 软件包 按钮时,将打开 packagesDialog。它允许您设置软件包服务器的 URL 和 acknowledgeMode(请参见下文中的确认对话框)。如果服务器 URL 有效且可以连接到 appman-package-server 实例,对话框的下半部分将显示服务器上的可用软件包列表。然后用户可以选择要安装或删除的软件包。

使用 PackageServerInterface 组件与服务器通信以及安装和删除软件包。

                            onClicked: {
                                if (isInstalled)
                                    packageServerInterface.remove(id)
                                else
                                    packageServerInterface.install(id)
                            }
确认对话框

出于安全原因,应用程序管理器期望系统 UI 确认软件包安装。这是通过响应 taskRequestingInstallationAcknowledge 信号调用 acknowledgePackageInstallation 方法来实现的。

本例展示了三种不同的方法来处理与用户交互相关的此问题。

    enum AcknowledgeMode {
        Always,                // always ask the user
        Never,                 // never ask the user
        CapabilitiesOnly       // only ask the user, if the package needs capabilities
    }

根据 acknowledgeMode 属性,组件将自动确认任何安装请求、始终询问用户确认,或者如果软件包请求 能力(有关更多信息,请参见 清单定义),则询问用户确认。作为一个额外的复杂性,一个软件包可以包含多个应用程序,它们可以请求不同的能力。本例将简单地收集所有能力到一个平列表中。

    property int mode: AcknowledgeDialog.AcknowledgeMode.Always

    property Connections connections: Connections {
        target: PackageManager
        function onTaskRequestingInstallationAcknowledge(taskId, pkg, extraMetaData, extraSignedMetaData) {
            // reduce the capabilities of all applications down to a set of unique values
            let capsSet = new Set()
            pkg.applications.forEach((app) => app.capabilities.forEach((cap) => capsSet.add(cap)))
            let capabilities = Array.from(capsSet)

            if ((mode === AcknowledgeDialog.Never)
                    || ((mode === AcknowledgeDialog.CapabilitiesOnly) && !capabilities.length)) {
                PackageManager.acknowledgePackageInstallation(taskId)

            } else if ((mode === AcknowledgeDialog.Always)
                    || ((mode === AcknowledgeDialog.CapabilitiesOnly) && capabilities.length)) {
                let d = acknowledgeDialog.createObject(root.contentItem, {
                                                           taskId: taskId,
                                                           packageName: pkg.name,
                                                           capabilities: capabilities
                                                       })
                d.open()
            }
        }
    }

实际对话框是一个标准的 MessageDialog,它将被动态实例化。根据用户是否接受或拒绝对话框,相应地通知 PackageManager

            onAccepted: if (!acknowledged) PackageManager.acknowledgePackageInstallation(taskId)
            onRejected: if (!acknowledged) PackageManager.cancelTask(taskId)

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd。此处包含的文档贡献是各自所有者的版权。此处提供的文档是根据自由软件基金会在 GNU 自由文档许可证 1.3 版本 的条款发布的。Qt 及相关的徽标是 The Qt Company Ltd. 在芬兰和/或其他国家和地区的商标。所有其他商标均为各自所有者的财产。