如何在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
相关产品推荐
相关产品推荐

