I want to build a PyQt5 application with Python that draws lines and arcs using known (already calculated) point coordinates, ie, lines with two end point and arcs with two end point and a center point. The point coordinates will be calculated from known geometric parameters such as length, angle, and arc radius. I would like to add horizontal sliders to control the geometric parameters and obtain an interactive 2D graphics application similar to the one in the following image. What is the fastest and most efficient way to achieve this with Pyt5 and Python? What 2D drawing libraries would be most suitable?
I solved this issue with the help from musicamante. I used QT's QPainter class. The drawLine() method is simple to use as it requires only the end point coordinates. The drawArc() method uses start angle and span angle which required additional method to obtain them. Following is the working code.
# 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_())
Output:
QPainterPath is generally a more appropriate choice for complex and connected drawing paths: not only it provides a unique object representing the path, but it also provides proper painting using dash patterns which wouldn't be (easily) possible with multiple segments.
Qt already provides basic functions to achieve what you want. Specifically, since the center is actually the center of the circle, you already know everything is needed to draw the arc:
p5
point;p5
and p3
(or p4
);p5
and p3
and p5
and p4
;Based on your answer, also note that:
drawLine(x1, y1, x2, y2)
, drawArc(x, y, w, h, angle, span)
, etc.) only accept integer values, to achieve precise painting you need to use the relative Qt objects: QPointF, QLineF, QRectF; always check the documentation to see the accepted arguments types (and don't rely too much in automatic python conversions); 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)
Note that for arbitrary connections you might need to check the direction of the lines to and from the arc in order to use the proper span angle (which might be negative for counter-clock directions).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.