如何通过 using\combining 多种形状在 Qt 中绘制自定义形状

How to draw custom shape in Qt by using\combining many shapes

如何使用(组合)其他基本形状(圆和线)绘制自定义形状?

我的问题是,如果我想旋转那个自定义形状,我需要做一些数学运算并旋转所有其他基本形状并重新定位。如何避免这种情况,在一个参数中我可以旋转整个自定义形状。

我想要这样的东西(例如):

orientation_angale = 40 # i.e
custom_shape.rotate(orientation_angale)

绘制上图的代码如下:

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QPoint, QPointF

class CustomShape(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.pixmap = QtGui.QPixmap()

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.drawPixmap(self.rect(), self.pixmap)
        pen = QtGui.QPen()
        pen.setWidth(1)
        painter.setPen(pen)
        # =========== Draw big circle ===========
        x, y = 100, 100
        r = 150
        painter.drawEllipse(x, y, r, r)

        # =========== Draw top line ===========
        s_point_x = x + r / 2  # centre of the big circle
        s_point_y = y
        s_point = QPointF(s_point_x, s_point_y)

        e_point_x = x + r / 2  # centre of the big circle
        e_point_y = y - 20
        e_point = QPointF(e_point_x, e_point_y)

        painter.drawLine(s_point, e_point)

        # =========== Draw small circle ===========
        s_r = 10
        s_x, s_y = x + (r / 2) - (s_r / 2), e_point_y - s_r
        painter.drawEllipse(s_x, s_y, s_r, s_r)


if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = CustomShape()
    w.resize(400, 400)
    w.show()
    sys.exit(app.exec_())

这是默认方向:

我想旋转那个自定义形状,所以它应该看起来像这样(旋转将应用 360 度)

您可以使用painter.rotate()函数,但请记住旋转始终围绕原点(0, 0),因此您首先必须translate到旋转中心,然后旋转,平移回来。

考虑一下您可以使用 QPainterPath 在单个元素中“存储”高级形状(另一种方法是使用 QPicture,它允许“缓存”QPainter 操作)。

class CustomShape(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.pixmap = QtGui.QPixmap()
        self.setMinimumSize(210, 210)

        self.path = QtGui.QPainterPath()
        self.path.addEllipse(100, 100, 150, 150)
        self.path.moveTo(175, 100)
        self.path.lineTo(175, 80)
        self.path.addEllipse(170, 70, 10, 10)
        self.center = QtCore.QPoint(175, 175)
        self.angle = 0

    def setAngle(self, angle):
        self.angle = angle % 360
        self.update()

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.drawPixmap(self.rect(), self.pixmap)
        painter.setRenderHint(painter.Antialiasing)
        painter.translate(self.center)
        painter.rotate(self.angle)
        painter.translate(-self.center)
        painter.drawPath(self.path)

if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = CustomShape()
    w.resize(400, 400)
    timer = QtCore.QTimer(interval=100)
    timer.timeout.connect(lambda: w.setAngle(w.angle + 1))
    timer.start()
    w.show()
    sys.exit(app.exec_())

请注意,Qt 提供了可用于计算简单几何图形和位置的辅助函数;虽然并不总是适合频繁更新,但由于它依赖于 C++ 函数,因此它 通常 比计算 python 方面的所有内容更快,或者至少,它提供了更好的可读形式:

class CustomShape(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.pixmap = QtGui.QPixmap()
        self.setMinimumSize(210, 210)

        self.center = QtCore.QPoint(175, 175)
        self.bigRadius = 75
        self.smallRadius = 5
        self.distance = 20
        self.angle = 0

    def setAngle(self, angle):
        self.angle = angle % 360
        self.update()

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.drawPixmap(self.rect(), self.pixmap)
        painter.setRenderHint(painter.Antialiasing)
        painter.drawEllipse(self.center, self.bigRadius, self.bigRadius)

        fullExtent = self.bigRadius + self.distance
        # create a line that extents to the edge of the small circle
        line = QtCore.QLineF.fromPolar(fullExtent, 90 - self.angle)
        # translate it to the center
        line.translate(self.center)
        # move the origin point based on the ratio of the radius
        line.setP1(line.pointAt(self.bigRadius / fullExtent))
        painter.drawLine(line)

        # create a line similar to the above to get the center of the 
        # small circle
        otherLine = QtCore.QLineF.fromPolar(
            fullExtent + self.smallRadius, 90 - self.angle)
        otherLine.translate(self.center)
        painter.drawEllipse(otherLine.p2(), self.smallRadius, self.smallRadius)