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

水平排列QGraphicsView中QTextEdit鼠标选中文本异常问题

QGraphicsView水平排布场景下QTextEdit鼠标选中文本异常问题

问题表现

同一布局内共放置3个QGraphicsView控件:

  • 当控件为垂直排列(orientation参数设为0)时,鼠标拖动选中QTextEdit内文本的功能无任何异常
  • 当控件为水平排列(orientation参数设为1)时,文本选择功能失效:即便视图是水平排布的,用户仍然需要垂直拖动鼠标才能选中文本,水平拖动无法触发符合直觉的选区更新

已排查到的逻辑

鼠标按下事件触发时,初始坐标以QTextEdit自身坐标系为基准判定;鼠标移动事件触发时,会以鼠标按下的位置作为锚点计算当前鼠标点位更新选区;鼠标释放事件的坐标判定逻辑与上述逻辑一致。

待解决疑问

是否有方法实现水平排布场景下自然流畅的鼠标选中文本效果?还是Qt本身仅支持垂直方向拖动的鼠标文本选择逻辑?

复现代码

from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsProxyWidget, QTextEdit
from PySide6.QtGui import QTextDocument, QPageSize, QPainter, QBrush, QTextCharFormat, QTextOption
from PySide6.QtCore import Qt, Signal, QRect, QRectF, QEvent, QSize, QSizeF, QPointF


class MainGraphicsView(QGraphicsView):
    def __init__(self, parent=None):
        super(MainGraphicsView, self).__init__(parent)
        self._mainscene = MainGraphicsScene(1)
        self._mainscene._mainview = self
        self.setScene(self._mainscene)          


class MainGraphicsScene(QGraphicsScene):  
    def __init__(self, orientation = 1, parent=None):
        super(MainGraphicsScene, self).__init__(parent)
        
        if orientation == 0:
            self.rootView = TextGraphicsView()        
            self.rootView.main_scene = self
            self.root_proxywidget = self.addWidget(self.rootView)
            self.rootView.setSceneRect(0, 0, self.document()._pageWidth, self.document()._pageHeight)

            self.secondaryView = SecondaryTextGraphicsView(2, self.rootView.scene())
            self.secondaryView.setSceneRect(0, self.document()._pageHeight, self.document()._pageWidth, self.document()._pageHeight)
            self.secondary_proxywidget = self.addWidget(self.secondaryView)
            self.secondary_proxywidget.setPos(0, self.document()._pageHeight)

            self.thirdView = SecondaryTextGraphicsView(3, self.rootView.scene())
            self.thirdView.setSceneRect(0, self.document()._pageHeight*2, self.document()._pageWidth, self.document()._pageHeight)
            self.third_proxywidget = self.addWidget(self.thirdView)
            self.third_proxywidget.setPos(0, self.document()._pageHeight*2)
            
        if orientation == 1:
            self.rootView = TextGraphicsView()        
            self.rootView.main_scene = self
            self.root_proxywidget = self.addWidget(self.rootView)
            self.rootView.setSceneRect(0, 0, self.document()._pageWidth, self.document()._pageHeight)

            self.secondaryView = SecondaryTextGraphicsView(2, self.rootView.scene())
            self.secondaryView.setSceneRect(0, self.document()._pageHeight, self.document()._pageWidth, self.document()._pageHeight)
            self.secondary_proxywidget = self.addWidget(self.secondaryView)
            self.secondary_proxywidget.setPos(self.document()._pageWidth, 0)

            self.thirdView = SecondaryTextGraphicsView(3, self.rootView.scene())
            self.thirdView.setSceneRect(0, self.document()._pageHeight*2, self.document()._pageWidth, self.document()._pageHeight)
            self.third_proxywidget = self.addWidget(self.thirdView)
            self.third_proxywidget.setPos(self.document()._pageWidth*2, 0)

        self.setBackgroundBrush(QBrush(Qt.gray))      
   
    def view(self):
        return self._mainview

    def textedit(self):
        return self.document().parent()

    def document(self):
        return self.rootView.document()
    
    
class TextGraphicsView(QGraphicsView):
    def __init__(self,  parent=None):
        super().__init__(parent)
        self.page = 1
        self.initScene()

    def mousePressEvent(self, event):
        SecondaryTextGraphicsView.selectedPage = self.page
        return QGraphicsView.mousePressEvent(self, event)

    def initScene(self):
        self.common_scene = TextGraphicsScene()
        self.common_scene.root_view = self
        self.setScene(self.common_scene)
        self.setSceneRect(0, self.document()._pageHeight*(self.page -1), self.document()._pageWidth, self.document()._pageHeight)
        self.resize(int(self.document()._pageWidth), int(self.document()._pageHeight))
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def view(self):
        return self.main_scene._mainview
    
    def textedit(self):
        return self.common_scene.textedit
    
    def document(self):
        return self.common_scene.textedit.document()       


class SecondaryTextGraphicsView(QGraphicsView):
    def __init__(self, page, scene, parent=None):
        super().__init__(scene, parent)
        self.page = page
        self.initScene()
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)    

    def initScene(self):
        self.setSceneRect(0, self.document()._pageHeight*(self.page-1), self.document()._pageWidth, self.document()._pageHeight)
        self.resize(int(self.document()._pageWidth), int(self.document()._pageHeight))

    def view(self):
        return self.scene().root_view.view()

    def textedit(self):
        return self.scene().textedit()
    
    def document(self):
        return self.scene().textedit.document()


class TextGraphicsScene(QGraphicsScene):
    def __init__(self, parent=None):
        super().__init__(parent)        
        self.textedit = TextEdit(scene=self)        
        self.addWidget(self.textedit)

    def view(self):
        return self.root_view

    def document(self):
        return self.textedit.document()     
    

class TextEdit(QTextEdit):
    def __init__(self,  scene, parent=None):
        super().__init__(parent)
        self.setAttribute(Qt.WA_InputMethodEnabled, True)
        self.setMouseTracking(False)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)       
        self.scene = scene
        self.setDocument(TextDocument(parent=self))
        self.setWordWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere)
        self.setLineWrapMode(self.LineWrapMode.FixedPixelWidth)
        self.setLineWrapColumnOrWidth(self.document()._pageWidth)
        self.setAttribute(Qt.WA_InputMethodEnabled, True)
        self.setInputMethodHints(Qt.ImhMultiLine)
        self.resize(self.document()._pageSize.toSize().width(), self.document()._pageSize.toSize().height()*3)
        self.setPlainText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        \Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
                        sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
                        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
                        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
                        Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")

    def mouseMoveEvent(self, event):
        print(event.position())
        return QTextEdit.mouseMoveEvent(self, event)
    
    def view(self):
        return self.scene.root_view
    
    def pageSize(self):
        return self.document()._pageSize    
        

class TextDocument(QTextDocument):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._pageSize = QPageSize.size(QPageSize.A4, QPageSize.Point)       
        self._pageHeight = self._pageSize.height()
        self._pageWidth  = self._pageSize.width()
        self.setDocumentMargin(30.5) 


def main():
    import sys
    app = QApplication([]) if QApplication.instance() is None else QApplication.instance()
    m = MainGraphicsView()
    m.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:39:18