PySide6 中的 QPainter 和位图图形
QPainter核心功能简介

在 PyQt5 中创建自定义控件的第一步是理解位图(基于像素)图形操作。所有标准控件都以位图的形式绘制在构成控件形状的矩形“画布”上。一旦你理解了它的工作原理,你就可以绘制任何你想要的控件了!

位图是由像素组成的矩形网格,其中每个像素都单独存储为若干比特。这与矢量图形不同,矢量图形中的图像存储为一系列重复执行的绘图指令,从而形成图像。

在本教程中,我们将了解QPainterQt 的 API,它用于执行位图图形操作,并奠定绘制自定义控件的基础。我们将学习一些基本的绘图操作,最后将所有内容整合起来,创建一个我们自己的简易绘图应用程序。

基本上就是油漆。

QPainter

在 Qt 中,位图绘制操作由 `Pixmap` 类处理QPainter。这是一个通用接口,可用于在各种表面上绘制图形,例如 `<div>` 和 `<div> QPixmap`。为了便于演示,我们将使用以下示例应用程序,该程序负责创建容器(`<div>` QLabel)、创建位图画布、在容器中显示位图以及将容器添加到主窗口。

Python
import sys
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Qt


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.label = QtWidgets.QLabel()
        canvas = QtGui.QPixmap(400, 300)
        canvas.fill(Qt.white)
        self.label.setPixmap(canvas)
        self.setCentralWidget(self.label)
        self.draw_something()

    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        painter.drawLine(10, 10, 300, 200)
        painter.end()
        self.label.setPixmap(canvas)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

为什么我们用它QLabel来绘制图形?该QLabel控件还可以用来显示图像,它是目前最简单的用于显示图形的控件QPixmap

在 PySide2 中,我们可以直接在标签内的 像素图上绘制图形。但在 PySide6 中,我们需要先获取像素图self.label.pixmap(),对其进行修改,然后再将其应用到标签上。第二种方法在 PySide2 中也适用,因此我们将在下文中使用它。

Martin Fitzpatrick 的《使用 PyInstaller 打包 Python 应用程序》 ——本分步指南将引导您完成 Python 应用程序的打包,从简单的示例到完整的安装程序和签名可执行文件。

 

将此保存到文件中并运行,您应该会看到以下内容——窗口框内有一条黑线——

画布上只有一条黑线。

画布上只有一条黑线。

所有绘制操作都在draw_something方法内部完成——我们创建一个QPainter实例,传入画布(self.label.pixmap()),然后发出绘制线条的命令。最后,我们调用.end()关闭绘制器并应用更改。

通常还需要调用方法.update()来触发小部件的刷新,但由于我们在应用程序窗口显示之前进行绘制,因此已经会发生刷新。

绘制基本图形

QPainter它提供了大量用于在位图表面上绘制形状和线条的方法(在 5.12 版本中,有 192 个QPainter特定的非事件方法)。好消息是,其中大多数是重载方法,它们只是调用相同基方法的不同方式。

例如,有 5 种不同的drawLine方法,它们都画出同一条线,但绘制该线的坐标定义方式不同。

方法 描述
drawLine(line) 绘制一个QLine实例
drawLine(line) 绘制一个QLineF实例
drawLine(x1, y1, x2, y2) 在 x1, y2 和 x2, y2 之间画一条线 ( int)
drawLine(p1, p2) 在 p1 和 p2 之间画一条线(两者都要画QPoint)。
drawLine(p1, p2) 在 p1 和 p2 之间画一条线(两者都要画QPointF)。

QLine如果你想知道 a和 a之间的区别QLineF,后者是用 指定的坐标float。如果你已经通过其他计算得到了浮点位置,这很方便,但除此之外就没什么用了。

忽略 F 变体,我们有三种独特的方式来绘制直线——使用直线对象、使用两组坐标(x1, y1), (x2, y2) 或使用两个QPoint对象。当你发现 `a`QLine本身被定义为 `a`QLine(const QPoint & p1, const QPoint & p2)或 `b`QLine(int x1, int y1, int x2, int y2)时,你会发现它们实际上完全相同。不同的调用签名仅仅是为了方便。

给定 x1、y1、x2、y2 坐标,这两个QPoint对象将被定义为QPoint(x1, y1)QPoint(x2, y2)

去掉重复项,我们有以下绘图操作—— ,,,,,,,,,,,,,drawArcdrawChord。为了避免不知所措drawConvexPolygon,我们将首先专注于基本形状和线条,并在掌握基础知识后再回过头来学习更复杂的操作。drawEllipsedrawLinedrawPathdrawPiedrawPointdrawPolygondrawPolylinedrawRectdrawRectsdrawRoundedRect

对于每个示例,请替换draw_something存根应用程序中的方法并重新运行它以查看输出。

绘制点

这段代码会在画布上的指定位置绘制一个点(或像素)drawPoint 。每次调用都会绘制一个像素。请将你的draw_something代码替换为以下代码。

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        painter.drawPoint(200, 150)
        painter.end()
        self.label.setPixmap(canvas)

如果重新运行该文件,你会看到一个窗口,但这次窗口中间会有一个黑色的点。你可能需要移动窗口才能看到它。

使用 QPainter 绘制单个点(像素)

使用 QPainter 绘制单个点(像素)

这看起来确实没什么特别的。为了让画面更有趣,我们可以改变绘制点的颜色和大小。在 PyQt 中,线条的颜色和粗细是通过QPainter 上的活动画笔QPen来定义的。你可以通过创建一个实例并应用它来进行设置。

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(40)
        pen.setColor(QtGui.QColor('red'))
        painter.setPen(pen)
        painter.drawPoint(200, 150)
        painter.end()
        self.label.setPixmap(canvas)

这将带来以下略微有趣的结果……

一个大大的红点。

一个大大的红点。

您可以随意执行多次绘制操作,QPainter直到绘图程序结束。在画布上绘制非常快——这里我们随机绘制了 1 万个点。

Python
from random import randint  # add this import at the top of the file.

    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(3)
        painter.setPen(pen)

        for n in range(10000):
            painter.drawPoint(
                200+randint(-100, 100),  # x
                150+randint(-100, 100)   # y
                )
        painter.end()
        self.label.setPixmap(canvas)

这些点宽为 3 像素,颜色为黑色(默认画笔)。

画布上的 1 万个 3 像素点

画布上的 1 万个 3 像素点

在绘图过程中,您经常需要更新当前画笔——例如,在保持其他属性(例如宽度)不变的情况下,绘制多个不同颜色的点。为了避免每次都创建新的画笔实例,您可以使用`using` 方法QPen获取当前活动的画笔。您还可以多次重新应用现有的画笔,每次都进行更改。QPainterpen = painter.pen()

Python
from random import randint, choice # add this import at the top of the file.

    def draw_something(self):
        colors = ['#FFD141', '#376F9F', '#0D1F2D', '#E9EBEF', '#EB5160']

        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(3)
        painter.setPen(pen)

        for n in range(10000):
            # pen = painter.pen() you could get the active pen here
            pen.setColor(QtGui.QColor(choice(colors)))
            painter.setPen(pen)
            painter.drawPoint(
                200+randint(-100, 100),  # x
                150+randint(-100, 100)   # y
                )
        painter.end()
        self.label.setPixmap(canvas)

将产生以下输出——

随机排列的3个宽度的点

随机排列的3个宽度的点

QPen一支笔上 只能有一个活动笔QPainter——即当前笔。

在屏幕上画点所能带来的兴奋感也就到此为止了,所以我们接下来将去看一些其他的绘图操作。

画线

我们一开始已经在画布上画了一条线来测试功能是否正常。但我们没有尝试设置画笔来控制线条的外观。

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(15)
        pen.setColor(QtGui.QColor('blue'))
        painter.setPen(pen)
        painter.drawLine(
            QtCore.QPoint(100, 100),
            QtCore.QPoint(300, 200)
        )
        painter.end()
        self.label.setPixmap(canvas)

在这个例子中,我们也使用QPoint定义要用直线连接的两个点,而不是传递单独的x1, y1, x2, y2参数——记住,这两种方法在功能上是相同的。

一条粗蓝线

一条粗蓝线

drawRect、drawRects 和 drawRoundedRect

这些函数都绘制矩形,矩形由矩形的坐标和 a 定义x,或者由提供y等效信息的实例定义。widthheightQRectQRectF

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(3)
        pen.setColor(QtGui.QColor("#EB5160"))
        painter.setPen(pen)
        painter.drawRect(50, 50, 100, 100)
        painter.drawRect(60, 60, 150, 100)
        painter.drawRect(70, 70, 100, 150)
        painter.drawRect(80, 80, 150, 100)
        painter.drawRect(90, 90, 100, 150)
        painter.end()
        self.label.setPixmap(canvas)

正方形就是宽度和高度都相同的长方形。

绘制矩形

绘制矩形

你也可以drawRect用一次调用并drawRects传入多个QRect对象来替换多次调用。这样会产生完全相同的结果。

Python
painter.drawRects(
    QtCore.QRect(50, 50, 100, 100),
    QtCore.QRect(60, 60, 150, 100),
    QtCore.QRect(70, 70, 100, 150),
    QtCore.QRect(80, 80, 150, 100),
    QtCore.QRect(90, 90, 100, 150),
)

在 PyQt 中,可以通过设置当前活动的画笔,并传入一个QBrush实例来填充绘制的形状painter.setBrush()。以下示例将所有矩形填充为带有图案的黄色。

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(3)
        pen.setColor(QtGui.QColor("#376F9F"))
        painter.setPen(pen)

        brush = QtGui.QBrush()
        brush.setColor(QtGui.QColor("#FFD141"))
        brush.setStyle(Qt.Dense1Pattern)
        painter.setBrush(brush)

        painter.drawRects([
            QtCore.QRect(50, 50, 100, 100),
            QtCore.QRect(60, 60, 150, 100),
            QtCore.QRect(70, 70, 100, 150),
            QtCore.QRect(80, 80, 150, 100),
            QtCore.QRect(90, 90, 100, 150),
        ])
        painter.end()
        self.label.setPixmap(canvas)

填充矩形

填充矩形

至于画笔,每个画家的笔刷一次只能激活一支,但你可以在绘画过程中切换或更换画笔。有很多笔刷样式可供选择。不过,你可能会用到Qt.SolidPattern最常用的几种。

必须设置样式才能看到任何填充效果,因为默认值为Qt.NoBrush

这些drawRoundedRect方法绘制一个矩形,但边缘是圆角,因此需要两个额外的参数来表示角的x 和 y半径。

Python
from random import randint  # add this import at the top of the file.

    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(3)
        pen.setColor(QtGui.QColor("#376F9F"))
        painter.setPen(pen)
        painter.drawRoundedRect(40, 40, 100, 100, 10, 10)
        painter.drawRoundedRect(80, 80, 100, 100, 10, 50)
        painter.drawRoundedRect(120, 120, 100, 100, 50, 10)
        painter.drawRoundedRect(160, 160, 100, 100, 50, 50)
        painter.end()
        self.label.setPixmap(canvas)

圆角矩形。

圆角矩形。

还有一个可选的最终参数,用于切换椭圆角点的 x 和 y 半径是以绝对像素值Qt.RelativeSize(默认值)还是相对于矩形大小(以 0…100 为值传递)定义的。传递此参数Qt.RelativeSize以启用此功能。

绘制椭圆

我们现在要介绍的最后一个基本绘图方法是,drawEllipse它可以用来绘制椭圆

使用 Python 和 Qt6 创建 GUI 应用程序 (作者:Martin Fitzpatrick )——(PyQt6 版)使用 Python 制作应用程序的实用指南——销量超过 10,000 册!

 

圆其实就是宽度和高度相等的椭圆。

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(3)
        pen.setColor(QtGui.QColor(204,0,0))  # r, g, b
        painter.setPen(pen)

        painter.drawEllipse(10, 10, 100, 100)
        painter.drawEllipse(10, 10, 150, 200)
        painter.drawEllipse(10, 10, 200, 300)
        painter.end()
        self.label.setPixmap(canvas)

在这个例子中,它需要 4 个参数,前两个参数是绘制椭圆的矩形左上角drawEllipse的 x 和 y 位置,后两个参数分别是该矩形的宽度和高度。

绘制具有 x、y、宽度、高度或 QRect 的椭圆。

绘制具有 x、y、宽度、高度或 QRect 的椭圆。

您可以通过传入一个参数来实现同样的效果。QRect

还有另一种调用签名,它将椭圆的中心作为第一个参数(以QPoint对象QPointF形式提供),然后是 ax 和 y轴半径。下面的示例展示了它的实际应用。

Python
painter.drawEllipse(QtCore.QPoint(100, 100), 10, 10)
painter.drawEllipse(QtCore.QPoint(100, 100), 15, 20)
painter.drawEllipse(QtCore.QPoint(100, 100), 20, 30)
painter.drawEllipse(QtCore.QPoint(100, 100), 25, 40)
painter.drawEllipse(QtCore.QPoint(100, 100), 30, 50)
painter.drawEllipse(QtCore.QPoint(100, 100), 35, 60)

利用点和半径绘制椭圆。

利用点和半径绘制椭圆。

填充椭圆的设置方式与QBrush填充矩形相同,样式和颜色等功能也相同。

文本

最后,我们简要了解一下QPainter文本绘制方法。要控制当前字体,QPainter需要setFont传入一个QFont实例。通过这种方式,您可以控制所写文本的字体系列、粗细和大小(以及其他属性)。但是,文本的颜色仍然由当前画笔定义。

Python
    def draw_something(self):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        pen = QtGui.QPen()
        pen.setWidth(1)
        pen.setColor(QtGui.QColor('green'))
        painter.setPen(pen)

        font = QtGui.QFont()
        font.setFamily('Times')
        font.setBold(True)
        font.setPointSize(40)
        painter.setFont(font)

        painter.drawText(100, 100, 'Hello, world!')
        painter.end()
        self.label.setPixmap(canvas)

您还可以使用QPoint或指定位置QPointF

笔的粗细对文字的外观没有影响。

位图文本“Hello World”示例。

位图文本“Hello World”示例。

还有一些方法可以在指定区域内绘制文本。这里的参数定义了边界框的 x 和 y 坐标以及宽度和高度。超出此框的文本将被裁剪(隐藏)。第五个参数标志可用于控制框内文本的对齐方式等。

Python
painter.drawText(100, 100, 100, 100, Qt.AlignHCenter, 'Hello, world!')

drawText 时边界框被裁剪。

drawText 时边界框被裁剪。

您可以通过对象在绘图器上设置活动字体,从而完全控制文本的显示。有关更多信息,QFont请参阅QFont 文档。

用 QPainter 做点有趣的事

刚才讲的内容有点深奥,我们来轻松一下,做点有趣的事情。到目前为止,我们一直在用编程的方式定义要执行的绘制操作。但我们也可以很容易地根据用户输入进行绘制——例如,允许用户在画布上随意涂鸦。在这一部分,我们将运用目前所学的知识,构建一个简易的绘图应用程序。

我们可以从同样的简单应用程序框架开始,用一个mouseMoveEvent事件处理程序MainWindow替换类中的绘制方法。在这里,我们将获取用户鼠标的当前位置,并在画布上绘制一个点。

Python
import sys
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Qt


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.label = QtWidgets.QLabel()
        canvas = QtGui.QPixmap(400, 300)
        canvas.fill(Qt.white)
        self.label.setPixmap(canvas)
        self.setCentralWidget(self.label)

    def mouseMoveEvent(self, e):
        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        painter.drawPoint(e.x(), e.y())
        painter.end()
        self.label.setPixmap(canvas)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

为什么没有点击事件?默认情况下,控件仅在按下鼠标按钮时接收鼠标移动事件,除非启用了鼠标跟踪.setMouseTracking。可以使用该方法进行配置——将其设置为True(默认情况下已False启用)将持续跟踪鼠标。

保存并运行此程序后,您应该能够移动鼠标并在屏幕上点击来绘制单个点。它应该看起来像这样——

绘制单个鼠标移动事件点。

绘制单个鼠标移动事件点。

问题在于,当你快速移动鼠标时,它实际上会在屏幕上的不同位置之间跳跃,而不是平滑地从一个位置移动到另一个位置。虽然mouseMoveEvent鼠标所在的每个位置都会触发一次事件,但这不足以绘制一条连续的线条,除非你移动得非常慢

解决这个问题的方法是画线而不是。对于每个事件,我们只需从之前的位置(e.x()e.y())画一条线到现在的位置(e.x()e.y())。我们可以通过跟踪last_xlast_y自身来实现这一点。

我们也需要在释放鼠标时忘记鼠标的最后位置,否则在将鼠标移动到页面上后,我们将再次从该位置开始绘制——也就是说,我们将无法断开线条。

Python
import sys
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Qt


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.label = QtWidgets.QLabel()
        canvas = QtGui.QPixmap(400, 300)
        canvas.fill(Qt.white)
        self.label.setPixmap(canvas)
        self.setCentralWidget(self.label)

        self.last_x, self.last_y = None, None

    def mouseMoveEvent(self, e):
        if self.last_x is None: # First event.
            self.last_x = e.x()
            self.last_y = e.y()
            return # Ignore the first time.

        canvas = self.label.pixmap()
        painter = QtGui.QPainter(canvas)
        painter.drawLine(self.last_x, self.last_y, e.x(), e.y())
        painter.end()
        self.label.setPixmap(canvas)

        # Update the origin for next time.
        self.last_x = e.x()
        self.last_y = e.y()

    def mouseReleaseEvent(self, e):
        self.last_x = None
        self.last_y = None


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

运行此程序后,您应该能够像预期那样在屏幕上涂鸦。

用鼠标绘制,使用连续线条。

用鼠标绘制,使用连续线条。

它现在看起来还是有点单调,所以我们添加一个简单的调色板,以便我们可以改变画笔颜色。

这需要对架构进行一些重新设计。目前我们一直使用 ` mouseMoveEventon` 属性QMainWindow。当窗口中只有一个控件时,这样做没问题——只要窗口大小不超过控件大小(你试过吗?),容器和嵌套控件的坐标就能对齐。但是,如果我们在布局中添加其他控件,这种方法就行不通了——控件的坐标QLabel会相对于窗口发生偏移,导致绘制位置错误。

这个问题很容易解决,只需将鼠标处理移到对象QLabel本身即可——它的事件坐标始终相对于自身。我们将其封装成一个自定义Canvas对象,该对象负责创建像素图表面,设置 x 和 y 坐标,并保存当前画笔颜色(默认为黑色)。

这是一个独立的、Canvas可直接插入的可绘制表面,您可以在自己的应用程序中使用它。

Python
import sys
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Qt


class Canvas(QtWidgets.QLabel):

    def __init__(self):
        super().__init__()
        pixmap = QtGui.QPixmap(600, 300)
        pixmap.fill(Qt.white)
        self.setPixmap(pixmap)

        self.last_x, self.last_y = None, None
        self.pen_color = QtGui.QColor('#000000')

    def set_pen_color(self, c):
        self.pen_color = QtGui.QColor(c)

    def mouseMoveEvent(self, e):
        if self.last_x is None: # First event.
            self.last_x = e.x()
            self.last_y = e.y()
            return # Ignore the first time.

        canvas = self.pixmap()
        painter = QtGui.QPainter(canvas)
        p = painter.pen()
        p.setWidth(4)
        p.setColor(self.pen_color)
        painter.setPen(p)
        painter.drawLine(self.last_x, self.last_y, e.x(), e.y())
        painter.end()
        self.setPixmap(canvas)

        # Update the origin for next time.
        self.last_x = e.x()
        self.last_y = e.y()

    def mouseReleaseEvent(self, e):
        self.last_x = None
        self.last_y = None

为了实现颜色选择,我们将基于现有组件构建一个自定义组件QPushButton。该组件接受一个color参数,该参数可以是QColour实例、颜色名称(例如“红色”、“黑色”)或十六进制值。此颜色将设置为组件的背景色,使其易于识别。我们可以使用标准QPushButton.pressed信号将其连接到任何操作。

Python
COLORS = [
# 17 undertones https://lospec.com/palette-list/17undertones
'#000000', '#141923', '#414168', '#3a7fa7', '#35e3e3', '#8fd970', '#5ebb49',
'#458352', '#dcd37b', '#fffee5', '#ffd035', '#cc9245', '#a15c3e', '#a42f3b',
'#f45b7a', '#c24998', '#81588d', '#bcb0c2', '#ffffff',
]


class QPaletteButton(QtWidgets.QPushButton):

    def __init__(self, color):
        super().__init__()
        self.setFixedSize(QtCore.QSize(24,24))
        self.color = color
        self.setStyleSheet("background-color: %s;" % color)

定义了这两个新部分之后,我们只需要遍历颜色列表,创建一个QPaletteButton颜色传递,将其按下信号连接到set_pen_color画布上的处理程序(通过lambda传递额外的颜色数据间接连接),并将其添加到调色板布局中。

Python
class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.canvas = Canvas()

        w = QtWidgets.QWidget()
        l = QtWidgets.QVBoxLayout()
        w.setLayout(l)
        l.addWidget(self.canvas)

        palette = QtWidgets.QHBoxLayout()
        self.add_palette_buttons(palette)
        l.addLayout(palette)

        self.setCentralWidget(w)

    def add_palette_buttons(self, layout):
        for c in COLORS:
            b = QPaletteButton(c)
            b.pressed.connect(lambda c=c: self.canvas.set_pen_color(c))
            layout.addWidget(b)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

这样您就可以使用功能齐全的多色绘画应用程序,在画布上绘制线条并从调色板中选择颜色。

可惜的是,这并不能让你成为一名优秀的艺术家。

可惜的是,这并不能让你成为一名优秀的艺术家。

喷 (pen 这个翻译着实有点。。。尴尬)

最后,为了增加一些趣味性,你可以将 `<br>` 替换mouseMoveEvent为以下代码,用“喷漆罐”效果而不是线条来绘制图形。这是通过random.gauss生成一系列围绕当前鼠标位置呈正态分布的drawPoint点来模拟的,我们用 `<br>` 绘制这些点。

Python
    def mouseMoveEvent(self, e):
        canvas = self.pixmap()
        painter = QtGui.QPainter(canvas)
        p = painter.pen()
        p.setWidth(1)
        p.setColor(self.pen_color)
        painter.setPen(p)

        for n in range(SPRAY_PARTICLES):
            xo = random.gauss(0, SPRAY_DIAMETER)
            yo = random.gauss(0, SPRAY_DIAMETER)
            painter.drawPoint(e.x() + xo, e.y() + yo)

        self.setPixmap(canvas)

在文件顶部定义 ` SPRAY_PARTICLESand`变量,并导入标准库模块。下图显示了使用以下设置时的喷雾行为:SPRAY_DIAMETERrandom

Python
import random

SPRAY_PARTICLES = 100
SPRAY_DIAMETER = 10

对于喷罐来说,我们不需要跟踪之前的位置,因为我们总是围绕当前点进行喷洒。

如果你想挑战一下自己,可以尝试添加一个额外的按钮来切换绘制和喷涂模式,或者添加一个输入来定义画笔/喷雾直径。

想要体验用 PyQt5 编写的功能齐全的绘图程序,请查看我的15 分钟应用程序“Piecasso”。

这篇介绍应该让你对它的功能有了大致的了解QPainter。正如前面提到的,这个系统是所有控件绘制的基础。如果你想深入了解,可以查看控件.paint()方法,该方法接收一个QPainter实例,允许控件在自身上绘制图形。你在这里学到的方法同样可以用于.paint()绘制一些基本的自定义控件。我们将在下一篇教程中详细讲解。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐