1. 程式人生 > >Qt 之 Graphics View Framework 簡介

Qt 之 Graphics View Framework 簡介

# Graphics View Framework 互動式 2D 圖形的 Graphics View 框架概述。自 Qt4.2 中引入了 Graphics View,以取代其前身 QCanvas。Graphics View 提供了一個用於管理和互動大量的可定製的 2D 圖形項與用於視覺化這些項的 View 小部件的 surface(曲面),並提供縮放(zooming)和旋轉(rotation)功能支援。 該框架包括事件傳播體系結構(an event propagation architecture),該體系結構允許對場景中的專案進行精確的雙精度互動功能。圖元可以被如下操作處理:按鍵事件,滑鼠按下,移動,釋放和雙擊事件,它們還可以跟蹤滑鼠的移動。 Graphics View 使用 BSP(Binary Space Partitioning,二進位制空間分割槽)樹提供了非常快速的圖元發現功能,因此,它可以實時視覺化大型場景(甚至可以包含數百萬個圖元)。下面列出 Graphics View Framework 的常用模組: ```mermaid graph LR subgraph Graphics W[QtCore.QObject] --> F(QtWidgets.QGraphicsObject) F --> S(QtWidgets.QGraphicsTextItem) end subgraph View BB[QtWidgets.QGraphicsLayoutItem] --> AA A[QtGui.QPaintDevice] --> B(QtWidgets.QWidget) B --> C(QtWidgets.QFrame) WW --> FF(QtWidgets.QGraphicsItemGroup) WW -->3F(QtWidgets.QGraphicsLineItem) WW --> 4F(QtWidgets.QGraphicsPixmapItem) W --> B WW --> F(QtWidgets.QGraphicsObject) F --> AA(QtWidgets.QGraphicsWidget) end subgraph Graphics AA --> AAA(QtCharts.QChart) C --> D(QtWidgets.QAbstractScrollArea) D -->E(QtWidgets.QGraphicsView) W --> U(QtWidgets.QGraphicsScene) end subgraph Shape WW[QtWidgets.QGraphicsItem] --> EE(QtWidgets.QAbstractGraphicsShapeItem) EE --> QQ(QtWidgets.QGraphicsEllipseItem) EE --> 3Q(QtWidgets.QGraphicsPathItem) EE --> 4Q(QtWidgets.QGraphicsPolygonItem) EE --> 5Q(QtWidgets.QGraphicsRectItem) EE --> 6Q(QtWidgets.QGraphicsSimpleTextItem) end ``` ## 1 The Graphics View Architecture Graphics View 提供了一種基於專案(item-based)的方法來進行模型-檢視程式設計(model-view programming),非常類似於 InterView 的便利類 QTableView,QTreeView 和 QListView。多個檢視可以觀察單個場景,並且該場景包含各種幾何形狀的圖元專案。 ### 1.1 The Scene [`QtWidgets.QGraphicsScene`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsScene.html#PySide2.QtWidgets.QGraphicsScene "PySide2.QtWidgets.QGraphicsScene") 提供了 Graphics View 場景。QGraphicsScene 類提供了一個用於管理大量 2D 圖形專案的容器(surface)。 場景具有如下功能: * 提供用於管理大量圖元專案的快速介面 * 將事件傳播到每個圖元專案 * 管理圖元專案狀態,例如選擇和焦點處理 * 提供未被轉換的渲染功能;主要用於印刷 場景用作 `QtWidgets.QGraphicsItem` 物件的容器。它與 `QtWidgets.QGraphicsView` 一起用於視覺化 2D 曲面上(2D surface)的圖形專案,例如線條,矩形,文字,甚至是自定義專案。通過呼叫 `addItem()` 將圖元專案新增到場景中,然後通過呼叫許多專案查詢功能之一來檢索專案。 請注意,QGraphicsScene 沒有自己的視覺外觀。它只管理圖元專案。您需要建立一個 QGraphicsView 小部件以視覺化場景。 要將專案新增到場景,首先需要構造一個 QGraphicsScene 物件。然後,您有兩個選擇:通過呼叫 `addItem()` 新增現有的 QGraphicsItem 物件,或者可以呼叫下列便捷函式之一 `addEllipse()`,`addLine()`,`addPath()`,`addPixmap()`,`addPolygon()`,`addRect()` 或 `addText()`,它們都返回一個指向新新增專案的指標。使用這些函式新增的圖元的維度是相對於圖元(item)的座標系的,並且圖元的位置在場景中被初始化為 $(0,0)$。 您可以使用 QGraphicsView 視覺化場景。當場景發生變化時(例如,當某項移動或變換時),QGraphicsScene 發出 `change()` 訊號。要刪除專案,請呼叫 `removeItem()`。 QGraphicsScene 使用索引演算法來有效地管理專案的位置。預設情況下,使用 BSP(二進位制空間分割槽)樹;一種適用於大型場景的演算法,其中大多數圖元保持靜止(即不要四處移動)。您可以選擇通過呼叫 `setItemIndexMethod()` 禁用此索引。 通過呼叫 `setSceneRect()` 設定場景的邊界矩形。可以將圖元專案放置在場景中的任何位置,預設情況下,場景的大小不受限制。場景 rect 僅用於內部簿記,維護場景的圖元專案索引。如果未設定場景矩形,則 QGraphicsScene 將使用 `itemsBoundingRect()` 返回的所有專案的邊界區域作為場景矩形。但是,`itemsBoundingRect()` 是一個相對耗時的函式,因為它通過收集場景中每個專案的位置資訊進行操作。因此,在大型場景上操作時,應始終設定場景 rect。 QGraphicsScene 的最大優勢之一就是能夠有效確定圖元的位置。即使場景中有數百萬個專案,`items()` 函式也可以在幾毫秒內確定專案的位置。`items()` 有多個過載:一個過載在某個位置查詢專案,一個過載在多邊形或矩形內部或與之相交,等等。返回的專案列表按堆疊順序排序,最高的專案是列表中的第一專案。為了方便起見,還有一個 `itemAt()` 函式可在給定位置返回最上面的專案。 QGraphicsScene 還管理某些圖元專案狀態,例如圖元專案選擇和焦點。要選擇專案,請呼叫 `setSelectionArea()`(傳遞任意形狀來選擇場景中的專案),並要清除當前選擇,請呼叫`clearSelection()`。呼叫 `selectedItems()` 以獲取所有選定項的列表。 函式 `setSelectionArea()` 還用作 QGraphicsView 中橡皮筋選擇的基礎。要獲取所有當前選定專案的列表,請呼叫 `selectedItems()` 。QGraphicsScene處理的另一個狀態是專案是否具有鍵盤輸入焦點。您可以通過呼叫 `setFocusItem()` 或 `setFocus()` 來設定焦點,或者通過呼叫 `focusItem()` 來獲取當前焦點。 場景的呼叫方法: ```python QGraphicsScene(sceneRect[, parent=None]) QGraphicsScene(x, y, width, height[, parent=None]) ``` QGraphicsScene 的另一個職責是傳播來自 QGraphicsView 的事件。要將事件傳送到場景,您可以構造一個繼承 QEvent 的事件,然後使用 `sendEvent()` 進行傳送。`event()` 負責將事件排程到各個圖元專案。一些常見事件由便利事件處理程式處理。例如,按鍵事件由 `keyPressEvent()` 處理,滑鼠按鍵事件由 `mousePressEvent()` 處理。 按鍵事件將傳遞到焦點圖元專案。要設定焦點圖元專案,可以呼叫 `setFocusItem()`,傳遞接受焦點的圖元專案,或者該專案本身可以呼叫 `setFocus()`。 呼叫 `focusItem()` 以獲取當前的焦點專案。為了與小部件相容,場景還保留其自己的焦點資訊。預設情況下,場景沒有焦點,並且所有按鍵事件都將被丟棄。如果呼叫 `setFocus()` 或場景中的某個專案獲得焦點,則場景將自動獲得焦點。如果場景具有焦點,則 `hasFocus()` 將返回 `true`,並將按鍵事件轉發到焦點項(如果有)。如果場景失去焦點(例如,有人在某個專案具有焦點時呼叫 `clearFocus()`),則場景將保留其專案焦點資訊,並且一旦場景重新獲得焦點,它將確保最後一個焦點專案重新獲得焦點。 對於滑鼠懸停效果,QGraphicsScene 排程懸停事件。如果某個圖元專案接受懸停事件(請參見`acceptHoverEvents()`),則當滑鼠進入其區域時,它將收到 GraphicsSceneHoverEnter 事件。當滑鼠繼續在專案區域內移動時, QGraphicsScene 將向其傳送 GraphicsSceneHoverMove 事件。當滑鼠離開該專案的區域時,該專案將收到 GraphicsSceneHoverLeave 事件。 所有滑鼠事件都會傳遞到當前的滑鼠採集器專案。如果一個專案接受滑鼠事件(請參見 `acceptedMouseButtons()` 並且接受滑鼠按下,則它將成為場景的滑鼠捕獲器。當沒有其他滑鼠按鈕被按下時,它會一直停留在滑鼠抓取器上,直到釋放滑鼠為止。您可以呼叫 `mouseGrabberItem()` 來確定當前正在抓住滑鼠的圖元專案。 最後,QGraphicsScene 允許您通過 `render()` 函式將場景的一部分渲染到繪製裝置中。 ### 1.2 The View QGraphicsView 類提供了一個用於顯示 QGraphicsScene 的內容小部件。QGraphicsView 在可滾動視口(viewport)中視覺化 QGraphicsScene 的內容。 QGraphicsView 提供了檢視小部件,可將場景的內容視覺化。您可以將多個檢視附加到同一場景,以在同一資料集中提供多個視口。檢視視窗小部件是一個滾動區域,並提供用於在大型場景中導航的滾動條。要啟用 OpenGL 支援,可以通過呼叫 `setViewport()` 將 QOpenGLWidget 設定為視口。 檢視從鍵盤和滑鼠接收輸入事件,然後將其轉換為場景事件(在適當的情況下將其轉換為場景座標),然後再將事件傳送到視覺化場景。 使用其變換矩陣 `transform()`,檢視可以變換場景的座標系。這允許高階導航功能,例如縮放和旋轉。為了方便起見,QGraphicsView 還提供了用於在檢視和場景座標之間進行轉換的函式:`mapToScene()` 和 `mapFromScene()`。 `QtWidgets.QGraphicsView` 在可滾動 viewport 中視覺化 `QtWidgets.QGraphicsScene` 的內容。 為了使場景視覺化,首先要構造一個 `QtWidgets.QGraphicsView` 物件,然後將要視覺化的場景的地址傳遞給 `QtWidgets.QGraphicsView` 的建構函式。另外,您可以呼叫 `setScene()` 在以後設定場景。呼叫 `show()` 後,預設情況下,檢視將滾動到場景的中心並顯示此時可見的所有圖元專案。 例如: ```python scene = QGraphicsScene() scene.addText("Hello, world!") # 等價於 QGraphicsView(scene) view = QGraphicsView() view.setScene(scene) view.show() ``` 您可以使用滾動條或呼叫 `centerOn()` 顯式滾動到場景中的任何位置。通過將一個點傳遞給 `centerOn()`,QGraphicsView 將滾動其視口以確保該點在檢視中居中。同時提供了用於滾動到 QGraphicsItem 的過載,在這種情況下,QGraphicsView 將看到專案的中心在檢視中居中。如果只需要確保某個區域可見(但不必居中),則可以呼叫 `sureVisible()`。 QGraphicsView 可用於視覺化整個場景或其中的一部分。預設情況下,第一次顯示檢視時,會自動檢測到視覺化區域(通過呼叫 `itemsBoundingRect()`)。要自己設定視覺化區域矩形,可以呼叫 `setSceneRect()`。這將適當地調整滾動條的範圍。請注意,儘管場景支援幾乎不受限制的大小,但滾動條的範圍永遠不會超出整數 (INT_MIN, INT_MAX) 的範圍。 QGraphicsScene 還管理某些圖元專案狀態,例如圖元專案選擇和焦點。您可以通過呼叫 `setSelectionArea()` 傳遞任意形狀來選擇場景中的專案。此函式還用作 QGraphicsView 中橡皮筋選擇的基礎。要獲取所有當前選定專案的列表,請呼叫 `selectedItems()`。 QGraphicsScene 處理的另一個狀態是專案是否具有鍵盤輸入焦點。您可以通過呼叫 `setFocusItem()` 或 `setFocus()` 來設定焦點,或者通過呼叫 `focusItem()` 來獲取當前焦點。 QGraphicsView 通過呼叫 `render()` 視覺化場景。預設情況下,使用常規 QPainter 並使用預設渲染提示將專案繪製到視口上。若要更改繪畫專案時 QGraphicsView 傳遞給 QPainter 的預設渲染提示,可以呼叫`setRenderHints()`。 預設情況下,QGraphicsView 為視口視窗小部件提供常規 QWidget。您可以通過呼叫 `viewport()` 來訪問此小部件,也可以通過呼叫 `setViewport()` 來替換它。 要使用 OpenGL 進行渲染,只需呼叫 `setViewport`(新的 QOpenGLWidget)。`QGraphicsView` 擁有視口小部件的所有權。 QGraphicsView 使用 QTransform 支援仿射變換。最常見的兩種轉換是 `scaling`,用於實現縮放(zooming)和旋轉(rotation)。QGraphicsView 在轉換過程中保持檢視中心不變。由於場景對齊(`setAligment()`),平移檢視將不會產生視覺影響。您可以將矩陣傳遞給 `setTransform()`,也可以呼叫便捷函式之一 `rotate()`,`scale()`,`translate()` 或 `shear()`。 您可以使用滑鼠和鍵盤與場景中的專案進行互動。QGraphicsView 將滑鼠和鍵事件轉換為場景事件(繼承 QGraphicsSceneEvent 的事件),並將其轉發到視覺化場景。最後,處理事件並對事件做出反應的是獨立專案。例如,如果單擊一個可選擇的專案,則該專案通常會讓場景知道它已被選中,並且它還將重繪自身以顯示選擇矩形。類似地,如果您單擊並拖動滑鼠以移動可移動項,則它是處理滑鼠移動並自行移動的項。預設情況下,圖形專案互動處於啟用狀態,您可以觸發呼叫。 您還可以通過建立 QGraphicsView 的子類並重新實現滑鼠和鍵事件處理程式來提供自己的自定義場景互動。 為了簡化您如何以程式設計方式與檢視中的專案進行互動,QGraphicsView 提供了對映函式 `mapToScene()` 和 `mapFromScene()` 以及專案訪問器 `items()` 和 `itemAt()`。這些功能允許您在檢視座標和場景座標之間對映點,矩形,多邊形和路徑,並使用檢視座標在場景中查詢專案。 `QtWidgets.QGraphicsView(scene[, parent=None])`: - `parent`:QWidget - `scene`:QGraphicsScene ### 1.3 The Item QGraphicsItem 是場景中圖形項的基類。Graphics View 為典型形狀提供了幾個標準項,例如矩形(QGraphicsRectItem),橢圓(QGraphicsEllipseItem)和文字項(QGraphicsTextItem),但時,最強大的是 QGraphicsItem 可以編寫自定義項。除此之外,QGraphicsItem 支援以下功能: - 滑鼠按下,移動,釋放和雙擊事件,以及滑鼠懸停事件,滾輪事件和上下文選單事件。 - 鍵盤輸入焦點和按鍵事件 - 拖放 - 通過 parent-child 關係以及 QGraphicsItemGroup 進行分組 - 碰撞檢測(Collision detection) 圖元專案位於本地座標系(local coordinate system)中,就像 QGraphicsView 一樣,它還提供了許多功能,用於在專案與場景之間以及專案與專案之間對映座標。而且,像 QGraphicsView 一樣,它可以使用 `matrix:transform()` 變換其座標系。這對於旋轉和縮放單個專案很有用。 項可以包含其他項(子項)。父項的轉換由其所有子項繼承。不過,不管某項的累積轉換如何,它的所有功能(例如,`contains()`, `boundingRect()`, `QGraphicsItem::collidesWith()`)都仍在本地座標下執行。 QGraphicsItem 通過 `shape()` 函式和 `QGraphicsItem::collidesWith()` 這兩個虛擬函式都支援衝突檢測。通過從 `shape()` 返回專案的形狀作為區域性座標 QPainterPath,QGraphicsItem 將為您處理所有碰撞檢測。但是,如果要提供自己的衝突檢測,則可以重新實現 `QGraphicsItem::collidesWith()`。 更多內容見:[QtWidgets.QGraphicsItem](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#PySide2.QtWidgets.QGraphicsItem)。 `QtWidgets.QGraphicsItem` 類是 `QtWidgets.QGraphicsScene` 中所有圖形項的基類。為了方便描述,將 `QtWidgets.QGraphicsItem` 例項命名為**圖元**。`QtWidgets.QGraphicsItem` 為編寫您自己的自定義圖元項提供了一個輕量級的基礎。它包括定義圖元項的幾何形狀(geometry),碰撞檢測(collision detection),且有繪畫實現以及通過事件處理程式進行的圖元項互動。 為方便起見,Qt為最常見的形狀提供了一組標準圖形項: * [`QGraphicsEllipseItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsEllipseItem.html#PySide2.QtWidgets.QGraphicsEllipseItem "PySide2.QtWidgets.QGraphicsEllipseItem"):提供 ellipse item * [`QGraphicsLineItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsLineItem.html#PySide2.QtWidgets.QGraphicsLineItem "PySide2.QtWidgets.QGraphicsLineItem"):提供 line item * [`QGraphicsPathItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsPathItem.html#PySide2.QtWidgets.QGraphicsPathItem "PySide2.QtWidgets.QGraphicsPathItem"):提供任意 path item * [`QGraphicsPixmapItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsPixmapItem.html#PySide2.QtWidgets.QGraphicsPixmapItem "PySide2.QtWidgets.QGraphicsPixmapItem"):提供 pixmap item * [`QGraphicsPolygonItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsPolygonItem.html#PySide2.QtWidgets.QGraphicsPolygonItem "PySide2.QtWidgets.QGraphicsPolygonItem"):提供 polygon item * [`QGraphicsRectItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsRectItem.html#PySide2.QtWidgets.QGraphicsRectItem "PySide2.QtWidgets.QGraphicsRectItem"):提供 rectangular item * [`QGraphicsSimpleTextItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsSimpleTextItem.html#PySide2.QtWidgets.QGraphicsSimpleTextItem "PySide2.QtWidgets.QGraphicsSimpleTextItem"):提供簡單 text label item * [`QGraphicsTextItem`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsTextItem.html#PySide2.QtWidgets.QGraphicsTextItem "PySide2.QtWidgets.QGraphicsTextItem"):提供高階 text browser item 圖元專案的所有幾何資訊均基於其**本地座標系**(Local Coordinate System)[^1]。該圖元項的位置 `pos()` 是唯一在本地座標中不起作用的函式,因為它在父座標中返回一個位置。 [^1]: 本地座標系是以物體自身位置作為原點,表示物體間相對位置和方向,並且會根據物體自身旋轉而旋轉。 您可以通過呼叫 `setVisible()` 設定圖元專案是否應可見(即繪製和接受事件)。隱藏圖元專案也會隱藏其子項。同樣,您可以通過呼叫 `setEnabled()` 啟用或禁用圖元專案。 如果禁用某個圖元專案,則其所有子項也將被禁用。預設情況下,圖元專案既可見又啟用。若要切換是否選擇圖元專案,請首先通過設定 `ItemIsSelectable` 標誌啟用選擇,然後呼叫 `setSelected()`。通常,由於使用者互動,場景(scene)會切換選擇。 要編寫自定義的圖元專案,首先建立 `QGraphicsItem` 的子類,然後實現其兩個純虛擬公共函式:`boundingRect()` 返回該圖元專案所繪製區域的估計值,`paint()` 實現實際繪圖(the actual painting)。例如: ```python class SimpleItem(QtWidgets.QGraphicsItem): def boundingRect(self): penWidth = 1.0 return QtCore.QRectF(-10 - penWidth / 2, -10 - penWidth / 2, 20 + penWidth, 20 + penWidth) def paint(self, painter, option, widget): painter.drawRoundedRect(-10, -10, 20, 20, 5, 5) ``` `boundingRect()` 函式有許多不同的用途。`QtWidgets.QGraphicsScene` 的專案索引基於 `boundingRect()`,並且 `QtWidgets.QGraphicsView` 將其用於剔除不可見的專案以及確定繪製重疊專案時需要重新組合的區域。此外,`QtWidgets.QGraphicsItem` 的碰撞檢測機制使用 `boundingRect()` 提供有效的截止點(cut-off)。`collidesWithItem()` 中的細粒度碰撞演算法基於呼叫 `shape()` 的方法,該方法會返回圖元形狀的精確輪廓作為`QtGui.QPainterPath`。 `QtWidgets.QGraphicsScene` 期望所有圖元專案的 `boundingRect()` 和 `shape()` 保持不變,除非得到通知。如果您想以任何方式更改圖元的幾何形狀,則必須首先呼叫 `prepareGeometryChange()` 以允許 `QtWidgets.QGraphicsScene` 更新其簿記(bookkeeping)。 碰撞檢測可以通過兩種方式完成: 1. 重新實現 `shape()` 以為您的圖元返回準確的形狀,並依靠 `collidesWithItem()` 的預設實現進行形狀與形狀的交點(shape-shape intersection)。如果形狀複雜,這可能代價會非常高。 2. 重新實現 `collidesWithItem()` 以提供您自己的自定義圖元專案和形狀碰撞演算法。 可以呼叫 `contains()` 函式來確定圖元專案是否包含一個點。該函式也可以通過圖元項重新實現。`contains()` 的預設行為是基於呼叫 `shape()` 的。 圖元專案可以包含其他圖元專案,也可以被包含在其他圖元專案中。所有圖元專案都可以有一個父圖元專案和一列子專案。除非該圖元專案沒有父物件,否則它的位置是父物件的座標(即父物件的本地座標)。父項將其位置及其變換傳播給所有子項。 #### 1.3.1 [Transformations](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#pyside2-qtwidgets-qgraphicsitem-transformations "Permalink to this headline") `QtWidgets.QGraphicsItem` 除了提供其基本位置 `pos()` 外,還支援投影變換(projective transformations)。有幾種更改圖元專案變換的方法。對於簡單轉換,可以呼叫便捷函式 `setRotation()` 或`setScale()`,也可以將任何變換矩陣傳遞給 `setTransform()`。對於高階轉換控制,您還可以通過呼叫 `setTransformations()` 來設定多個組合轉換。 圖元項變換從父項到子項累積,因此,如果父項和子項都旋轉 90 度,則子項的總轉換將為 180 度。同樣,如果專案的父項縮放到其原始大小的 2 倍(2x),則其子項也將擴大兩倍。圖元項的變形不會影響其自身的區域性幾何關係( local geometry);所有幾何函式(例如,`contains()`,`update()` 和所有對映函式)仍在區域性座標(local coordinates)下執行。為方便起見,`QtWidgets.QGraphicsItem` 提供了一個函式 `SceneTransform()`(它返回該項的總變換矩陣(包括其位置以及所有父項的位置和變換))和 `scenePos()`(該函式返回其在場景座標中的位置)。 要重置圖元的矩陣,請呼叫 `resetTransform()`。 某些轉換操作根據其應用順序產生不同的結果。例如,如果縮放轉換然後旋轉,則可能會得到與首先旋轉轉換不同的結果。但是,您在 `QtWidgets.QGraphicsItem` 上設定轉換屬性的順序不會影響最終的轉換。`QtWidgets.QGraphicsItem` 始終以固定的定義順序應用屬性: >The item’s base transform is applied ( transform() ) The item’s transformations list is applied in order ( transformations() ) The item is rotated relative to its transform origin point ( rotation() , transformOriginPoint() ) The item is scaled relative to its transform origin point ( scale() , transformOriginPoint() ) #### 1.3.2 [Painting](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#painting "Permalink to this headline") `QtWidgets.QGraphicsView` 呼叫 `paint()` 函式來繪製圖元專案的內容。該圖元專案沒有背景或沒有預設填充值;該圖元專案後面的任何內容都會在此功能中未明確繪製的所有區域中 shine。您可以呼叫 `update()` 安排重新繪製,可以選擇傳遞需要重新繪製的矩形。根據圖元專案是否在檢視中可見,該專案可能會或可能不會重新粉刷(repaint)。`QtWidgets.QGraphicsItem` 中沒有等效於 `repaint()` 的函式。 圖元專案是按檢視繪製的,從父項開始,然後是子項,按升序排列。您可以通過呼叫 `setZValue()` 設定圖元的堆疊順序,並通過呼叫 `zValue()` 對其進行測試,其中,在 z 值高的圖元之前先繪製 z 值低的圖元。堆疊順序適用於同級圖元;父項總是在子項前被繪製。 #### 1.3.3 [Sorting](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#sorting "Permalink to this headline") 所有圖元專案均按定義的穩定順序繪製,並且此相同的順序決定了當您單擊場景時哪些專案將首先接收滑鼠輸入。通常,您不必擔心排序,因為專案遵循“自然順序”,遵循場景的邏輯結構。 某個圖元專案的子項堆疊在父項的頂部,而同級項則按照插入順序(即,它們被新增到場景或新增到同一父項的順序)堆疊。如果您新增專案 A,然後新增 B,則 B 將位於 A 的頂部。如果您新增 C,則專案的堆疊順序將是 A,然後是 B,然後是 C。 對於高階使用者,有一些方法可以更改專案的排序方式: - 您可以在一個圖元專案上呼叫 `setZValue()`,以將其顯式堆疊在其他同級專案之上或之下。項的預設 Z 值為 0。具有相同 Z 值的項按插入順序堆疊。 - 您可以呼叫 `stackBefore()` 重新排序子級列表。這將直接修改插入順序。 - 您可以設定 `ItemStacksBehindParent` 標誌以將子項堆疊在其父項之後。 兩個同級圖元的堆疊順序也計入每個圖元的子代圖元和後代圖元。因此,如果一項在另一項之上,則其所有子項也將在另一項所有子項之上。 #### 1.3.4 [Events](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#events "Permalink to this headline") `QtWidgets.QGraphicsItem` 通過虛擬函式 `sceneEvent()` 從 `QtWidgets.QGraphicsScene` 接收事件。此函式將最常見的事件分配給一組便捷事件處理程式: - `contextMenuEvent()` handles context menu events - `focusInEvent()` and `focusOutEvent()` handle focus in and out events - `hoverEnterEvent()` , `hoverMoveEvent()` , and `hoverLeaveEvent()` handles hover enter, move and leave events - `inputMethodEvent()` handles input events, for accessibility support - `keyPressEvent()` and `keyReleaseEvent()` handle key press and release events - `mousePressEvent()` , `mouseMoveEvent()` , `mouseReleaseEvent()` , and `mouseDoubleClickEvent()` handles mouse press, move, release, click and doubleclick events 您可以通過安裝事件過濾器(event filters)來過濾任何其他圖元專案的事件。此功能與 Qt 的常規事件過濾器(請參閱 `installEventFilter()`)分開,後者僅適用於 QObject 的子類。在通過呼叫 `installSceneEventFilter()` 將專案安裝為另一個專案的事件過濾器之後,虛擬函式 `sceneEventFilter()` 會接收到過濾後的事件。您可以通過呼叫 `removeSceneEventFilter()` 來刪除專案事件過濾器。 #### 1.3.5 [Custom Data](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#custom-data "Permalink to this headline") 有時,將自定義資料註冊到某個圖元專案(自定義圖元專案或標準圖元專案)很有用。您可以在任何圖元專案上呼叫 `setData()`,以使用鍵值對(鍵為整數,並且值為 `QVariant`)將資料儲存在其中。要從專案中獲取自定義資料,請呼叫 `data()`。Qt 本身完全沒有涉及此功能。 #### 1.3.6 `QtWidgets.QGraphicsItem` 的使用 `class QGraphicsItem([parent=None])` 使用給定的父項構造一個 `QtWidgets.QGraphicsItem`。它不會修改 `parent()` 返回的父物件。 如果 `parent` 是 `None`,你可以通過呼叫 [`addItem()`](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsScene.html#PySide2.QtWidgets.PySide2.QtWidgets.QGraphicsScene.addItem "PySide2.QtWidgets.PySide2.QtWidgets.QGraphicsScene.addItem") 將圖元專案新增到場景中。該圖元專案將成為頂級圖元專案(top-level item)。 `QtWidgets.QGraphicsItem.GraphicsItemFlag`:列舉描述了可以在圖元專案上設定的不同標誌,以切換圖元專案行為中的不同功能。 常量|描述 :-|:- `QGraphicsItem.ItemIsMovable`|該專案支援使用滑鼠進行互動式移動。通過單擊該專案然後拖動,該專案將與滑鼠游標一起移動。如果該專案有孩子,則所有孩子也將移動。如果專案是選擇的一部分,則所有選擇的專案也會移動。通過 `QtWidgets.QGraphicsItem` 的滑鼠事件處理程式的基本實現,可以方便地提供此功能。 `QGraphicsItem.ItemIsSelectable`|該專案支援選擇。啟用此功能將使 `setSelected()` 可以切換圖元專案的選擇。通過呼叫 `setSelectionArea()`,單擊某項或在 `QtWidgets.QGraphicsView` 中使用橡皮筋選擇,它還將使該項自動被選擇。 `QGraphicsItem.ItemIsFocusable`|該專案支援鍵盤輸入焦點(即它是一個輸入專案)。啟用此標誌將允許該專案接受焦點,這再次允許將鍵事件傳遞到 `keyPressEvent()` 和 `keyReleaseEvent()`。 `QGraphicsItem.ItemClipsToShape`|圖元會剪裁成自己的形狀。該專案無法在其形狀之外繪製或接收滑鼠,平板電腦,拖放或懸停事件。預設情況下禁用。此行為由 `drawItems()` 或 `drawItems()` 強制執行。這個標誌是在 Qt 4.3 中引入的。 `QGraphicsItem.ItemClipsChildrenToShape`|該項將其所有後代的繪畫剪裁成自己的形狀。此項的直接或間接子項不能超出該專案的形狀。預設情況下,此標誌為禁用狀態。孩子們可以在任何地方畫畫。此行為由 `drawItems()` 或 `drawItems()` 強制執行。 這個標誌是在 Qt 4.3 中引入的。 更多 flags 見:[QGraphicsItem](https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGraphicsItem.html#qgraphicsitem "Permalink to this headline")。 #### 1.3.7 QtWidgets.QGraphicsItem.boundingRect() 返回 `QRectF`。這個純虛擬函式將圖元專案的外部邊界定義為矩形;所有繪畫都必須限制在圖元專案的邊界區域內。`QtWidgets.QGraphicsView` 使用它來確定該圖元專案是否需要重繪。儘管圖元專案的形狀可以是任意的,但邊界矩形始終為矩形,並且不受圖元專案變換的影響。 如果要更改圖元專案的邊界矩形,必須首先呼叫 `prepareGeometryChange()`。這會通知場景即將發生的更改,以便可以更新其圖元專案幾何索引;否則,場景將不會意識到該圖元的新幾何形狀,並且結果是不確定的(通常,渲染工件( rendering artifacts)保留在檢視(view)中)。 重新實現此功能,以使 `QtWidgets.QGraphicsView` 決定需要重繪視窗小部件的哪些部分(如果有)。注意:對於繪製輪廓/筆觸的形狀,重要的是在邊界矩形中包括筆寬的一半。但是,沒有必要補償抗鋸齒。 例子: ```python def boundingRect(self): penWidth = 1.0 return QtCore.QRectF(-radius - penWidth / 2, -radius - penWidth / 2, diameter + penWidth, diameter + penWi