在 QTextEdit 的任一方向上扩展选择

Extending selection in either direction in a QTextEdit

目前,QTextEdit 允许选择文本,然后仅在锚点对面的选择一侧通过 shift-click-drag 更改该选择。锚点位于选择开始的位置。如果用户试图在开始附近更改选择,则选择将围绕锚点旋转而不是扩展。我想允许从任一侧更改选择。

我的第一次尝试是简单地将锚点设置在光标所在位置的另一侧。例如,选择从 10 到 20。如果光标在位置 8 处按住 shift 单击并拖动,则锚点将设置为 20。如果光标在位置 22 处按住 shift 单击并拖动,则anchor 将设置为 10。稍后,我会尝试更强大的东西,也许基于选择的中心点。

我认为这段代码可以工作,但它似乎根本不影响默认行为。我错过了什么?

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class TextEditor(QTextEdit):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setReadOnly(True)
        self.setMouseTracking(True)

    def mouseMoveEvent(self, event):
        point = QPoint()
        x = event.x()    #these are relative to the upper left corner of the text edit window
        y = event.y()
        point.setX(x)
        point.setY(y)
        self.mousepos = self.cursorForPosition(point).position()  # get character position of current mouse using local window coordinates

        if event.buttons()==Qt.LeftButton:
            modifiers = QApplication.keyboardModifiers()
            if modifiers == Qt.ShiftModifier:
                start = -1 #initialize to something impossible
                end = -1
                cursor = self.textCursor()
                select_point1 = cursor.selectionStart()
                select_point2 = cursor.selectionEnd()
                if select_point1 < select_point2: # determine order of selection points
                    start = select_point1
                    end = select_point2
                elif select_point2 < select_point1:
                    start = select_point2
                    end = select_point1
                if self.mousepos > end: # if past end when shift-click then trying to extend right
                    cursor.setPosition(start, mode=QTextCursor.MoveAnchor)
                elif self.mousepos < start: # if before start when shift-click then trying to extend left
                    cursor.setPosition(end, mode=QTextCursor.MoveAnchor)
                if start != -1 and end != -1: #if selection exists then this should trigger
                    self.setTextCursor(cursor)

        super().mouseMoveEvent(event)

这是实现当前选择的 shift+click 扩展的第一次尝试。好像还可以,但我还没死死测试过,所以可能会有一两个小故障。预期的行为是在选择上方或下方的 shift+click 应在该方向上扩展整个选择; shift+click 拖拽应该做同样的事情,只是连续。

请注意,我还设置了文本交互标志,以便插入符号在只读模式下可见,并且还可以使用键盘以各种方式操作选择(例如,ctrl+shift+right 扩展选择下一个单词)。

import sys
from PySide.QtCore import *
from PySide.QtGui import *

class TextEditor(QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setReadOnly(True)
        self.setTextInteractionFlags(
            Qt.TextSelectableByMouse |
            Qt.TextSelectableByKeyboard)

    def mouseMoveEvent(self, event):
        if not self.setShiftSelection(event, True):
            super().mouseMoveEvent(event)

    def mousePressEvent(self, event):
        if not self.setShiftSelection(event):
            super().mousePressEvent(event)

    def setShiftSelection(self, event, moving=False):
        if (event.buttons() == Qt.LeftButton and
            QApplication.keyboardModifiers() == Qt.ShiftModifier):
            cursor = self.textCursor()
            start = cursor.selectionStart()
            end = cursor.selectionEnd()
            if not moving or start != end:
                anchor = cursor.anchor()
                pos = self.cursorForPosition(event.pos()).position()
                if pos <= start:
                    start = pos
                elif pos >= end:
                    end = pos
                elif anchor == start:
                    end = pos
                else:
                    start = pos
                if pos <= anchor:
                    start, end = end, start
                cursor.setPosition(start, QTextCursor.MoveAnchor)
                cursor.setPosition(end, QTextCursor.KeepAnchor)
                self.setTextCursor(cursor)
                return True
        return False

if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = TextEditor()
    window.setText(open(__file__).read())
    window.setGeometry(600, 50, 800, 800)
    window.show()
    sys.exit(app.exec_())