关于前端:基于-Qt-Quick-Plugin-快速构建桌面端跨平台组件

34次阅读

共计 5903 个字符,预计需要花费 15 分钟才能阅读完成。

桌面端的 UI 开发框架比照挪动端、Web 端的成熟计划,始终处于不温不火的状态。随着疫情掀起的风波,桌面端在线教育、视频会议等需要不断涌现。传统平台下的开发框架难以满足需要,而类 DirectUI 的框架因跨平台、可拓展性差、门槛低等问题并不能失去一些企业的认可。桌面端 Electron、Flutter 类框架出于性能、原生平台反对等个性化需要思考,往往得不到最好的解决方案。

Qt Quick 能够较好得解决上述提到的问题。 本文将从两个方面介绍通过 Qt Quick 是如何疾速实现桌面端跨平台业务组件构建的,首先咱们聊一下 Qt Quick 在桌面端开发的劣势,再具体如何创立一个 C++ 拓展插件给 Qt Quick 利用来应用。

Qt Quick 劣势

跨平台个性

Qt Quick Plugin 机制能够满足下面提到的诸多需要。首先 Qt 对跨平台反对十分敌对,仅须要对非凡平台做一些简略适配就能够应用一套代码可跑在不同终端。官网以“One framework. One codebase. Any platform”作为题目也突显了其在跨平台的方面所做的工作。

易散发组件

应用 Qt 编写的 Qt Quick 组件容易散发,它最终导出能够是源码模式也能够是公布的二进制文件夹,外部蕴含了对数据模型和 UI 根底组件的包装。

UI 组件高度复用

应用 Qt Quick 能够很容易的创立一个可复用组件,官网也提供了一些根底组件如 Google Material 格调的控件等。基于这些根底组件,咱们就能够拓展出不同模式的 UI 组件,在不毁坏内部结构的状况下提供内部应用。

前端 QML 学习门槛低

Qt Quick 用来形容前端的 QML 语言语法简练,非常容易了解,能够与 JavaScript 混编,实现简直所有咱们能想到的能力。并且新版本 Qt Quick 对 C++ 和 QML 交互做了进一步加强,应用简略的脚本即可实现丰盛的能力。

适宜封装业务模块

得力于 Qt Quick 的 Model-View-Delegate 设计思维,咱们能够对业务数据和 UI 根底展现能力的封装齐全拆散,通过 Model 提供残缺的数据链条,通过 View 和 Delegate 来对不同场景做数据展现。

通过 Qt Quick Plugin 机制创立一个残缺的利用,能够采取相似下图这种形式:

以音视频场景举例,无论下层利用最终最终以什么状态出现,底层都是一些固定的数据,比方成员和成员的状态治理、设施列表和设施的检测抉择,用户视觉上看到的无非是视频画面。通过封装,咱们看到的是这样一种模式:

相似 MemberList 的设计,不要给其设置固定的视觉款式,通过全局预约义样式表来管制能够让其 UI 追随使用者的格调变动。在会议场景它可能叫做“与会成员”,在在线教育场景它可能叫做“学生列表”。这样咱们能够随便搭配组成各式类型的业务场景:

构建一个 Qt Quick C++ Plugin

一个原生的 Qt Quick 利用容许咱们间接基于其能力实现业务性能,像下面提到的场景,当不同产品线须要应用同样的性能组件或须要拓展 Qt Quick 能力时,咱们就能够借助 Qt Quick 2 Extension Plugin 来对这些组件进行封装了。通过简略的几个步骤,咱们就能够创立一个属于本人的 Qt Quick 插件。

创立插件

首先通过 Qt Creator 创立一个 Qt Quick 2 Extension Plugin 工程。创立好的根底插件工程中,会默认创立一个派生于 QQmlExtensionPlugin 的子类,用来让咱们注册本人的自定义模块提供内部应用:

#include <QQmlExtensionPlugin>

class NEMeetingPlugin : public QQmlExtensionPlugin { 
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)

public:
    void registerTypes(const char* uri) override;
};

通过该接口注册咱们的自定义类型提供引入插件的 QML 前端应用:

void NEMeetingPlugin::registerTypes(const char* uri) {
    // @uri NEMeeting
    qmlRegisterType<NEMEngine>(uri, 1, 0, "NEMEngine");
    qmlRegisterType<NEMAuthenticate>(uri, 1, 0, "NEMAuthenticate");
    qmlRegisterType<NEMAccount>(uri, 1, 0, "NEMAccount");
    //......
    // Devices
    qmlRegisterType<NEMDevices>(uri, 1, 0, "NEMDevices");
    qmlRegisterType<NEMDevicesModel>(uri, 1, 0, "NEMDeviceModel");
    //......
    // Schedules
    qmlRegisterType<NEMSchedule>(uri, 1, 0, "NEMSchedule");
    qmlRegisterType<NEMScheduleModel>(uri, 1, 0, "NEMScheduleModel");
    //......
    // Meeting
    qmlRegisterType<NEMSession>(uri, 1, 0, "NEMSession");
    qmlRegisterType<NEMMine>(uri, 1, 0, "NEMMine");
    qmlRegisterType<NEMAudioController>(uri, 1, 0, "NEMAudioController");
    //......
    // Providers
    qmlRegisterType<NEMFrameProvider>(uri, 1, 0, "NEMFrameProvider");
    //......
}

这些组件有些是前端不可见组件,他们将作为一个前端可实例化的对象来创立具体的实例,例如 NEMEngine 是整个组件的惟一引擎,这些对象要继承自 QObject。

class NEMEngine : public QObject {}

而数据相干的封装则不同,他们须要继承自 QAbstract*Model,以设施相干的数据模型举例,以下为示例代码:

class NEMDevicesModel : public QAbstractListModel {
    Q_OBJECT

public:
    explicit NEMDevicesModel(QObject* parent = nullptr);

    enum {DeviceName, DevicePath, DeviceProperty};

    Q_PROPERTY(NEMDevices* deviceController READ deviceController WRITE setDeviceController NOTIFY deviceControllerChanged)
    Q_PROPERTY(NEMDevices::DeviceType deviceType READ deviceType WRITE setDeviceType NOTIFY deviceTypeChanged)

    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;

    NEMDevices* deviceController() const;
    void setDeviceController(NEMDevices* deviceController);

    NEMDevices::DeviceType deviceType() const;
    void setDeviceType(const NEMDevices::DeviceType& deviceType);

Q_SIGNALS:
    void deviceControllerChanged();
    void deviceTypeChanged();

private:
    NEMDevices* m_deviceController = nullptr;
    NEMDevices::DeviceType m_deviceType = NEMDevices::DEVICE_TYPE_UNKNOWN;
};

对数据模型的封装秉持残缺、可定制、参数化的准则,尽量不要在组件的封装过程中掺杂细节的业务需要,以 NeRTC 2.0 SDK 设施枚举程序举例,SDK 提供了两种枚举设施的形式。

  • 一种是 SDK 举荐设施,当你有内置设施、外接、蓝牙等不同设施时,SDK 会抉择一个最适宜的作为第一个设施应用。
  • 另外一种是零碎默认设施,追随零碎变更来抉择设施应用。

两种计划从某些业务场景角度思考只须要一种,但作为一个能够二次开发的组件来说,应该都能够提供下层配置,所以在设施相干的管理器中,提供了 AutoSelectMode 参数提供内部引入插件的开发者来管制应用哪种模式。

除了对数据模型、自定义类型等进行封装外,还能够提供一些前端组件让应用插件的开发者更快捷的创立利用。以视频渲染的容器举例,以下是借助 C++ 注册到前端的 NEMFrameProvider 来实现一个简略的视频渲染的 Delegate。

import QtQuick 2.0
import QtMultimedia 5.14
import NEMeeting 1.0

Rectangle {
    id: root

    property bool mirrored: false
    property alias frameProvider: frameProvider

    color: '#000000'

    VideoOutput {
        anchors.fill: parent
        source: frameProvider
        transform: Rotation {
            origin.x: root.width / 2
            origin.y: root.height / 2
            axis {x: 0; y: 1; z: 0}
            angle: mirrored ? 180 : 0
        }
    }

    NEMFrameProvider {id: frameProvider}
}

通过工程配置,咱们让其导出插件时同时将这些 .qml UI 文件也同时导出:

pluginfiles.files += \
    imports/$$QML_IMPORT_NAME/qmldir \
    imports/$$QML_IMPORT_NAME/components/NEMVideoOutput.qml
    .......

引入插件

应用一个创立好的插件更为不便,个别插件编译实现后最终是一个文件夹的模式散发,咱们只须要在引入的性能中配置咱们要引入的插件及门路即可:

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = $$PWD/../bin

在 QML 中应用时,咱们首先须要 import 相应的插件:

import NEMeeting 1.0

这样你就能够应用插件中注册进来的类型了:

// 创立引擎实例
NEMEngine { 
    id: nemEngine
    appKey: "092dcd94d2c2566d1ed66061891*****"
}

对设施列表做展现仅须要创立一个列表,并指定插件注册进来的设施数据模型即可。

ComboBox {
    Layout.fillWidth: true
    textRole: "deviceName"
    valueRole: "deviceId"
    currentIndex: {return nemDevices.currentPlayoutIndex}
    // 应用 C++ 注册进来的数据模型
    model: NEMDeviceModel {
        id: listModel
        deviceController: nemDevices
        deviceType: NEMDevices.DEVICE_TYPE_PLAYOUT
    }
    onActivated: {nemDevices.selectDevice(NEMDevices.DEVICE_TYPE_PLAYOUT, currentValue)
    }
}

设施对象类型创立时咱们能够通过预设的参数来指定设施的抉择形式为 SDK 举荐模式
NEMDevices.RECOMMENDED_MODE:

NEMDevices {
    id: nemDevices
    engine: nemEngine
    autoSelectMode: NEMDevices.RECOMMENDED_MODE
}

程序在公布时,你只须要将插件目录与程序同时散发即可,无需多余的配置即可实现利用的打包公布流程。

总结

对于 Qt Quick 2 Extension Plugin 的开发和应用,官网提供了十分具体的文档。通过这种机制,咱们不仅能够创立一个封装了某底层能力 SDK 残缺性能的开发组件,还能够让使用者高度自定义交互行为。这是以往桌面端 UI 开发框架很难甚至无奈做到的事件。

QML 语言的低门槛也能够让从事过前端、C++ 或一些脚本类语言的开发者迅速切换到 Qt Quick 开发环境。他们不须要关注某个插件的具体实现细节,仅须要将这些组件做一些简略拼装就能够组成一个残缺的利用。同时这也是网易云信团队始终以来致力的方向,咱们通过解决方案及易用体系等形式,让音视频以及即时通信等技术可能疾速、高效接入相应的服务中。

以上就是本文的全副分享,对于 Qt Quick 更多技术干货,也欢送继续锁定咱们。

作者介绍

邓佳佳,网易智企云信高级开发工程师,负责保护网易云信跨平台 NIM SDK 和下层解决方案预研开发,包含基于 NIM SDK 和 NERTC SDK 构建的在线教育、互动直播、IM 即时通讯、网易会议解决方案的保护,对 Duilib、Qt Quick、CEF 框架有丰盛的实战经验。

5 月 20 日线上直播预报

直播点播曾经与日常生活非亲非故,这个过程中大家最关注的是什么,更低的播放老本?更高的画质?这就波及到了窄带高清技术,低带宽高清画质离不开视频编码。

业界已有比拟多窄带高清技术计划,如淘宝直播、阿里窄带高清、腾讯极速高清等。NE264 是网易自研 H264 编码器,已胜利利用于视频通话、直播点播中,带来更低时延、更高画质的优质体验。

本次分享将为您揭开窄带高清技术以及 JND 感知编码技术的神秘面纱。

本期主题 :MCtalk Live#3 直播点播窄带高清之 JND 感知编码技术

戳这里,报名

更多技术干货,欢送关注【网易智企技术 +】

正文完
 0