Python PyQt5二维图形应用:如何画两个端点坐标的线和两个端点和一个圆心坐标的圆弧
Python PyQt5 2D graphics application: How to draw lines with two end point coordinates and arcs with two end and a center point coordinates
我想使用 Python 构建一个 PyQt5 应用程序,该应用程序使用已知(已计算)点坐标绘制直线和圆弧,即具有两个端点的线和具有两个端点和一个中心点的圆弧。点坐标将从已知的几何参数(例如长度、角度和圆弧半径)计算得出。我想添加水平滑块来控制几何参数并获得类似于下图中的交互式 2D 图形应用程序。使用 Pyt5 和 Python 实现此目的最快最有效的方法是什么?哪些二维绘图库最合适?
我在 musicamante 的帮助下解决了这个问题。我用的是QT的QPainterclass。 drawLine() 方法使用简单,因为它只需要端点坐标。 drawArc() 方法使用起始角度和跨度角度,这需要额外的方法来获取它们。以下是工作代码。
# Draw lines and arcs.
import sys
import math
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QPen, QColor, QBrush
from PyQt5.QtCore import Qt
def three_points_angle(p1x, p1y, p2x, p2y, c1x, c1y):
numerator = p1y*(c1x-p2x) + c1y*(p2x-p1x) + p2y*(p1x-c1x)
denominator = (p1x-c1x)*(c1x-p2x) + (p1y-c1y)*(c1y-p2y)
ratio = numerator/denominator
angleRad = math.atan(ratio)
angleDeg = (angleRad*180)/math.pi
if angleDeg < 0:
angleDeg = 180 + angleDeg
return angleDeg
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setFixedSize(500, 200)
self.setWindowTitle('Draw Lines and Arcs')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.draw_arc(qp)
qp.end()
def draw_arc(self, qp):
qp.setPen(QPen(Qt.blue, 3))
r1 = 3
p0y = 0.000000
p0x = 56.000000
p1y = 7.000000
p1x = 56.000000
p2y = 7.000000
p2x = 55.500000
p3y = 3.410242
p3x = 53.256870
p4y = 2.001828
p4x = 50.608028
p5y = 5.000000
p5x = 50.712726
p6y = 3.349775
p6x = 12.007856
sf = 490/p1x
qp.drawLine(round(p0x*sf), round(p0y*sf), round(p1x*sf), round(p1y*sf))
qp.drawLine(round(p1x*sf), round(p1y*sf), round(p2x*sf), round(p2y*sf))
qp.drawLine(round(p2x*sf), round(p2y*sf), round(p3x*sf), round(p3y*sf))
a1_start = three_point_angle(p5x+1, p5y, p3x, p3y, p5x, p5y)
print("start angle: %f" %a1_start)
a1_span = three_point_angle(p3x, p3y, p4x, p4y, p5x, p5y)
print("span angle: %f" %a1_span)
qp.drawArc(round((p5x-r1)*sf), round((p5y-r1)*sf), round(2*r1*sf), round(2*r1*sf), round(a1_start*16), round(a1_span*16))
qp.drawLine(round(p4x*sf), round(p4y*sf), round(p6x*sf), round(p6y*sf))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
输出:
QPainterPath 通常是更适合复杂且相互连接的绘图路径的选择:它不仅提供了表示路径的唯一对象,而且还提供了使用破折号图案的正确绘画,这不会(很容易) ) 可能有多个段。
Qt已经提供了基本的功能来实现你想要的。具体来说,由于 center 实际上是圆的中心,您已经知道绘制圆弧所需的一切:
- 椭圆(圆)的长方形(正方形)为
p5
点;
- 它的大小是
p5
和p3
(或p4
)之间长度的两倍;
- 跨度是
p5
和 p3
以及 p5
和 p4
之间的线的角度;
根据您的回答,还请注意:
- 所有接受坐标位置参数(
drawLine(x1, y1, x2, y2)
、drawArc(x, y, w, h, angle, span)
等)的基本 QPainter 函数只接受整数值,要实现精确绘画,您需要使用相关的 Qt 对象:QPointF , QLineF, QRectF;始终检查文档以查看可接受的参数类型(并且不要过分依赖自动 python 转换);
- 通常最好使用 QPointF 对象(也可以乘以因子),因为它们提供单个对象引用;
- 平滑的线条和曲线应该使用抗锯齿;
- 高级绘画与 Graphics View Framework 配合使用时效果更好,它还提供更容易的转换(尤其是缩放和旋转);
def draw_arc(self, qp):
# ...
path = QPainterPath(QPointF(p0x, p0y) * sf)
path.lineTo(QPointF(p1x, p1y) * sf)
path.lineTo(QPointF(p2x, p2y) * sf)
start = QPointF(p3x, p3y) * sf
end = QPointF(p4x, p4y) * sf
center = QPointF(p5x, p5y) * sf
# create reference lines to the center of the circle
startLine = QLineF(center, start)
endLine = QLineF(center, end)
radius = startLine.length()
arcRect = QRectF(center.x() - radius, center.y() - radius,
radius * 2, radius * 2)
# no need to lineTo(start), as arcTo() already connects the previous
# point to the start angle of the arc
path.arcTo(arcRect, startLine.angle(), endLine.angle() - startLine.angle())
path.lineTo(QPointF(p6x, p6y) * sf)
qp.setRenderHints(qp.Antialiasing)
qp.drawPath(path)
请注意,对于任意连接,您可能需要检查进出圆弧的线的方向,以便使用正确的跨度角(counter-clock 方向可能为负)。
我想使用 Python 构建一个 PyQt5 应用程序,该应用程序使用已知(已计算)点坐标绘制直线和圆弧,即具有两个端点的线和具有两个端点和一个中心点的圆弧。点坐标将从已知的几何参数(例如长度、角度和圆弧半径)计算得出。我想添加水平滑块来控制几何参数并获得类似于下图中的交互式 2D 图形应用程序。使用 Pyt5 和 Python 实现此目的最快最有效的方法是什么?哪些二维绘图库最合适?
我在 musicamante 的帮助下解决了这个问题。我用的是QT的QPainterclass。 drawLine() 方法使用简单,因为它只需要端点坐标。 drawArc() 方法使用起始角度和跨度角度,这需要额外的方法来获取它们。以下是工作代码。
# Draw lines and arcs.
import sys
import math
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QPen, QColor, QBrush
from PyQt5.QtCore import Qt
def three_points_angle(p1x, p1y, p2x, p2y, c1x, c1y):
numerator = p1y*(c1x-p2x) + c1y*(p2x-p1x) + p2y*(p1x-c1x)
denominator = (p1x-c1x)*(c1x-p2x) + (p1y-c1y)*(c1y-p2y)
ratio = numerator/denominator
angleRad = math.atan(ratio)
angleDeg = (angleRad*180)/math.pi
if angleDeg < 0:
angleDeg = 180 + angleDeg
return angleDeg
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setFixedSize(500, 200)
self.setWindowTitle('Draw Lines and Arcs')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.draw_arc(qp)
qp.end()
def draw_arc(self, qp):
qp.setPen(QPen(Qt.blue, 3))
r1 = 3
p0y = 0.000000
p0x = 56.000000
p1y = 7.000000
p1x = 56.000000
p2y = 7.000000
p2x = 55.500000
p3y = 3.410242
p3x = 53.256870
p4y = 2.001828
p4x = 50.608028
p5y = 5.000000
p5x = 50.712726
p6y = 3.349775
p6x = 12.007856
sf = 490/p1x
qp.drawLine(round(p0x*sf), round(p0y*sf), round(p1x*sf), round(p1y*sf))
qp.drawLine(round(p1x*sf), round(p1y*sf), round(p2x*sf), round(p2y*sf))
qp.drawLine(round(p2x*sf), round(p2y*sf), round(p3x*sf), round(p3y*sf))
a1_start = three_point_angle(p5x+1, p5y, p3x, p3y, p5x, p5y)
print("start angle: %f" %a1_start)
a1_span = three_point_angle(p3x, p3y, p4x, p4y, p5x, p5y)
print("span angle: %f" %a1_span)
qp.drawArc(round((p5x-r1)*sf), round((p5y-r1)*sf), round(2*r1*sf), round(2*r1*sf), round(a1_start*16), round(a1_span*16))
qp.drawLine(round(p4x*sf), round(p4y*sf), round(p6x*sf), round(p6y*sf))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
输出:
QPainterPath 通常是更适合复杂且相互连接的绘图路径的选择:它不仅提供了表示路径的唯一对象,而且还提供了使用破折号图案的正确绘画,这不会(很容易) ) 可能有多个段。
Qt已经提供了基本的功能来实现你想要的。具体来说,由于 center 实际上是圆的中心,您已经知道绘制圆弧所需的一切:
- 椭圆(圆)的长方形(正方形)为
p5
点; - 它的大小是
p5
和p3
(或p4
)之间长度的两倍; - 跨度是
p5
和p3
以及p5
和p4
之间的线的角度;
根据您的回答,还请注意:
- 所有接受坐标位置参数(
drawLine(x1, y1, x2, y2)
、drawArc(x, y, w, h, angle, span)
等)的基本 QPainter 函数只接受整数值,要实现精确绘画,您需要使用相关的 Qt 对象:QPointF , QLineF, QRectF;始终检查文档以查看可接受的参数类型(并且不要过分依赖自动 python 转换); - 通常最好使用 QPointF 对象(也可以乘以因子),因为它们提供单个对象引用;
- 平滑的线条和曲线应该使用抗锯齿;
- 高级绘画与 Graphics View Framework 配合使用时效果更好,它还提供更容易的转换(尤其是缩放和旋转);
def draw_arc(self, qp):
# ...
path = QPainterPath(QPointF(p0x, p0y) * sf)
path.lineTo(QPointF(p1x, p1y) * sf)
path.lineTo(QPointF(p2x, p2y) * sf)
start = QPointF(p3x, p3y) * sf
end = QPointF(p4x, p4y) * sf
center = QPointF(p5x, p5y) * sf
# create reference lines to the center of the circle
startLine = QLineF(center, start)
endLine = QLineF(center, end)
radius = startLine.length()
arcRect = QRectF(center.x() - radius, center.y() - radius,
radius * 2, radius * 2)
# no need to lineTo(start), as arcTo() already connects the previous
# point to the start angle of the arc
path.arcTo(arcRect, startLine.angle(), endLine.angle() - startLine.angle())
path.lineTo(QPointF(p6x, p6y) * sf)
qp.setRenderHints(qp.Antialiasing)
qp.drawPath(path)
请注意,对于任意连接,您可能需要检查进出圆弧的线的方向,以便使用正确的跨度角(counter-clock 方向可能为负)。