You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQt/PySide中为两个QPushButton控件绘制箭头?

在Qt按钮树形节点间添加箭头的实现

你可以通过重写父控件的paintEvent方法,使用QPainter绘制箭头连线,以下是修改后的完整代码:

from PyQt5.QtWidgets import (QScrollArea, QVBoxLayout, QHBoxLayout, 
                             QPushButton, QWidget)
from PyQt5.QtGui import QPainter, QPen, QPolygonF
from PyQt5.QtCore import Qt, QPointF

class ArrowWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.baselayout = QVBoxLayout(self)
        # 保存按钮引用,方便后续获取位置
        self.btn0 = QPushButton("Button 0")
        self.baselayout.addWidget(self.btn0)
        
        layout1 = QHBoxLayout()
        self.btn1 = QPushButton("Button 1")
        self.btn2 = QPushButton("Button 2")
        layout1.addWidget(self.btn1)
        layout1.addWidget(self.btn2)
        self.baselayout.addLayout(layout1)

    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)
        # 设置画笔样式
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)
        
        # 计算Button 0的底部中点
        btn0_rect = self.btn0.geometry()
        btn0_bottom_center = QPointF(btn0_rect.center().x(), btn0_rect.bottom())
        
        # 绘制Button 0到Button 1的箭头
        btn1_rect = self.btn1.geometry()
        btn1_top_center = QPointF(btn1_rect.center().x(), btn1_rect.top())
        # 绘制连线
        painter.drawLine(btn0_bottom_center, btn1_top_center)
        # 绘制箭头头部
        self.draw_arrow_head(painter, btn0_bottom_center, btn1_top_center)
        
        # 绘制Button 0到Button 2的箭头
        btn2_rect = self.btn2.geometry()
        btn2_top_center = QPointF(btn2_rect.center().x(), btn2_rect.top())
        painter.drawLine(btn0_bottom_center, btn2_top_center)
        self.draw_arrow_head(painter, btn0_bottom_center, btn2_top_center)

    def draw_arrow_head(self, painter, start_point, end_point):
        # 计算箭头方向向量
        dx = end_point.x() - start_point.x()
        dy = end_point.y() - start_point.y()
        length = (dx**2 + dy**2)**0.5
        if length == 0:
            return
        # 单位向量
        unit_dx = dx / length
        unit_dy = dy / length
        # 箭头长度和角度
        arrow_length = 10
        arrow_angle = 0.5  # 弧度,约28.6度
        # 计算箭头两个端点
        point1 = QPointF(
            end_point.x() - arrow_length * (unit_dx * 0.866 + unit_dy * 0.5),
            end_point.y() - arrow_length * (unit_dy * 0.866 - unit_dx * 0.5)
        )
        point2 = QPointF(
            end_point.x() - arrow_length * (unit_dx * 0.866 - unit_dy * 0.5),
            end_point.y() - arrow_length * (unit_dy * 0.866 + unit_dx * 0.5)
        )
        # 绘制箭头三角形
        painter.drawPolygon(QPolygonF([end_point, point1, point2]))

class MyWidget(QScrollArea):
    def __init__(self):
        super().__init__()  
        
        self.widget = ArrowWidget()
        self.setWidget(self.widget)
        # 确保控件大小变化时重绘箭头
        self.widget.layout().setContentsMargins(20,20,20,20)
        self.widget.updateGeometry()

关键步骤说明:

  • 自定义ArrowWidget继承自QWidget,保存所有按钮的引用,方便后续获取它们的几何位置。
  • 重写paintEvent方法,在其中使用QPainter绘制按钮之间的连线和箭头头部。
  • 通过geometry()方法获取按钮的位置和尺寸,计算连线的起点(按钮0底部中点)和终点(按钮1/2顶部中点)。
  • 实现draw_arrow_head方法,根据连线的方向计算箭头的两个端点,绘制三角形箭头。
  • 当控件大小变化时,Qt会自动触发paintEvent,确保箭头始终正确连接按钮。

如果你的树形结构更复杂(比如多层嵌套),可以考虑维护一个节点关系列表,遍历列表统一绘制连线,这样扩展性更好。

内容的提问来源于stack exchange,提问作者Diego Palacios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:25:39