桌面端的 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」 做爲標題也突顯了其在跨平臺的方面所作的工做。app
易分發組件
使用 Qt 編寫的 Qt Quick 組件容易分發,它最終導出能夠是源碼形式也能夠是發佈的二進制文件夾,內部包含了對數據模型和 UI 基礎組件的包裝。框架
UI 組件高度複用
使用 Qt Quick 能夠很容易的建立一個可複用組件,官方也提供了一些基礎組件如 Google Material 風格的控件等。基於這些基礎組件,咱們就能夠拓展出不一樣形式的 UI 組件,在不破壞內部結構的狀況下提供外部使用。ide
前端 QML 學習門檻低
Qt Quick 用來描述前端的 QML 語言語法簡練,很是容易理解,能夠與 JavaScript 混編,實現幾乎全部咱們能想到的能力。而且新版本 Qt Quick 對 C++ 和 QML 交互作了進一步加強,使用簡單的腳本便可實現豐富的能力。性能
適合封裝業務模塊
得力於 Qt Quick 的 Model-View-Delegate 設計思想,咱們能夠對業務數據和 UI 基礎展現能力的封裝徹底分離,經過 Model 提供完整的數據鏈條,經過 View 和 Delegate 來對不一樣場景作數據展現。學習
經過 Qt Quick Plugin 機制建立一個完整的應用,能夠採起相似下圖這種方式:ui
以音視頻場景舉例,不管上層應用最終最終以什麼形態呈現,底層都是一些固定的數據,好比成員和成員的狀態管理、設備列表和設備的檢測選擇,用戶視覺上看到的無非是視頻畫面。經過封裝,咱們看到的是這樣一種形式:編碼
相似 MemberList 的設計,不要給其設置固定的視覺樣式,經過全局預約義樣式表來控制可讓其 UI 跟隨使用者的風格變化。在會議場景它可能叫作「與會成員」,在在線教育場景它可能叫作「學生列表」。這樣咱們能夠隨意搭配組成各式類型的業務場景:url
構建一個 Qt Quick C++ Plugin
一個原生的 Qt Quick 應用容許咱們直接基於其能力實現業務功能,像上面提到的場景,當不一樣產品線須要使用一樣的功能組件或須要拓展 Qt Quick 能力時,咱們就能夠藉助 [Qt Quick 2 Extension Plugin](http://Creating C++ Plugins for QML) 來對這些組件進行封裝了。經過簡單的幾個步驟,咱們就能夠建立一個屬於本身的 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 推薦模式<br />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日線上技術直播預告
明晚19點,相約聊聊【直播點播窄帶高清之 JND 感知編碼技術】當即報名
更多技術乾貨,歡迎關注【網易智企技術+】微信公衆號