首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PySide6 GUI 编程(42):QPainter 的使用

PySide6 GUI 编程(42):QPainter 的使用

原创
作者头像
密码学人CipherHUB
修改2024-09-15 09:50:53
修改2024-09-15 09:50:53
95730
代码可运行
举报
文章被收录于专栏:编码视界编码视界
运行总次数:0
代码可运行

QPainter的作用

QPainter 是 PySide6 中用于在小部件和其他绘图设备上进行低级绘图的类。

它提供了一系列的绘图函数,可以绘制从简单线条到复杂形状(如饼图和和弦图)的各种图形。

QPainter 还可以绘制对齐文本和图片。

通常,它使用“自然”坐标系进行绘图,但也支持视图和世界坐标转换。

QPainter 的基础使用范式

绘制线条的示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys
from datetime import datetime

from PySide6.QtCore import QLine, QPoint, Qt
from PySide6.QtGui import QPainter, QPixmap
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow


def get_time_str() -> str:
    return datetime.now().isoformat(sep = ' ')


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布大小
        self.pixmap = QPixmap(400, 300)
        self.pixmap.fill(Qt.GlobalColor.black)

        # 在画布上绘制图线
        self.painter = QPainter(self.pixmap)
        self.painter.setPen(Qt.GlobalColor.red)
        # 使用坐标画线
        self.painter.drawLine(0, 0, 400, 300)
        self.painter.drawLine(400, 0, 0, 300)
        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        self.painter.end()

        # 使用 QLine 画线
        self.painter = QPainter(self.pixmap)
        self.painter.setPen(Qt.GlobalColor.blue)
        self.painter.drawLine(QLine(0, 150, 400, 150))
        self.painter.end()

        # 使用 QPoint 画线
        self.painter = QPainter(self.pixmap)
        self.painter.setPen(Qt.GlobalColor.cyan)
        self.painter.drawLine(QPoint(0, 100), QPoint(400, 100))
        self.painter.end()

        self.label = QLabel()
        self.label.setPixmap(self.pixmap)
        self.setCentralWidget(self.label)

        self.update()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

通常来说,使用 QPainter 画图的步骤为:

  1. 设置画布大小 self.pixmap = QPixmap(400, 300)
  2. 初始化QPainter self.painter = QPainter(self.pixmap)
  3. 设置画笔 self.painter.setPen(Qt.GlobalColor.red)
  4. 画图 self.painter.drawxxxx
  5. 释放 painter 实例 self.painter.end()
  6. 刷新图形到画布 self.label.setPixmap(self.pixmap)
  7. 刷新所有视图 self.update()

运行效果

使用 QPainter 画线
使用 QPainter 画线

QPainter常用的基础图形

绘制坐标点

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys
from random import choice, randint

from PySide6.QtCore import QPoint, Qt
from PySide6.QtGui import QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布大小
        self.pixmap = QPixmap(400, 300)
        self.pixmap.fill(Qt.GlobalColor.black)

        # 在画布上绘制图线
        self.painter = QPainter(self.pixmap)
        self.pen = QPen()
        self.pen.setWidth(3)
        self.pen.setColor(Qt.GlobalColor.red)
        self.painter.setPen(self.pen)
        colors = [x for x in Qt.GlobalColor]
        # 使用坐标画线
        for i in range(1, 10000):
            self.pen.setColor(choice(colors))
            # 这里需要每次都重新设置 pen,否则颜色不生效
            # There can only ever be one  QPen active on a QPainter
            # -- the current pen.
            self.painter.setPen(self.pen)
            self.painter.drawPoint(QPoint(200 + randint(-100, 100), 150 + randint(-100, 100)))
        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,会导致程序崩溃或者运行效率降低
        """
        self.painter.end()

        self.label = QLabel()
        self.label.setPixmap(self.pixmap)
        self.setCentralWidget(self.label)

        self.update()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

运行效果

随机彩色矩形点阵
随机彩色矩形点阵

绘制矩形

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys

from PySide6.QtCore import QRect, Qt
from PySide6.QtGui import QBrush, QColor, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布大小
        self.pixmap = QPixmap(400, 300)
        self.pixmap.fill(Qt.GlobalColor.black)

        # 在画布上绘制图线
        self.painter = QPainter(self.pixmap)
        self.pen = QPen()
        self.pen.setWidth(3)
        self.pen.setColor(Qt.GlobalColor.red)
        self.painter.setPen(self.pen)
        # 第一个参数:x 坐标,表示矩形左上角的水平位置
        # 第二个参数:y 坐标,表示矩形左上角的垂直位置
        # 第三个参数:width,表示矩形的宽度
        # 第四个参数:height,表示矩形的高度
        self.painter.drawRect(10, 10, 200, 200)

        self.pen.setColor(Qt.GlobalColor.blue)
        self.painter.setPen(self.pen)
        self.painter.drawRects([QRect(20, 20, 20, 20),
                                QRect(20, 20, 30, 30),
                                QRect(20, 20, 40, 40),
                                QRect(20, 20, 50, 50),
                                QRect(20, 20, 60, 60)])

        self.pen.setColor(Qt.GlobalColor.green)
        self.painter.setPen(self.pen)
        self.brush = QBrush()
        self.brush.setStyle(Qt.BrushStyle.SolidPattern)
        self.brush.setColor(QColor("#FFD007"))
        self.painter.setBrush(self.brush)
        self.painter.drawRoundedRect(QRect(85, 85, 100, 100), 100, 100, Qt.SizeMode.RelativeSize)

        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        self.painter.end()

        self.label = QLabel()
        self.label.setPixmap(self.pixmap)
        self.setCentralWidget(self.label)

        self.update()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

上述示例中,使用drawRectdrawRectsdrawRoundedRect分别绘制了矩形,其中,drawRoundedRect中,当把曲率调位 100 时,矩形将会变为圆形。

drawRoundedRect函数原型
drawRoundedRect函数原型

运行效果

绘制矩形
绘制矩形

绘制椭圆形

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys

from PySide6.QtCore import QPoint, Qt
from PySide6.QtGui import QBrush, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布大小
        self.pixmap = QPixmap(400, 300)
        self.pixmap.fill(Qt.GlobalColor.black)

        # 在画布上绘制图线
        self.painter = QPainter(self.pixmap)
        self.pen = QPen()
        self.pen.setWidth(3)
        self.pen.setColor(Qt.GlobalColor.green)
        self.painter.setPen(self.pen)
        self.brush = QBrush()
        self.brush.setStyle(Qt.BrushStyle.SolidPattern)
        self.brush.setColor(Qt.GlobalColor.red)
        self.painter.setBrush(self.brush)
        self.painter.drawEllipse(QPoint(200, 150), 100, 50)

        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        self.painter.end()

        self.label = QLabel()
        self.label.setPixmap(self.pixmap)
        self.setCentralWidget(self.label)

        self.update()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

运行效果

绘制椭圆形
绘制椭圆形

绘制文本

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys
from datetime import datetime

from PySide6.QtCore import Qt
from PySide6.QtGui import QFont, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布大小
        self.pixmap = QPixmap(600, 400)
        self.pixmap.fill(Qt.GlobalColor.black)

        # 在画布上绘制图线
        self.painter = QPainter(self.pixmap)
        self.pen = QPen()
        self.pen.setWidth(5)
        self.pen.setColor(Qt.GlobalColor.darkMagenta)
        self.painter.setPen(self.pen)

        self.font = QFont('ComicShannsMono Nerd Font', 40)
        # self.font.setBold(True)
        self.painter.setFont(self.font)
        self.painter.drawText(0, 0, 600, 400, Qt.AlignmentFlag.AlignCenter,
                              'Hello, PySide6!\n你好,GUI 编程!\n{}\n{}'.format(datetime.now().isoformat(), self.font.family()))
        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        self.painter.end()

        self.label = QLabel()
        self.label.setPixmap(self.pixmap)
        self.setCentralWidget(self.label)

        self.update()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

运行效果

绘制文本
绘制文本

QPainter 的高级用法

基于鼠标坐标绘制线条

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys

from PySide6.QtCore import Qt
from PySide6.QtGui import QMouseEvent, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布初始状态
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(Qt.GlobalColor.white)

        # 设置布局
        self.label = QLabel()
        self.label.setPixmap(self.pixmap)
        self.button = QPushButton('清除所有')
        self.button.clicked.connect(self.clear_all)
        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.label)
        self.v_layout.addWidget(self.button)
        self.container = QWidget()
        self.container.setLayout(self.v_layout)
        self.setCentralWidget(self.container)

        # 鼠标坐标
        self.last_x = None
        self.last_y = None

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        pos = event.position()
        if self.last_x is None and self.last_y is None:
            self.last_x = pos.x()
            self.last_y = pos.y()
            return

        # 在画布上绘制图线
        painter = QPainter(self.pixmap)
        pen = QPen()
        pen.setWidth(3)
        pen.setStyle(Qt.PenStyle.SolidLine)
        pen.setCosmetic(True)
        pen.setColor(Qt.GlobalColor.darkMagenta)
        painter.setPen(pen)
        painter.drawLine(self.last_x, self.last_y, int(pos.x()), int(pos.y()))
        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        painter.end()
        # 更新QLabel中显示的内容,使其显示最新的QPixmap
        self.label.setPixmap(self.pixmap)
        # 记录上一次的鼠标坐标
        self.last_x = pos.x()
        self.last_y = pos.y()
        # 显式刷新界面
        self.update()

    def mouseReleaseEvent(self, event: QMouseEvent) -> None:
        self.last_x = None
        self.last_y = None

    def clear_all(self) -> None:
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(Qt.GlobalColor.white)
        self.label.setPixmap(self.pixmap)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

运行效果

使用鼠标画图
使用鼠标画图

更改画笔颜色

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import sys

from PySide6.QtCore import Qt
from PySide6.QtGui import QMouseEvent, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布初始状态
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(Qt.GlobalColor.white)

        # 设置布局
        self.label = QLabel()
        self.label.setPixmap(self.pixmap)

        # 颜色列表
        self.colors_v_layout = QVBoxLayout()
        colors_h_layout = QHBoxLayout()
        self.colors_map = {}
        colors_count = 0
        for x in Qt.GlobalColor:
            self.colors_map[x.name] = x
            # css中没有 darkYellow 这种颜色,需要用 #CCCC00 来表示
            # 因此为了逻辑上的方便,QPushButton 中不使用 darkYellow 来作为背景色
            # 同时像 transparent、color0、color1 这样的颜色也过滤掉
            # 由于画板的背景色已经是白色,因此这里也将 white 过滤掉
            if x.name in ('transparent', 'white', 'color0', 'color1', 'darkYellow'):
                continue
            color_button = QPushButton(x.name)
            style = """
            QPushButton { background-color: %s; color: white; }
            QPushButton:pressed { background-color: #CCCC00; }
            """ % x.name
            color_button.setStyleSheet(style)
            color_button.clicked.connect(self.color_changed)
            colors_h_layout.addWidget(color_button)
            colors_count += 1
            if colors_count % 9 == 0:
                self.colors_v_layout.addLayout(colors_h_layout)
                colors_h_layout = QHBoxLayout()
                colors_count = 0
        self.colors_v_layout.addLayout(colors_h_layout)

        # 清除按钮
        self.button = QPushButton('清除所有')
        self.button.clicked.connect(self.clear_all)

        # 布局
        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.label)
        self.v_layout.addLayout(self.colors_v_layout)
        self.v_layout.addWidget(self.button)

        # 容器
        self.container = QWidget()
        self.container.setLayout(self.v_layout)

        # 组件排布
        self.setCentralWidget(self.container)

        # 鼠标坐标
        self.last_x = None
        self.last_y = None
        self.current_color = Qt.GlobalColor.darkMagenta

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        pos = event.position()
        if self.last_x is None and self.last_y is None:
            self.last_x = pos.x()
            self.last_y = pos.y()
            return

        # 在画布上绘制图线
        painter = QPainter(self.pixmap)
        pen = QPen()
        pen.setWidth(3)
        pen.setStyle(Qt.PenStyle.SolidLine)
        pen.setCosmetic(True)
        pen.setColor(self.current_color)
        painter.setPen(pen)
        painter.drawLine(self.last_x, self.last_y, int(pos.x()), int(pos.y()))
        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        painter.end()
        # 更新QLabel中显示的内容,使其显示最新的QPixmap
        self.label.setPixmap(self.pixmap)
        # 记录上一次的鼠标坐标
        self.last_x = pos.x()
        self.last_y = pos.y()
        # 显式刷新界面
        self.update()

    def mouseReleaseEvent(self, event: QMouseEvent) -> None:
        self.last_x = None
        self.last_y = None

    def clear_all(self) -> None:
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(Qt.GlobalColor.white)
        self.label.setPixmap(self.pixmap)

    def color_changed(self) -> None:
        self.current_color = self.colors_map[self.sender().text()]


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

界面静态效果

界面静态效果
界面静态效果

动态效果

更改画笔颜色
更改画笔颜色

喷雾效果

示例代码

代码语言:python
代码运行次数:0
运行
复制
from __future__ import annotations

import random
import sys
from cmath import pi

from PySide6.QtCore import Qt
from PySide6.QtGui import QMouseEvent, QPainter, QPen, QPixmap
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')

        # 设置画布初始状态
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(Qt.GlobalColor.white)

        # 设置布局
        self.label = QLabel()
        self.label.setPixmap(self.pixmap)

        # 颜色列表
        self.colors_v_layout = QVBoxLayout()
        colors_h_layout = QHBoxLayout()
        self.colors_map = {}
        colors_count = 0
        for x in Qt.GlobalColor:
            self.colors_map[x.name] = x
            # css中没有 darkYellow 这种颜色,需要用 #CCCC00 来表示
            # 因此为了逻辑上的方便,QPushButton 中不使用 darkYellow 来作为背景色
            # 同时像 transparent、color0、color1 这样的颜色也过滤掉
            # 由于画板的背景色已经是白色,因此这里也将 white 过滤掉
            if x.name in ('transparent', 'white', 'color0', 'color1', 'darkYellow'):
                continue
            color_button = QPushButton(x.name)
            style = """
            QPushButton { background-color: %s; color: white; }
            QPushButton:pressed { background-color: #CCCC00; }
            """ % x.name
            color_button.setStyleSheet(style)
            color_button.clicked.connect(self.color_changed)
            colors_h_layout.addWidget(color_button)
            colors_count += 1
            if colors_count % 9 == 0:
                self.colors_v_layout.addLayout(colors_h_layout)
                colors_h_layout = QHBoxLayout()
                colors_count = 0
        self.colors_v_layout.addLayout(colors_h_layout)

        # 清除按钮
        self.button = QPushButton('清除所有')
        self.button.clicked.connect(self.clear_all)

        # 布局
        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.label)
        self.v_layout.addLayout(self.colors_v_layout)
        self.v_layout.addWidget(self.button)

        # 容器
        self.container = QWidget()
        self.container.setLayout(self.v_layout)

        # 组件排布
        self.setCentralWidget(self.container)

        # 鼠标坐标
        self.current_color = Qt.GlobalColor.darkMagenta

    def mousePressEvent(self, event: QMouseEvent) -> None:
        pos = event.position()
        # 在画布上绘制图线
        painter = QPainter(self.pixmap)
        pen = QPen()
        pen.setWidth(3)
        pen.setStyle(Qt.PenStyle.SolidLine)
        pen.setCosmetic(True)
        pen.setColor(self.current_color)
        painter.setPen(pen)
        radius = 15
        for _ in range(0, int(pi * (radius ** 2))):
            x_offset = random.randint(0 - radius, radius)
            y_offset = random.randint(0 - radius, radius)
            # 画出一个半径为 radius 的圆
            if int(x_offset) ** 2 + int(y_offset) ** 2 <= radius ** 2:
                painter.drawPoint(int(pos.x() + x_offset), int(pos.y() + y_offset))
        """
        如果不调用这个方法,那么这些资源可能会被锁定,导致无法被其他QPainter对象使用,
        或者在某些情况下可能会导致内存泄漏,特别是当你在一个循环中多次使用QPainter对象时,
        如果不调用end()方法,可能会导致程序崩溃或者运行效率降低
        """
        painter.end()
        # 更新QLabel中显示的内容,使其显示最新的QPixmap
        self.label.setPixmap(self.pixmap)
        # 显式刷新界面
        self.update()

    def clear_all(self) -> None:
        self.pixmap = QPixmap(800, 600)
        self.pixmap.fill(Qt.GlobalColor.white)
        self.label.setPixmap(self.pixmap)

    def color_changed(self) -> None:
        self.current_color = self.colors_map[self.sender().text()]


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

运行效果

喷雾效果
喷雾效果

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • QPainter的作用
  • QPainter 的基础使用范式
    • 绘制线条的示例代码
    • 运行效果
  • QPainter常用的基础图形
    • 绘制坐标点
      • 示例代码
      • 运行效果
    • 绘制矩形
      • 示例代码
      • 运行效果
    • 绘制椭圆形
      • 示例代码
      • 运行效果
    • 绘制文本
      • 示例代码
      • 运行效果
  • QPainter 的高级用法
    • 基于鼠标坐标绘制线条
      • 示例代码
      • 运行效果
    • 更改画笔颜色
      • 示例代码
      • 界面静态效果
      • 动态效果
    • 喷雾效果
      • 示例代码
      • 运行效果
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档