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

Qt QGraphicsTextItem拖动后随视图缩放保持相对位置方案问询

解决方案:基于相对比例固定文本位置

要实现拖动文本后,视图缩放或调整大小时文本保持相对位置,核心思路是记录文本相对于视图视口的位置比例,每次视图尺寸变化时,根据该比例重新计算文本的绝对位置。具体步骤如下:

  • 拖动结束时,计算文本左上角(或你指定的锚点)相对于当前视图视口的横向/纵向比例
  • 监听视图的尺寸变化事件,每次变化时结合保存的比例和新视口尺寸重新计算文本绝对位置
  • 处理文本锚点偏移,确保计算的是你期望的相对基准点(比如文本左上角而非默认的基线位置)

以下是修改后的完整代码:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QGraphicsScene, QGraphicsView, QGraphicsTextItem
from PyQt5.QtCore import Qt, QPointF, QEvent


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        layout = QVBoxLayout()
        self.setLayout(layout)

        self.scene = QGraphicsScene()
        self.text_item = QGraphicsTextItem("Hello, World!")
        self.text_item.setTextInteractionFlags(Qt.TextEditorInteraction)
        self.text_item.setFlags(
            QGraphicsTextItem.ItemIsSelectable 
            | QGraphicsTextItem.ItemIsMovable 
            | QGraphicsTextItem.ItemIsFocusable
        )
        self.scene.addItem(self.text_item)

        # 保存文本相对于视图视口的位置比例(x:横向比例,y:纵向比例)
        self.text_rel_x = 0.9  # 初始偏向右侧
        self.text_rel_y = 0.9  # 初始偏向底部

        # 给文本项安装事件过滤器,捕捉拖动结束事件
        self.text_item.installEventFilter(self)

        self.view = QGraphicsView(self.scene)
        # 重写视图的resize事件处理函数
        self.view.resizeEvent = self.on_view_resize
        layout.addWidget(self.view)

        # 初始化文本位置
        self.adjust_text_pos()

    def eventFilter(self, obj, event):
        # 捕捉文本项的左键释放事件(拖动结束)
        if (obj == self.text_item 
            and event.type() == QEvent.MouseButtonRelease 
            and event.button() == Qt.LeftButton):
            self.update_text_rel_pos()
        return super().eventFilter(obj, event)

    def update_text_rel_pos(self):
        # 获取当前视图视口对应的场景区域
        view_rect = self.view.viewport().rect()
        scene_rect = self.view.mapToScene(view_rect).boundingRect()
        # 获取文本项的边界矩形,计算其左上角的场景坐标
        text_rect = self.text_item.boundingRect()
        text_top_left = self.text_item.pos() + QPointF(text_rect.left(), text_rect.top())
        # 计算相对于视口的比例
        self.text_rel_x = (text_top_left.x() - scene_rect.left()) / scene_rect.width()
        self.text_rel_y = (text_top_left.y() - scene_rect.top()) / scene_rect.height()

    def on_view_resize(self, event):
        # 视图尺寸变化时调整文本位置
        self.adjust_text_pos()
        # 调用父类的resizeEvent确保正常渲染
        super(type(self.view), self.view).resizeEvent(event)

    def adjust_text_pos(self):
        view_rect = self.view.viewport().rect()
        scene_rect = self.view.mapToScene(view_rect).boundingRect()
        text_rect = self.text_item.boundingRect()

        # 根据比例计算文本左上角的新场景坐标
        new_top_left_x = scene_rect.left() + self.text_rel_x * scene_rect.width()
        new_top_left_y = scene_rect.top() + self.text_rel_y * scene_rect.height()

        # 将左上角坐标转换为文本项的pos(默认pos是文本基线的左端点)
        new_pos = QPointF(new_top_left_x - text_rect.left(), new_top_left_y - text_rect.top())
        self.text_item.setPos(new_pos)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())

关键细节说明

  1. 锚点自定义:代码默认用文本左上角作为相对位置基准,你可以自行修改update_text_rel_pos和adjust_text_pos中的坐标计算逻辑,改为中心、右下角等锚点。
  2. 缩放适配:mapToScene方法会自动处理视图的缩放变换,所以即使视图缩放,计算出的场景区域依然准确,比例不会失效。
  3. 拖动状态捕捉:通过事件过滤器监听文本的左键释放事件,确保每次拖动完成后及时更新比例值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:00:59