Qt小知識4.QWindow和QWidget

Qt小罗發表於2024-03-27

1 引言

QWindow 和 QWidget 都是 Qt 框架中用於建立和管理視窗的類,但它們在設計上服務於不同的目的和場景。這兩者的區別不僅體現在 API 設計上,還體現在它們在 Qt 框架中的角色和使用方式上。

2 典型區別

2.1 繼承結構和依賴

  • QWidget 繼承自 QObject 和 QPaintDevice,是所有視窗部件的基類。它是 Qt Widgets 模組的一部分,主要用於傳統的桌面應用程式,依賴於 Qt 的 QApplication。
  • QWindow 直接繼承自 QObject 和 QSurface,代表一個可以有視覺化輸出的視窗。它是 Qt GUI 模組的一部分,可以用於更接近系統底層的視窗建立,依賴於 Qt 的 QGuiApplication。

2.2 使用場景

  • QWidget 適合於需要豐富互動介面的傳統桌面應用程式開發,提供了按鈕、文字輸入框等內建介面元素,以及佈局管理器來自動管理元素佈局。
  • QWindow 更多用於需要直接使用 OpenGL 或 Vulkan 這類底層圖形API進行渲染的場景,或者是當需要建立一個不附加任何傳統控制元件的輕量級視窗時。

2.3 繪圖和渲染

  • QWidget 支援 Qt 的繪圖機制,可以透過重寫 paintEvent 方法並使用 QPainter API 實現繪圖。
  • QWindow 的繪圖通常依賴於更底層的圖形系統,例如直接與 OpenGL 上下文整合。雖然 QWindow 也可以使用 QPainter 透過 QBackingStore 進行繪圖,但這不是其主要用途。

3 為什麼引入QWindow?

  • 更清晰的分層架構
    在 Qt 5 之前的版本中,所有的視窗部件都是基於 QWidget 構建的,這意味著即使是不需要複雜部件和佈局系統的簡單視窗,也需要引入整個 QWidget 系統。QWindow 的引入提供了一個更輕量級的選擇,允許建立視窗而不必帶上全部 QWidget 的開銷。

  • 支援現代圖形APIs
    QWindow 提供了一個平臺無關的視窗控制代碼(handle),這使得開發者可以更直接地使用 OpenGL、Vulkan、DirectX 等現代圖形API來進行底層渲染。這對於需要高效能渲染的應用程式,如遊戲或高階圖形模擬,是非常重要的。

  • 促進 Qt Quick 的發展
    隨著 Qt Quick 和 QML 的引入,需要一個能夠與新的場景圖(scene graph)渲染系統無縫整合的視窗類。QWindow 扮演著這個角色,它可以容納一個完整的 Qt Quick UI,與底層圖形系統有效協作。

  • 跨平臺的視窗管理
    QWindow 封裝了作業系統的視窗管理功能,提供了跨平臺的視窗建立、事件處理、視窗狀態管理等,這使得開發者可以寫出更加跨平臺通用的程式碼,而不需要關心底層作業系統的特定實現。

  • 變得更加模組化
    隨著開發者對模組化應用程式的需求日益增加,QWindow 類使得 Qt 框架能夠提供更加模組化的元件。開發者可以選擇只使用圖形模組來建立視窗和管理底層圖形,而不必載入和依賴更多的 QWidget 功能。

3 依賴關係

在 Qt 5 之前,Qt 的 Widgets 系統和其視窗系統是緊密耦合的,QWidget 直接處理所有與視窗相關的操作,如顯示、事件處理等。隨著 Qt 5 的推出以及 Qt Quick(基於 QML 的高效能介面技術)的引入,Qt 架構師重新設計了圖形和視窗系統的底層架構來提高其效能和靈活性。QWindow 被引入為這個新架構的核心元件之一。

從 Qt 5 開始,QWidget 的實現在內部是依賴於 QWindow 的。這是 Qt 為了整合其圖形和視窗系統架構做出的設計決策。這種依賴關係主要體現在:每個 QWidget(或者更具體地說,每個 QWidget 的頂層視窗)背後都有一個 QWindow 例項負責實際的視窗管理和底層圖形操作。

3.1 如何理解?

  • 視窗例項:當你建立一個 QWidget 並且使其成為一個視窗(即沒有父物件或者透過呼叫 setWindowFlags 方法),Qt 內部會為這個 QWidget 建立一個 QWindow 例項。這個 QWindow 例項是實際上與作業系統層進行互動的物件,負責視窗的顯示、事件的接收等。對於嵌入在其他 QWidget 中的 QWidget,它們共享同一個 QWindow 例項,因為在作業系統層面,它們實際上屬於同一個視窗。

  • 繪圖與渲染:雖然 QWidget 和 QWindow 都可以進行繪圖和渲染操作,但它們的目的和方法不同。QWidget 的繪圖是基於 QPainter 的,更適合傳統的 GUI 應用程式。而 QWindow 則給予了開發者直接使用 OpenGL 或 Vulkan 等現代圖形 API 的能力。在 QWidget 中,繪圖最終還是透過它關聯的 QWindow 實現的,因為所有的圖形輸出都是透過作業系統的視窗系統完成的。

3.2 實際應用

在實際的應用程式設計中,開發者通常不需要直接處理 QWindow,除非他們在進行一些較為底層的圖形操作或建立高度定製化的視窗。QWidget 和 QWindow 的設計讓 Qt 能夠支援從簡單的桌面應用程式到複雜的圖形密集型應用程式的廣泛需求。

雖然從API使用者角度看 QWidget 和 QWindow 是分開的,但從 Qt 的內部實現來看,QWidget 的視窗展示功能是依賴 QWindow 的,這樣做既保留了 Qt Widgets 的高層次抽象方便開發傳統應用,又能從底層支援現代的圖形顯示需求。

4 總結

使用 QWidget 建立圖形使用者介面相對簡單,並且可以直接利用 Qt 的豐富的控制元件和佈局系統。而選擇 QWindow 可能需要更多底層的操作,但它可以更靈活地與底層圖形API整合,適用於特定的高效能渲染場景或當需要直接控制渲染迴圈時。