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

PyQt5中如何在QGraphicsPathItem鼠标悬停时显示文本?

实现QGraphicsPathItem悬停显示文本

问题说明

使用QGraphicsPathItem绘制连接线时,需要鼠标悬停在线条上时显示提示文本,目前hoverEnterEvent能正常触发,但不知道如何实现文本显示功能。

修改后的完整代码

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class NodeEditorWnd(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        self.setGeometry(200, 200, 800, 600)

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.layout)

        self.scene = MyScene()
        self.addNodes()

        self.view = MyView(self.scene)
        self.layout.addWidget(self.view)

        self.setWindowTitle("Node Editor")
        self.show()

    def addNodes(self):
        node1 = Node(self.scene, "My Awesome Node 1", inputs=[1,2,3], outputs=[1])
        node2 = Node(self.scene, "My Awesome Node 2", inputs=[1,2,3], outputs=[1])
        node1.setPos(-350, -250)
        node2.setPos(-75, 0)

        edge1 = Edge(self.scene, 100, 500, "这是连接线的提示文本")
        self.scene.addItem(edge1)
        
class NodeContent(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        self.wdg_label = QLabel("Some Title")
        self.layout.addWidget(self.wdg_label)
        self.layout.addWidget(QTextEdit("foo"))

class Node(QGraphicsItem):
    def __init__(self, scene_ref, title="MyNode", inputs=[], outputs=[], parent=None):
        super().__init__(parent)
        self.scene_ref = scene_ref

        self._title_color = Qt.white
        self._title_font = QFont("Ubuntu", 10)

        self.width = 180
        self.height = 240
        self.edge_size = 10.0
        self.title_height = 24.0
        self._padding = 4.0
        self._pen_default = QPen(QColor("#7F000000"))
        self._pen_selected = QPen(QColor("#FFFFA637"))

        self._brush_title = QBrush(QColor("#FF313131"))
        self._brush_background = QBrush(QColor("#E3212121"))

        self.content = NodeContent()

        self.initTitle()
        self.title = title
        self.initContent()
        self.initUI()
        self.scene_ref.addNode(self)
        self.scene_ref.addItem(self)

    def boundingRect(self):
        return QRectF(
            0,
            0,
            2 * self.edge_size + self.width,
            2 * self.edge_size + self.height
        ).normalized()

    def initUI(self):
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)

    def initTitle(self):
        self.title_item = QGraphicsTextItem(self)
        self.title_item.setDefaultTextColor(self._title_color)
        self.title_item.setFont(self._title_font)
        self.title_item.setPos(self._padding, 0)
        self.title_item.setTextWidth(
            self.width
            - 2 * self._padding
        )

    @property
    def title(self): return self._title
    @title.setter
    def title(self, value):
        self._title = value
        self.title_item.setPlainText(self._title)

    def paint(self, painter, QStyleOptionGraphicsItem, widget=None):
        # 绘制标题栏
        path_title = QPainterPath()
        path_title.setFillRule(Qt.WindingFill)
        path_title.addRoundedRect(0,0, self.width, self.title_height, self.edge_size, self.edge_size)
        path_title.addRect(0, self.title_height - self.edge_size, self.edge_size, self.edge_size)
        path_title.addRect(self.width - self.edge_size, self.title_height - self.edge_size, self.edge_size, self.edge_size)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_title)
        painter.drawPath(path_title.simplified())

        # 绘制内容区域
        path_content = QPainterPath()
        path_content.setFillRule(Qt.WindingFill)
        path_content.addRoundedRect(0, self.title_height, self.width, self.height - self.title_height, self.edge_size, self.edge_size)
        path_content.addRect(0, self.title_height, self.edge_size, self.edge_size)
        path_content.addRect(self.width - self.edge_size, self.title_height, self.edge_size, self.edge_size)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_background)
        painter.drawPath(path_content.simplified())

    def initContent(self):
        self.grContent = QGraphicsProxyWidget(self)
        self.content.setGeometry(
            int(self.edge_size), 
            int(self.title_height + self.edge_size), 
            int(self.width - 2*self.edge_size), 
            int(self.height - 2*self.edge_size - self.title_height)
        )
        self.grContent.setWidget(self.content)

class Edge(QGraphicsPathItem):
    def __init__(self, scene_ref, sp, ep, tooltip_text="", parent=None):
        super().__init__(parent)
        self._color = QColor("#001000")
        self._color_selected = QColor("#00ff00")
        self._color_hover = QColor("red")
        self._pen = QPen(self._color)
        self._pen_selected = QPen(self._color_selected)
        self._pen_hover = QPen(self._color_hover)
        self._pen.setWidthF(2.0)
        self._pen_selected.setWidthF(2.0)
        self._pen_hover.setWidthF(2.0)
        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemIsSelectable)

        self.setZValue(-1)

        self.posSource = [-170, -190]
        self.posDestination = [100, 100]

        # 初始化悬停提示文本
        self.tooltip = QGraphicsTextItem(self)
        self.tooltip.setPlainText(tooltip_text)
        self.tooltip.setFont(QFont("Ubuntu", 10))
        self.tooltip.setDefaultTextColor(Qt.white)
        # 默认隐藏提示
        self.tooltip.hide()
        # 设置提示的背景(可选)
        self.tooltip_background = QGraphicsRectItem(self)
        self.tooltip_background.setBrush(QBrush(QColor("#80000000")))
        self.tooltip_background.setPen(Qt.NoPen)
        self.tooltip_background.hide()

    def paint(self, painter, QStyleOptionGraphicsItem, widget=None):
        self.updatePath()
        # 根据状态选择画笔
        if self.isSelected():
            painter.setPen(self._pen_selected)
        elif self.isUnderMouse():
            painter.setPen(self._pen_hover)
        else:
            painter.setPen(self._pen)
        painter.setBrush(Qt.NoBrush)
        painter.drawPath(self.path())

    def updatePath(self):
        path = QPainterPath(QPointF(self.posSource[0], self.posSource[1]))
        path.lineTo(self.posDestination[0], self.posDestination[1])
        self.setPath(path)

    def hoverEnterEvent(self, event):
        # 计算线条中点位置,用于放置提示文本
        mid_x = (self.posSource[0] + self.posDestination[0]) / 2
        mid_y = (self.posSource[1] + self.posDestination[1]) / 2
        # 调整提示文本位置,避免覆盖线条
        self.tooltip.setPos(mid_x - self.tooltip.boundingRect().width()/2, mid_y - 20)
        # 更新背景矩形大小
        bg_rect = self.tooltip.boundingRect().adjusted(-5, -3, 5, 3)
        self.tooltip_background.setRect(bg_rect)
        self.tooltip_background.setPos(self.tooltip.pos())
        # 显示提示和背景
        self.tooltip.show()
        self.tooltip_background.show()
        super().hoverEnterEvent(event)

    def hoverLeaveEvent(self, event):
        # 隐藏提示
        self.tooltip.hide()
        self.tooltip_background.hide()
        super().hoverLeaveEvent(event)

class MyScene(QGraphicsScene):
    def __init__(self, parent = None):
        super().__init__(parent)
        self.nodes = []
        self.edges = []
        self.scene_width = 64000
        self.scene_height = 64000
        self._color_background = QColor("#0af5d2")
        self.setBackgroundBrush(self._color_background)
        
    def addNode(self, node):
        self.nodes.append(node)

class MyView(QGraphicsView):
    def __init__(self, scene_ref, parent=None):
        super().__init__(parent)
        self.scene_ref = scene_ref
        self.setScene(self.scene_ref)
        # 开启抗锯齿
        self.setRenderHint(QPainter.Antialiasing)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    wnd = NodeEditorWnd()
    sys.exit(app.exec_())

关键修改说明

  1. Edge类初始化添加提示组件:
    • 创建QGraphicsTextItem作为提示文本容器,默认隐藏
    • 可选添加半透明背景矩形,提升文本可读性
  2. hover事件处理:
    • hoverEnterEvent:计算线条中点位置,调整提示文本的显示位置,显示提示和背景
    • hoverLeaveEvent:隐藏提示文本和背景
  3. 画笔状态优化:
    • 区分正常、选中、悬停三种状态的画笔颜色,让交互反馈更清晰
  4. 视图添加抗锯齿:提升图形显示效果

内容的提问来源于stack exchange,提问作者A.G.Progm.Enthusiast

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:59:56