QGraphics框架简介 - 使用QGraphics视图框架创建矢量界面

571 阅读16分钟

Qt图形视图框架允许你开发_快速_ _有效的_2D矢量图形场景。场景可以包含_数百万个_项目,每个项目都有自己的特征和行为。通过PyQt使用图形视图,你可以在Python中访问这个高性能的图形层。无论你是将矢量图形视图集成到现有的PyQt应用程序中,还是仅仅想要一个强大的Python矢量图形界面,Qt的图形视图都是你正在寻找的。

图形视图的一些常见用途包括数据可视化、地图应用、二维设计工具、现代数据仪表盘,甚至是二维游戏。

在本教程中,我们将迈出第一步,看看Qt图形视图框架,用一些简单的矢量项目构建一个场景。这将使我们熟悉API和坐标系统,以后我们将用它来构建更复杂的例子。

图形视图框架

图形视图框架由3个主要部分组成:QGraphicsViewQGraphicsSceneQGraphicsItem ,每个部分有不同的职责。

该框架可以用模型-视图范式来解释,QGraphicsScene 作为_模型_,QGraphicsView 作为_视图_。每个场景可以有多个视图。 场景中的_QGraphicsItems_可以被看作是模型中的项目,持有视觉数据,场景将其结合起来定义完整的图像。

QGraphicsScene 是将所有东西粘在一起的中心组件。它就像一块_白板_,所有的项目都画在上面(圆形、矩形、线、像素图等)。QGraphicsView 负责渲染一个给定的场景--或其中的一部分,并进行一些转换(缩放、旋转、剪切)--将其显示给用户。视图是一个标准的Qt部件,可以放在任何Qt布局中。

QGraphicsScene 提供了一些开箱即用的重要功能,因此我们可以用它们来开发高级的应用程序,而不必纠结于低级的细节。例如 --

  • 碰撞检测,检测一个图形项目与另一个项目的碰撞。
  • 项目选择,让我们有能力同时处理多个项目,例如,用户可以选择多个项目,当按下删除键时,有一个函数要求场景给出所有被选择的项目的列表,然后删除它们。
  • 物品发现,场景可以告诉我们在一个特定的点或一些定义的区域内有哪些物品(或其中的一部分),例如,如果用户添加的物品与一个禁区相交,程序会检测到它们并给它们另一种颜色(主要是红色)。
  • 事件传播,场景接收事件,然后将其传播给物品。

要定义一个QGraphicsScene ,你要定义它的边界或_sceneRect_,它定义了场景的x & y原点和尺寸。如果你不提供_sceneRect_,它将默认为所有子项目的最小边界矩形--随着项目的添加、移动或删除而更新。这很灵活,但效率较低。

场景中的项目是由QGraphicsItem 对象表示的。这些对象是任何二维场景的基本构件,代表将在场景中显示的形状、像素图或SVG图像。每个项目在sceneRect 内都有一个相对位置,并且可以有不同的变换效果(缩放、平移、旋转、剪切)。

最后,QGraphicsView 是场景的渲染器,将场景全部或部分地显示给用户。视图本身可以应用变换(缩放、平移、旋转和剪切)来修改显示,而不影响底层场景。默认情况下,视图会将鼠标和键盘事件转发到场景中,以便于用户交互。这可以通过调用view.setInteractive(False) 来禁用。

一个简单的场景

让我们从创建一个简单的场景开始。下面的代码创建了QGraphicsScene ,定义了一个400 x 200的场景,然后将其显示在QGraphicsView

  • PyQt5
  • PyQt6

蟒蛇

import sys
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QApplication

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

view = QGraphicsView(scene)
view.show()
app.exec_()

蟒蛇

import sys
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QApplication

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

view = QGraphicsView(scene)
view.show()
app.exec()

如果你运行这个例子,你会看到一个空窗口。

Empty Scene displayed in a window 空的图形场景,显示在一个QGraphicsView窗口中。

还不是很刺激 -- 但这是我们的QGraphicsView ,显示我们的_空_场景。

如前所述,QGraphicsView 是一个_widget_。在Qt中,任何没有父级的widget都会显示为窗口。这就是为什么我们的QGraphicsView 在桌面上显示为一个窗口。

添加项目

让我们开始在场景中添加一些项目。有许多内置的_图形项_,你可以自定义并添加到你的场景中。 在下面的例子中,我们使用QGraphicsRectItem ,它可以画一个矩形。我们创建了这个项目,输入了它的尺寸,然后在把它添加到场景中之前设置了它的位置笔和画笔。

  • PyQt5
  • PyQt6

蟒蛇

import sys
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsRectItem, QApplication
from PyQt5.QtGui import QBrush, QPen
from PyQt5.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.cyan)
pen.setWidth(10)
rect.setPen(pen)

scene.addItem(rect)

view = QGraphicsView(scene)
view.show()
app.exec_()

蟒蛇

import sys
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsRectItem, QApplication
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

scene.addItem(rect)

view = QGraphicsView(scene)
view.show()
app.exec()

运行上述程序,你会在场景中看到一个单一的、颜色相当难看的矩形。

A single rectangle in the scene 场景中的一个矩形

添加更多的项目只是简单地创建对象,自定义它们,然后将它们添加到场景中。在下面的例子中,我们使用QGraphicsEllipseItem ,添加了一个圆--一个圆只是一个高度和宽度相等的椭圆。

  • PyQt5
  • PyQt6

蟒蛇

import sys
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsRectItem, QGraphicsEllipseItem, QApplication
from PyQt5.QtGui import QBrush, QPen
from PyQt5.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)


view = QGraphicsView(scene)
view.show()
app.exec_()

蟒蛇

import sys
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsRectItem, QGraphicsEllipseItem, QApplication
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.GlobalColor.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.GlobalColor.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)


view = QGraphicsView(scene)
view.show()
app.exec()

上述代码将得到以下结果。

Scene with two items 一个有两个项目的场景

你添加项目的顺序会影响场景中的堆叠顺序--后来添加的项目总是出现_在_先添加的项目_之上_。然而,如果你需要更多的控制,你可以使用.setZValue 来_设置_堆叠顺序。

python

ellipse.setZValue(500)
rect.setZValue(200)

现在,圆(椭圆)出现在矩形的上方。

Using Zvalue to order items in the scene 使用Z值来排列场景中的项目

试着实验一下设置两个项目的Z值--你可以_在_项目进入场景_之前_或_之后_设置它,并且可以随时改变它。

Z在这里指的是Z坐标。X和Y坐标分别是场景中的水平和垂直位置。Z坐标决定了物品在场景中前后的相对位置--从屏幕中 "出来 "朝向观看者。

还有方便的方法.stackBefore().stackAfter() ,这些方法允许你在场景中的另一个项目后面或前面堆叠你的QGraphicsItem

蟒蛇

ellipse.stackAfter(rect)

使项目可移动

我们的两个QGraphicsItem 对象目前在我们放置它们的地方是固定的,但它们不一定是固定的。如前所述,Qt的图形视图框架允许项目响应用户的输入,例如允许它们在场景中随意拖动。像这样的简单功能实际上已经内置了,你只需要在每个QGraphicsItem 。要做到这一点,我们需要在项目上设置标志QGraphicsItem.GraphicsItemFlags.ItemIsMoveable

图形项目标志的完整列表可以在这里找到

  • PyQt5
  • PyQt6

蟒蛇

import sys
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsItem, QGraphicsRectItem, QGraphicsEllipseItem, QApplication
from PyQt5.QtGui import QBrush, QPen
from PyQt5.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)

ellipse.setFlag(QGraphicsItem.ItemIsMovable)

view = QGraphicsView(scene)
view.show()
app.exec_()

蟒蛇

import sys
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsItem, QGraphicsRectItem, QGraphicsEllipseItem, QApplication
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.GlobalColor.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.GlobalColor.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)

ellipse.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)

view = QGraphicsView(scene)
view.show()
app.exec()

在上面的例子中,我们只在_椭圆_上设置了ItemIsMovable 。你可以在场景中拖动椭圆--包括在矩形的后面--但矩形本身将被锁定在原地。实验一下添加更多的项目和配置可移动状态。

如果你想让一个项目是_可选择的_,你可以通过设置ItemIsSelectable 标志来实现,例如这里用.setFlags() 来同时设置多个标志。

python

ellipse.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)

如果你点击椭圆,你现在会看到它被一条虚线包围,表示它被选中了。我们将在后面的教程中更详细地研究如何使用项目选择。

A selected item 场景中的一个被选中的项目,用虚线突出显示

创建对象的另一种方法。

到目前为止,我们一直是通过创建对象,_然后_把它们添加到场景中来创建项目。但是你也可以通过调用场景本身的一个辅助方法来直接_在_场景中创建一个对象,例如:scene.addEllipse() 。这将_创建对象_并_返回_它,所以你可以像以前一样修改它。

  • PyQt5
  • PyQt6

蟒蛇

import sys
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsRectItem, QApplication
from PyQt5.QtGui import QBrush, QPen
from PyQt5.QtCore import Qt

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rect = scene.addRect(0, 0, 200, 50)
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.cyan)
pen.setWidth(10)
rect.setPen(pen)

view = QGraphicsView(scene)
view.show()
app.exec_()

蟒蛇

import sys
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsRectItem, QApplication
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt
app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rect = scene.addRect(0, 0, 200, 50)
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

view = QGraphicsView(scene)
view.show()
app.exec()

在你的代码中,请随意使用你认为最舒服的形式。

你只能对内置的QGraphicsItem 对象类型使用这种方法。

建立一个更复杂的场景

到目前为止,我们已经使用基本的QGraphicsRectItemQGraphicsEllipseItem 形状建立了一个简单的场景。现在让我们使用一些其他的QGraphicsItem 对象来构建一个更复杂的场景,包括线条、文本和QPixmap (图像)。

  • PyQt5
  • PyQt5

蟒蛇

from PyQt5.QtCore import QPointF, Qt
from PyQt5.QtWidgets import QGraphicsRectItem, QGraphicsScene, QGraphicsView, QApplication
from PyQt5.QtGui import QBrush, QPainter, QPen, QPixmap, QPolygonF
import sys

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rectitem = QGraphicsRectItem(0, 0, 360, 20)
rectitem.setPos(20, 20)
rectitem.setBrush(QBrush(Qt.red))
rectitem.setPen(QPen(Qt.cyan))
scene.addItem(rectitem)

textitem = scene.addText("QGraphics is fun!")
textitem.setPos(100, 100)

scene.addPolygon(
    QPolygonF(
        [
            QPointF(30, 60),
            QPointF(270, 40),
            QPointF(400, 200),
            QPointF(20, 150),
        ]),
    QPen(Qt.darkGreen),
)

pixmap = QPixmap("cat.jpg")
pixmapitem = scene.addPixmap(pixmap)
pixmapitem.setPos(250, 70)

view = QGraphicsView(scene)
view.setRenderHint(QPainter.Antialiasing)
view.show()

app.exec_()

蟒蛇

from PyQt6.QtCore import QPointF, Qt
from PyQt6.QtWidgets import QGraphicsRectItem, QGraphicsScene, QGraphicsView, QApplication
from PyQt6.QtGui import QBrush, QPainter, QPen, QPixmap, QPolygonF
import sys

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rectitem = QGraphicsRectItem(0, 0, 360, 20)
rectitem.setPos(20, 20)
rectitem.setBrush(QBrush(Qt.GlobalColor.red))
rectitem.setPen(QPen(Qt.GlobalColor.cyan))
scene.addItem(rectitem)

textitem = scene.addText("QGraphics is fun!")
textitem.setPos(100, 100)

scene.addPolygon(
    QPolygonF(
        [
            QPointF(30, 60),
            QPointF(270, 40),
            QPointF(400, 200),
            QPointF(20, 150),
        ]),
    QPen(Qt.GlobalColor.darkGreen),
)

pixmap = QPixmap("cat.jpg")
pixmapitem = scene.addPixmap(pixmap)
pixmapitem.setPos(250, 70)

view = QGraphicsView(scene)
view.setRenderHint(QPainter.RenderHint.Antialiasing)
view.show()

app.exec()

如果你运行上面的例子,你会看到下面的场景。

Scene with multiple items 场景中有多个项目,包括一个矩形、多边形、文本和一个像素图。

让我们看一下代码中有趣的部分。

多边形是用一系列的QPointF 对象来定义的,这些对象给出了相对于_项目_位置的坐标。因此,例如,如果你创建了一个点在30,20的多边形对象,然后移动这个多边形对象的X和Y坐标50,40,那么这个点将在场景中显示为80,60。

项目内的点总是相对于项目本身的,而项目坐标总是相对于场景的--或者项目的父对象,如果它有一个的话。我们将在下一个教程中仔细研究图形视图的坐标系统。

为了将图像添加到场景中,我们可以使用QPixmap() 从一个文件中打开它。这将创建一个QPixmap 对象,然后再使用scene.addPixmap(pixmap) 添加到场景中。这将返回一个QGraphicsPixmapItem ,它是像素图的QGraphicsItem 类型--一个处理在场景中显示像素图的包装器。你可以使用这个对象来对场景中的项目进行任何改变。

多层的对象会让人感到困惑,所以选择合理的变量名是很重要的,它可以明确区分,例如,_像素图_本身和包含它的_像素图项_之间的区别。

最后,我们在视图上设置标志RenderHint,Antialiasing ,以_平滑_对角线的边缘。你几乎_总是_想在你的视图上启用这个功能,否则任何旋转的物体看起来都会非常难看。下面是我们没有启用抗锯齿的场景,你可以看到多边形上锯齿状的线条。

Scene with multiple items 禁用抗锯齿的场景。

然而,抗锯齿对性能有(小的)影响,所以如果你正在构建有数百万个旋转项目的场景,在某些情况下,关闭它可能是有意义的。

将图形视图添加到Qt布局中

QGraphicsView 是从QWidget 子类化的,这意味着它可以像其他widget一样被放在布局中。在下面的例子中,我们将视图添加到一个简单的界面中,按钮对视图执行一个基本的效果--提高和降低所选项目的ZValue。这样做的效果是允许我们将项目移到其他对象的前面和后面。

下面给出了完整的代码。

  • PyQt5
  • PyQt6

蟒蛇

import sys

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush, QPainter, QPen
from PyQt5.QtWidgets import (
    QApplication,
    QGraphicsEllipseItem,
    QGraphicsItem,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QHBoxLayout,
    QPushButton,
    QSlider,
    QVBoxLayout,
    QWidget,
)


class Window(QWidget):
    def __init__(self):
        super().__init__()

        # Defining a scene rect of 400x200, with it's origin at 0,0.
        # If we don't set this on creation, we can set it later with .setSceneRect
        self.scene = QGraphicsScene(0, 0, 400, 200)

        # Draw a rectangle item, setting the dimensions.
        rect = QGraphicsRectItem(0, 0, 200, 50)
        rect.setPos(50, 20)
        brush = QBrush(Qt.red)
        rect.setBrush(brush)

        # Define the pen (line)
        pen = QPen(Qt.cyan)
        pen.setWidth(10)
        rect.setPen(pen)

        ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
        ellipse.setPos(75, 30)

        brush = QBrush(Qt.blue)
        ellipse.setBrush(brush)

        pen = QPen(Qt.green)
        pen.setWidth(5)
        ellipse.setPen(pen)

        # Add the items to the scene. Items are stacked in the order they are added.
        self.scene.addItem(ellipse)
        self.scene.addItem(rect)

        # Set all items as moveable and selectable.
        for item in self.scene.items():
            item.setFlag(QGraphicsItem.ItemIsMovable)
            item.setFlag(QGraphicsItem.ItemIsSelectable)

        # Define our layout.
        vbox = QVBoxLayout()

        up = QPushButton("Up")
        up.clicked.connect(self.up)
        vbox.addWidget(up)

        down = QPushButton("Down")
        down.clicked.connect(self.down)
        vbox.addWidget(down)

        rotate = QSlider()
        rotate.setRange(0, 360)
        rotate.valueChanged.connect(self.rotate)
        vbox.addWidget(rotate)

        view = QGraphicsView(self.scene)
        view.setRenderHint(QPainter.Antialiasing)

        hbox = QHBoxLayout(self)
        hbox.addLayout(vbox)
        hbox.addWidget(view)

        self.setLayout(hbox)

    def up(self):
        """ Iterate all selected items in the view, moving them forward. """
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)

    def down(self):
        """ Iterate all selected items in the view, moving them backward. """
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z - 1)

    def rotate(self, value):
        """ Rotate the object by the received number of degrees """
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)


app = QApplication(sys.argv)

w = Window()
w.show()

app.exec()

蟒蛇

import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QBrush, QPainter, QPen
from PyQt6.QtWidgets import (
    QApplication,
    QGraphicsEllipseItem,
    QGraphicsItem,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QHBoxLayout,
    QPushButton,
    QSlider,
    QVBoxLayout,
    QWidget,
)


class Window(QWidget):
    def __init__(self):
        super().__init__()

        # Defining a scene rect of 400x200, with it's origin at 0,0.
        # If we don't set this on creation, we can set it later with .setSceneRect
        self.scene = QGraphicsScene(0, 0, 400, 200)

        # Draw a rectangle item, setting the dimensions.
        rect = QGraphicsRectItem(0, 0, 200, 50)
        rect.setPos(50, 20)
        brush = QBrush(Qt.GlobalColor.red)
        rect.setBrush(brush)

        # Define the pen (line)
        pen = QPen(Qt.GlobalColor.cyan)
        pen.setWidth(10)
        rect.setPen(pen)

        ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
        ellipse.setPos(75, 30)

        brush = QBrush(Qt.GlobalColor.blue)
        ellipse.setBrush(brush)

        pen = QPen(Qt.GlobalColor.green)
        pen.setWidth(5)
        ellipse.setPen(pen)

        # Add the items to the scene. Items are stacked in the order they are added.
        self.scene.addItem(ellipse)
        self.scene.addItem(rect)

        # Set all items as moveable and selectable.
        for item in self.scene.items():
            item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
            item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)

        # Define our layout.
        vbox = QVBoxLayout()

        up = QPushButton("Up")
        up.clicked.connect(self.up)
        vbox.addWidget(up)

        down = QPushButton("Down")
        down.clicked.connect(self.down)
        vbox.addWidget(down)

        rotate = QSlider()
        rotate.setRange(0, 360)
        rotate.valueChanged.connect(self.rotate)
        vbox.addWidget(rotate)

        view = QGraphicsView(self.scene)
        view.setRenderHint(QPainter.RenderHint.Antialiasing)

        hbox = QHBoxLayout(self)
        hbox.addLayout(vbox)
        hbox.addWidget(view)

        self.setLayout(hbox)

    def up(self):
        """ Iterate all selected items in the view, moving them forward. """
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)

    def down(self):
        """ Iterate all selected items in the view, moving them backward. """
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z - 1)

    def rotate(self, value):
        """ Rotate the object by the received number of degrees. """
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)


app = QApplication(sys.argv)

w = Window()
w.show()

app.exec()

如果你运行它,你会得到一个如下所示的窗口。通过在图形视图中选择一个项目,然后点击 "向上 "或 "向下 "按钮,你可以在场景中上下移动项目--在彼此的后面和前面。这些项目都是可移动的,所以你也可以拖动它们。点击滑块将使当前选定的项目旋转设定的度数。

A graphics scene with some custom controls 一个带有一些自定义控件的图形场景

提升和降低是由我们的自定义方法updown 来处理的,它的工作原理是在场景中迭代_当前选定的_项目--使用scene.selectedItems() 检索,然后获得项目的z值,并分别增加或减少它。

蟒蛇

    def up(self):
        """ Iterate all selected items in the view, moving them forward. """
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)

而旋转是使用item.setRotation 方法处理的。这个方法从QSlider 接收当前的角度,并再次将其应用于场景中任何当前选定的项目。

蟒蛇


    def rotate(self, value):
        """ Rotate the object by the received number of degrees. """
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)

看看QGraphicsItem的文档,了解一些你可以用部件控制的其他属性,并尝试扩展接口以允许你动态地改变它们。

希望这个关于Qt图形视图框架的快速介绍能给你一些关于你能用它做什么的想法。在接下来的教程中,我们将看看如何在项目上处理事件和用户交互,以及如何为你自己的场景创建自定义和复合项目。

更多内容请看完整的PyQt5教程。