如何绘制具有两个端点坐标的直线和具有两个端点和中心点坐标的圆弧

发布于 2025-01-10 05:22:14 字数 339 浏览 5 评论 0原文

我想用Python构建一个PyQt5应用程序,使用已知的(已经计算出的)点坐标绘制直线和圆弧,即具有两个端点的直线和具有两个端点和一个中心点的圆弧。点坐标将根据已知的几何参数(例如长度、角度和圆弧半径)计算得出。我想添加水平滑块来控制几何参数并获得类似于下图中的交互式 2D 图形应用程序。使用 Pyt5 和 Python 实现此目的最快、最有效的方法是什么?哪些 2D 绘图库最合适?

输入图片此处描述

I want to build a PyQt5 application with Python that draws lines and arcs using known (already calculated) point coordinates, i.e., 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?

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

生生漫 2025-01-17 05:22:14

QPainterPath 通常是复杂且连接的绘图路径的更合适选择:不仅它提供了一个代表路径的独特对象,但它还使用破折号图案提供了正确的绘画,而这对于多个线段来说是不可能(轻易)实现的。

Qt已经提供了基本的功能来实现你想要的。具体来说,由于中心实际上是圆的中心,因此您已经知道绘制圆弧所需的一切:

  • 椭圆(圆)的矩形(正方形)是p5点;
  • 它的大小是 p5p3(或 p4)之间长度的两倍;
  • 跨度是 p5p3 以及 p5p4 之间的线的角度;

根据您的回答,还请注意:

  • 所有接受坐标位置参数的 QPainter 函数 (drawLine(x1, y1, x2, y2)drawArc(x, y, w, h 、角度、跨度等)只接受整数值,要实现精确绘制需要使用相对的Qt对象:QPointF、QLineF、QRectF;总是检查文档以查看接受的参数类型(并且不要过多依赖自动 python 转换);
  • 通常最好使用 QPointF 对象(也可以乘以因子),因为它们提供单个对象引用;
  • 抗锯齿应用于平滑的直线和曲线;
  • 高级绘画与图形视图框架配合使用效果更好,它还可以更轻松地访问变换(特别是缩放和旋转);
    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)

请注意,对于任意连接,您可能需要检查往返弧线的方向,以便使用正确的跨度角(对于逆时针方向可能为负值)。

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:

  • the rectangle (square) of the ellipse (circle) is the p5 point;
  • its size is twice the length between p5 and p3 (or p4);
  • the spans are the angles of the lines between p5 and p3 and p5 and p4;

Based on your answer, also note that:

  • all basic QPainter functions that accept positional arguments for coordinates (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);
  • it is usually better to use QPointF objects (which can also be multiplied by factors) as they provide a single object reference;
  • antialiasing should be used for smooth lines and curves;
  • advanced painting works better with the Graphics View Framework, which also provide easier access to transformation (notably, scaling and rotation);
    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).

柏拉图鍀咏恒 2025-01-17 05:22:14

我在musicamante的帮助下解决了这个问题。我使用了QT的QPainter类。 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_points_angle(p5x+1, p5y, p3x, p3y, p5x, p5y)
        print("start angle: %f" %a1_start)
        a1_span = three_points_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_())

输出:

在此输入图像描述

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_points_angle(p5x+1, p5y, p3x, p3y, p5x, p5y)
        print("start angle: %f" %a1_start)
        a1_span = three_points_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:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文