Qt5 绘制图形

为了方便开发,在 GitHub 上放置 xinetzone / xinet 用于 GUI 开发。该仓库的 xinet 文件夹下放置了一些 Qt for Python 的 API。由于运行 GUI 的主循环是类似的,故而,可以写一个统一接口(xinet/run_qt.py):

from Qt.qt5 import QtWidgets


def run(window_type, *args, **kwargs):
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = window_type(*args, **kwargs)
    window.show()
    app.exec_()

1 QPainter

QPaintEvent 事件包含一个需要更新的 region() 和一个 rect(),均返回该矩形区域的边界矩形实例。因为许多小部件不能充分利用 region(),而 rect() 可以比 region().boundingRect() 快得多,所以推荐使用 rect()

在绘画事件的处理过程中,painting 被剪切到 region()。这种裁剪是由 Qt 的绘画系统执行的,并且与应用于绘画设备上的 QPainter 的任何 clipping 无关。

1.1 文本涂鸦

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets
from xinet.run_qt import run



class Drawing(QtWidgets.QWidget):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.setWindowTitle("在窗体中绘画出文字例子")
        self.resize(300, 200)
        self.text = '欢迎学习 PyQt5'

    def paintEvent(self, event):
        painter = QtGui.QPainter()
        painter.begin(self)
        # 自定义的绘画方法
        ## 设置笔的颜色
        painter.setPen(QtGui.QColor('purple'))
        ## 设置字体
        painter.setFont(QtGui.QFont('SimSun', 20))
        rect = event.rect() # event.region().boundingRect()
        print(rect.getRect() == (0, 0, 300, 200))
        # 画出在 widget 中央画出文本
        painter.drawText(rect, QtCore.Qt.AlignCenter, self.text)
        painter.end()


if __name__ == "__main__":
    run(Drawing)

效果:

也可以这样:

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class DrawText(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        self.pen = QtGui.QPen()

    def initUI(self):
        self.setGeometry(500, 500, 280, 270)

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw_something()
        self.painter.end()

    def draw_something(self):
        from random import randint
        self.pen.setWidth(1)
        self.pen.setColor(QtGui.QColor('green'))
        self.painter.setPen(self.pen)
        font = QtGui.QFont()
        font.setFamily('Times')
        font.setBold(True)
        font.setPointSize(30)
        self.painter.setFont(font)
        self.painter.drawText(50, 100, 'Hello, world!')


if __name__ == "__main__":
    run(DrawText)

效果:

1.2 点的绘画

import math

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets
from xinet.run_qt import run


class DrawingPoint(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.resize(300, 200)
        self.setWindowTitle("在窗体中画点")

    def paintEvent(self, event):
        qp = QtGui.QPainter()
        qp.begin(self)
        # 自定义画点方法
        self.draw_points(qp)
        qp.end()

    def draw_points(self, qp):
        qp.setPen(QtCore.Qt.red)
        size = self.size()  # 得到窗口的当前大小
        for i in range(1000):
            # [-100, 100] 两个周期的正弦函数图像
            x = 100 * (-1+2.0*i/1000) + size.width()/2.0
            y = -50 * math.sin((x - size.width()/2.0) *
                               math.pi/50) + size.height()/2.0
            qp.drawPoint(x, y)


if __name__ == "__main__":
    run(DrawingPoint)

效果:

1.3 在 QLabel 上画图

前面都是在 QWidget 上绘制图形的,也可以在 QLabel 上绘制:

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets
from xinet.run_qt import run


class Label(QtWidgets.QLabel):
    def __init__(self):
        super().__init__()
        canvas = QtGui.QPixmap(400, 300)
        self.setPixmap(canvas)
        self.draw_something()

    def draw_something(self):
        painter = QtGui.QPainter(self.pixmap())
        painter.setPen(QtCore.Qt.red)
        painter.drawLine(10, 10, 300, 200)
        painter.end()


if __name__ == "__main__":
    run(Label)

效果:

Method Description
drawLine(line) Draw a QLine instance
drawLine(line) Draw a QLineF instance
drawLine(x1, y1, x2, y2) Draw a line between x1, y2 and x2, y2 (int)
drawLine(p1, p2) Draw a line between p1 and p2 (both QPoint)
drawLine(p1, p2) Draw a line between p1 and p2 (both QPointF)

2 QPen

QPen(钢笔)是一个基本的图形对象,用于绘制直线、曲线或者给轮廓画出矩形、椭圆形、多边形及其他形状等。

2.1 画不同样式的线段

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets
from xinet.run_qt import run


class DrawLine(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        # 为了能更清晰地看清各线之间的差异,将颜色设置成黑色,宽度设置为2像素(px)
        ##  QtCore.Qt.SolidLine是预定义的线条样式之一
        self.pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)

    def initUI(self):
        self.setGeometry(500, 500, 280, 270)
        self.setWindowTitle('钢笔样式例子')

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw()
        self.painter.end()

    def change_style(self, style):
        self.pen.setStyle(style)
        self.painter.setPen(self.pen)

    def draw(self):
        self.painter.setPen(self.pen)
        self.painter.drawLine(20, 40, 250, 40)

        self.change_style(QtCore.Qt.DashLine)
        self.painter.drawLine(20, 80, 250, 80)

        self.change_style(QtCore.Qt.DashDotLine)
        self.painter.drawLine(20, 120, 250, 120)

        self.change_style(QtCore.Qt.DotLine)
        self.painter.drawLine(20, 160, 250, 160)

        self.change_style(QtCore.Qt.DashDotDotLine)
        self.painter.drawLine(20, 200, 250, 200)

        self.pen.setStyle(QtCore.Qt.CustomDashLine)  # 创建线条样式
        self.pen.setDashPattern([1, 4, 5, 4])
        self.painter.setPen(self.pen)
        self.painter.drawLine(20, 240, 250, 240)


if __name__ == "__main__":
    run(DrawLine)

效果:

2.2 画不同大小的点

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets
from xinet.run_qt import run


class DrawPoints(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        # 将颜色设置成红色
        self.pen = QtGui.QPen(QtGui.QColor('red'))

    def initUI(self):
        self.setGeometry(500, 500, 280, 270)
        self.setWindowTitle('钢笔样式例子')

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw()
        self.painter.end()

    def draw_point(self, x, y):
        self.painter.setPen(self.pen)
        self.painter.drawPoint(x, y)

    def draw(self):
        self.pen.setWidth(5)
        self.draw_point(40, 40)

        self.pen.setWidth(10)
        self.draw_point(40, 80)

        self.pen.setWidth(20)
        self.draw_point(40, 120)

        self.pen.setWidth(30)
        self.draw_point(40, 160)

        self.pen.setWidth(40)
        self.draw_point(40, 200)


if __name__ == "__main__":
    run(DrawPoints)

效果:

2.3 画一条带宽度的黄色线段

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class DrawYellowLine(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()

    def initUI(self):
        self.setGeometry(500, 500, 280, 270)

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw()
        self.painter.end()

    def draw(self):
        pen = QtGui.QPen(QtGui.QColor('yellow'), 20)
        self.painter.setPen(pen)
        self.painter.drawLine(QtCore.QPoint(20, 20), QtCore.QPoint(200, 200))


if __name__ == "__main__":
    run(DrawYellowLine)

效果:

2.4 画矩形

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class DrawRect(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        self.pen = QtGui.QPen()

    def initUI(self):
        self.setGeometry(500, 500, 280, 270)

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw_something()
        self.painter.end()

    def draw_something(self):
        self.pen.setWidth(3)
        self.pen.setColor(QtGui.QColor("#FB51E0"))
        self.painter.setPen(self.pen)
        self.painter.drawRect(50, 50, 100, 100)
        self.painter.drawRect(60, 60, 150, 100)
        self.painter.drawRect(70, 70, 100, 150)
        self.painter.drawRect(80, 80, 150, 100)
        self.painter.drawRect(90, 90, 100, 150)


if __name__ == "__main__":
    run(DrawRect)

效果:

其实,上面的画矩形的代码可简写为:

def draw_something(self):
        self.pen.setWidth(3)
        self.pen.setColor(QtGui.QColor("#FB51E0"))
        self.painter.setPen(self.pen)
        self.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)])

2.5 画圆角矩形

修改 DrawRect 代码为:

def draw_something(self):
    col = QtGui.QColor(0, 0, 0)
    col.setNamedColor('green')
    self.painter.setPen(col)
    self.painter.drawRoundedRect(40, 40, 100, 100, 10, 10)
    self.painter.drawRoundedRect(80, 80, 100, 100, 10, 50)
    self.painter.drawRoundedRect(120, 120, 100, 100, 50, 10)
    self.painter.drawRoundedRect(160, 160, 100, 100, 50, 50)

效果:

2.6 画椭圆

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class DrawEllipse(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        self.pen = QtGui.QPen()

    def initUI(self):
        self.setGeometry(500, 500, 280, 270)

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw_something()
        self.painter.end()

    def draw_something(self):
        from random import randint
        self.pen.setWidth(3)
        self.pen.setColor(QtGui.QColor(204, 0, 0))  # r, g, b
        self.painter.setPen(self.pen)

        self.painter.drawEllipse(10, 10, 100, 100)
        self.painter.drawEllipse(10, 10, 150, 200)
        self.painter.drawEllipse(10, 10, 200, 300)


if __name__ == "__main__":
    run(DrawEllipse)

效果:

也可以使用如下方式进行绘制:

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

效果为:

3 QBrush

QBrush 也是图像的一个基本元素。是用来填充一些物体的背景图用的,比如矩形,椭圆,多边形等。有三种类型:预定义、渐变和纹理。

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class Drawing(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        self.pen = QtGui.QPen()

    def initUI(self):
        self.setGeometry(500, 500, 380, 270)

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw_something()
        self.painter.end()

    def draw_rect(self, brush, bbox):
        self.painter.setBrush(brush)
        self.painter.drawRect(*bbox)

    def draw_something(self):
        brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
        bbox = 10, 15, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.Dense1Pattern)
        bbox = 130, 15, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.Dense2Pattern)
        bbox = 250, 15, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.Dense3Pattern)
        bbox = 10, 105, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.DiagCrossPattern)
        bbox = 10, 15, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.Dense5Pattern)
        bbox = 130, 105, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.Dense6Pattern)
        bbox = 250, 105, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.HorPattern)
        bbox = 10, 195, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.VerPattern)
        bbox = 130, 195, 90, 60
        self.draw_rect(brush, bbox)

        brush = QtGui.QBrush(QtCore.Qt.BDiagPattern)
        bbox = 250, 195, 90, 60
        self.draw_rect(brush, bbox)


if __name__ == "__main__":
    run(Drawing)

效果:

4 颜色

颜色是一个物体显示的 RGB 的混合色。RBG 值的范围是 0~255。我们有很多方式去定义一个颜色,最常见的方式就是 RGB 和 16 进制表示法,也可以使用 RGBA,增加了一个透明度的选项,透明度值的范围是0~255,0 代表完全透明。

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class Drawing(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        self.pen = QtGui.QPen()

    def initUI(self):
        self.setGeometry(500, 500, 380, 100)

    def paintEvent(self, e):
        self.painter.begin(self)
        self.draw_something()
        self.painter.end()

    def draw_something(self):
        col = QtGui.QColor(0, 0, 0)
        col.setNamedColor('#d4d4d4')
        self.painter.setPen(col)

        self.painter.setBrush(QtGui.QColor(255, 0, 0, 110))
        self.painter.drawRect(10, 15, 90, 60)

        self.painter.setBrush(QtGui.QColor(255, 80, 0))
        self.painter.drawRect(130, 15, 90, 60)

        self.painter.setBrush(QtGui.QColor(25, 0, 90, 200))
        self.painter.drawRect(250, 15, 90, 60)


if __name__ == "__main__":
    run(Drawing)

效果:

5 贝塞尔曲线

贝塞尔曲线是立方线。PyQt5 中的贝塞尔曲线可以使用 QPainterPath 创建。绘制器路径是由许多图形构建基块(如矩形、椭圆、线条和曲线)组成的对象。

from xinet.Qt.qt5 import QtCore, QtGui, QtWidgets  # , Signal, Slot
from xinet.run_qt import run


class DrawBezierCurve(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.painter = QtGui.QPainter()
        self.pen = QtGui.QPen()

    def initUI(self):
        self.setGeometry(500, 500, 380, 280)

    def paintEvent(self, e):
        self.painter.begin(self)
        # 反锯齿,边缘柔化、消除混叠、反走样
        self.painter.setRenderHint(QtGui.QPainter.Antialiasing)
        self.draw_something()
        self.painter.end()

    def draw_something(self):
        path = QtGui.QPainterPath()
        path.moveTo(30, 30)
        path.cubicTo(30, 30, 200, 350, 350, 30)

        self.painter.drawPath(path)


if __name__ == "__main__":
    run(DrawBezierCurve)

效果:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容