Search code examples
qtpyside

How to prevent QPlainText area from scrolling to end of line?


When appending text longer than the widget's width with word wrap set to NoWrap, how do I prevent the view from moving to the end of the line like in the second image?

What I want:

enter image description here

What I get:

enter image description here

Code for re-creating the above image(s):

from PySide6 import QtWidgets as qtw


class MainWindow(qtw.QMainWindow):

    def __init__(self):
        super().__init__()
        self._plain_text_edit = qtw.QPlainTextEdit()
        self._plain_text_edit.appendPlainText(' '.join(str(i) for i in range(1, 101)))
        self.setCentralWidget(self._plain_text_edit)
        self._plain_text_edit.setLineWrapMode(qtw.QPlainTextEdit.NoWrap)
        self.show()

app = qtw.QApplication()
mw = MainWindow()
app.exec()

This one example has a single line but the actual program will have multiple lines increasing throughout the run of the program. I'm using a QPlainText edit to display logs. I've tried moving the cursor, but I want the user to be able to select the text even when there's text being added and moving the cursor resets the selection every new line added.

Edit:
An MRE for a situation more similar to my actual program:

from PySide6 import QtWidgets as qtw
from PySide6 import QtCore


class MainWindow(qtw.QTabWidget):

    def __init__(self):
        super().__init__()
        self._plain_text_edit = qtw.QPlainTextEdit()
        self._plain_text_edit.setLineWrapMode(qtw.QPlainTextEdit.NoWrap)

        self._button = qtw.QPushButton("Press to add text.")
        self._button.clicked.connect(
            lambda:
                self._plain_text_edit.appendPlainText(
                    ' '.join(str(i) for i in range(1, 101))
                )
        )

        self.addTab(self._button, "Nothing")
        self.addTab(self._plain_text_edit, "edit")
        self.show()

app = qtw.QApplication()
mw = MainWindow()
app.exec()

Solution

  • It is quite simple. Before adding the text, get the selection (anchor and cursor position) and after adding the text, just restore the selection.

    from PySide6 import QtWidgets as qtw
    from PySide6 import QtCore, QtGui
    
    def appendPlainTextAndKeepCursor(text_edit, text):
        tc = text_edit.textCursor()
        if tc.atEnd():
            # store the current selection
            anchor = tc.anchor()
            position = tc.position()
    
            # change the text
            text_edit.appendPlainText(text)
    
            # restore the selection
            tc.setPosition(anchor)
            tc.setPosition(position, QtGui.QTextCursor.KeepAnchor)
            text_edit.setTextCursor(tc)
        else:
            # just add the text
            text_edit.appendPlainText(text)
    
    
    class MainWindow(qtw.QTabWidget):
    
        def __init__(self):
            super().__init__()
            self._plain_text_edit = qtw.QPlainTextEdit()
            self._plain_text_edit.setLineWrapMode(qtw.QPlainTextEdit.NoWrap)
    
            self._button = qtw.QPushButton("Press to add text.")
            self._button.clicked.connect(lambda: appendPlainTextAndKeepCursor(self._plain_text_edit, ' '.join(str(i) for i in range(1, 101))))
    
            self.addTab(self._button, "Nothing")
            self.addTab(self._plain_text_edit, "edit")
            self.show()
    
    app = qtw.QApplication()
    mw = MainWindow()
    app.exec()