应用特性示例

展示具有各种功能(包括原生应用)的客户应用程序。

简介

本示例演示如何实现某些您可能在应用程序中需要的特定功能,例如

  • 如何实现嵌套合成器
  • 如何模拟崩溃并从中恢复
  • 如何同时显示多个顶级窗口
  • 如何使用原生运行时,不使用 QML

大多数这些功能仅在 多进程模式 中正确支持。

注意:本示例重点介绍应用(客户端)端。系统 UI(合成器/服务器)基于对 桌面系统 UI 示例 进行的一些修改。有关如何实现系统 UI 的更多详细信息,请参阅该示例。

嵌套合成器

嵌套合成器应用程序展示了如何在应用程序(Wayland 客户端)窗口内部实现一个 Wayland 合成器。合成器完全使用 QML 实现,并尽量减少使用。要在合成器中显示 Wayland 客户端,需要适当地设置 WAYLAND_DISPLAY 环境变量。

使用通过命令行设置的此环境变量启动客户

WAYLAND_DISPLAY=qtam-wayland-nested qmlscene client.qml -platform wayland

此命令仅在多进程模式下有效,因为嵌套合成器需要一个真实窗口作为其根元素。

嵌套合成器应用程序的 QML 代码如下

import QtQuick 2.11
import QtApplicationManager.Application 2.0
import QtWayland.Compositor 1.3

ApplicationManagerWindow {
    id: root
    color: "lightgrey"

    property ListModel shellSurfaces: ListModel {}

    Text {
        anchors.fill: parent
        anchors.margins: 8
        font.pointSize: 14
        wrapMode: Text.Wrap
        textFormat: Text.RichText
        text: "This Wayland<sup>*</sup> client window implements a Wayland compositor (nested compositor). " +
              "To display Wayland clients here, set:<br><br><b>WAYLAND_DISPLAY=qtam-wayland-nested</b>" +
              "<br><br>For instance:<br>WAYLAND_DISPLAY=qtam-wayland-nested qmlscene client.qml -platform wayland" +
              "<br><br><small>* in multi-process mode</small>"
    }

    WaylandCompositor {
        socketName: "qtam-wayland-nested"

        WaylandOutput {
            window: root
            sizeFollowsWindow: true
        }

        WlShell {
            onWlShellSurfaceCreated: shellSurfaces.append({shellSurface: shellSurface});
        }

        XdgShellV6 {
            onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface});
        }

        XdgShell {
            onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface});
        }
    }

    Repeater {
        model: shellSurfaces
        ShellSurfaceItem {
            shellSurface: modelData
            onSurfaceDestroyed: shellSurfaces.remove(index)
        }
    }

    Component.onCompleted: console.info("Start a client application in the nested compositor for instance with:\n  " +
                                        "WAYLAND_DISPLAY=qtam-wayland-nested QT_WAYLAND_DISABLE_WINDOWDECORATION=1 " +
                                        "QT_WAYLAND_SHELL_INTEGRATION=xdg-shell qmlscene client.qml -platform wayland");
}
崩溃模拟和恢复

此应用程序提供各种方法来强制应用程序崩溃,例如段错误。它利用一个用 C++ 实现的 QML 插件,以提供 Terminator QML 类型来触发崩溃。然后应用程序管理器打印出崩溃的原因和相关信息,如回溯。系统 UI 实现了一种基本的崩溃恢复形式:重新启动应用程序。

此应用程序仅在多进程模式下工作。在单进程模式中,崩溃会影响整个程序(系统 UI)。

崩溃模拟和恢复应用程序的 QML 代码如下

import QtQuick 2.11
import QtApplicationManager.Application 2.0
import Terminator 2.0

ApplicationManagerWindow {
    id: root

    property var methods: ({ illegalMemory: "Illegal memory access",
                             illegalMemoryInThread: "Illegal memory access in a thread",
                             stackOverflow: "Force stack overflow",
                             divideByZero: "Divide by zero",
                             unhandledException: "Throw unhandled exception",
                             abort: "Call abort",
                             raise: "Raise signal 7",
                             gracefully: "Exit gracefully" })

    function accessIllegalMemory()
    {
        Terminator.accessIllegalMemory();
    }

    Grid {
        columns: 2
        Repeater {
            model: Object.keys(methods)
            Rectangle {
                width: root.width / 2
                height: root.height / 4
                border.width: 1
                color: "lightgrey"

                Text {
                    anchors.fill: parent
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    wrapMode: Text.Wrap
                    font.pointSize: 14
                    text: methods[modelData]
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        switch (modelData) {
                        case "illegalMemory": accessIllegalMemory(); break;
                        case "illegalMemoryInThread": Terminator.accessIllegalMemoryInThread(); break;
                        case "stackOverflow": Terminator.forceStackOverflow(); break;
                        case "divideByZero": Terminator.divideByZero(); break;
                        case "unhandledException": Terminator.throwUnhandledException(); break;
                        case "abort": Terminator.abort(); break;
                        case "raise": Terminator.raise(7); break;
                        case "gracefully": Terminator.exitGracefully(); break;
                        }
                    }
                }
            }
        }
    }
}
两个顶级窗口

此应用程序说明了如何通过将 QtObject 作为应用程序的根元素来显示多个顶级窗口。

两个顶级窗口应用程序的 QML 代码如下

import QtQuick 2.11
import QtApplicationManager.Application 2.0

QtObject {
    property var win1: ApplicationManagerWindow {
        color: "lightsteelblue"

        Rectangle {
            width: 80; height: 80; radius: 40
            color: "orange"

            MouseArea {
                anchors.fill: parent
                drag.target: parent
            }
        }
    }

    property var win2: ApplicationManagerWindow {
        color: "transparent"

        Rectangle {
            id: rect
            anchors.fill: parent
            color: "orange"
            opacity: 0.4
        }

        ApplicationManagerWindow {
            id: popup
            width: 300; height: 100

            Rectangle {
                anchors.fill: parent
                border.width: 1
                color: "orangered"
            }

            Text {
                anchors.centerIn: parent
                text: "Click to hide!"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: popup.visible = false;
            }

            Component.onCompleted: setWindowProperty("type", "pop-up");
        }
    }
}
原生小部件

此应用程序基于 QWidget。与其他示例中的其他应用程序相比较,这些应用程序是 QML 应用程序,本程序使用原生运行时。因此,应用程序的入口点不是 main.qml 文件,而是一个可执行文件。这是一个基本的应用程序,仍然遵循此特定的系统 UI。目的是说明概念:系统 UI 需要一个表示正常窗口和弹出的窗口的 type 窗口属性。

此程序仅在多进程模式下工作,因为无法在单进程模式中启动应用程序进程。

原生小部件应用程序的 C++ 代码如下

#include <QApplication>
#include <QPushButton>
#include <QDialog>
#include <QVBoxLayout>

#include "launchermain.h"
#include "logging.h"
#include "dbusapplicationinterface.h"
#include "dbusnotification.h"

int main(int argc, char *argv[])
{
    QtAM::Logging::initialize(argc, argv);
    QtAM::Logging::setApplicationId("widgets");

    QtAM::LauncherMain::initialize();
    QApplication app(argc, argv);
    QtAM::LauncherMain launcher;

    launcher.registerWaylandExtensions();
    launcher.loadConfiguration();
    launcher.setupLogging(false, launcher.loggingRules(), QString(), launcher.useAMConsoleLogger());
    launcher.setupDBusConnections();

    QWidget window;
    QVBoxLayout layout(&window);

    // Popup using application manager window property
    QPushButton button1(QStringLiteral("Click to open/close a popup"));
    button1.setStyleSheet(QStringLiteral("QPushButton { background-color : limegreen; font-size: 36px; }"));
    layout.addWidget(&button1);

    QDialog *popup1 = new QDialog(&window);
    (new QPushButton(QStringLiteral("I'm a popup!"), popup1))->resize(340, 140);
    popup1->setStyleSheet(QStringLiteral("QPushButton { background-color : limegreen; color : white; font-size: 24px; }"));
    QObject::connect(&button1, &QPushButton::clicked, [&popup1, &launcher] () {
        popup1->setVisible(!popup1->isVisible());
        launcher.setWindowProperty(popup1->windowHandle(), "type", QStringLiteral("pop-up"));
    });

    // Notification
    QPushButton button2(QStringLiteral("Click to open a notification"));
    button2.setStyleSheet(QStringLiteral("QPushButton { background-color : darkgrey; font-size: 36px; }"));
    layout.addWidget(&button2);

    QtAM::DBusNotification *notification = QtAM::DBusNotification::create(&app);
    notification->setSummary(QStringLiteral("I'm a notification"));
    QObject::connect(&button2, &QPushButton::clicked, notification, &QtAM::DBusNotification::show);

    // Application interface for handling quit
    QtAM::DBusApplicationInterface iface(launcher.p2pDBusName(), launcher.notificationDBusName());
    iface.initialize();
    QObject::connect(&iface, &QtAM::DBusApplicationInterface::quit, [&iface] () {
        iface.acknowledgeQuit();
    });

    app.processEvents();
    window.showNormal();

    return app.exec();
}

代码结构

与其他纯粹基于QML的Qt应用程序管理器示例相比,本示例需要您显式构建。代码以这种方式结构化,结果的应用程序文件夹仅包含运行应用程序所需的工件。因此,您可以将这些应用程序打包并安装。

要构建包括其示例在内的Qt应用程序管理器,您需要向qmake传递-config enable-examples参数。了解更多详情,请参阅构建

示例项目 @ code.qt.io

©2019 Luxoft Sweden AB. 本文档中包含的贡献是各自所有者的版权。本文档根据 Free Software Foundation 发布的 GNU自由文档许可协议版本1.3 的条款进行许可。Qt和相应标志是芬兰的Qt公司以及全球其他国家/地区的商标。所有其他商标均为各自所有者的财产。