0

I am writing a slot method for the signal of scrolling down a scrollbar in QPlainTextEdit.

I only found this signalQPlainTextEdit.verticalScrollBar().valueChanged.

I tested this signal and it returned the position number when scrolls to a new position.

My purpose is that when the scrollbar move down and trigger the slot method. But in that signal when move up it also triggeres the slot. I read the document but I couldn't find other signals.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Lisen
  • 168
  • 1
  • 2
  • 11
  • It could be useful to know why do you need to have this notification, as the position (and value, which might and might not be synchronous) of the scroll bar could be changed in various ways, depending on lots of factors: not only direct user interaction on the scroll bar, but also cursor position change, changes in the size of contents, resizing of the window, visibility changes of other UI elements in the same layout... – musicamante Sep 13 '21 at 03:38
  • I want to use this signal to trigger a QSqlQueryModel to load more data. Just like the behaviour in QTableView: when scrolling down it display more data from model – Lisen Sep 13 '21 at 04:17

1 Answers1

3

A possible solution is to save the previous position and compare with the new position using sliderPosition property:

from PyQt5.QtWidgets import QApplication, QPlainTextEdit


class PlainTextEdit(QPlainTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.last_position = self.verticalScrollBar().sliderPosition()
        self.verticalScrollBar().sliderMoved.connect(self.handle_value_changed)

    def handle_value_changed(self, position):
        if position > self.last_position:
            print("down")
        else:
            print("up")
        self.last_position = position


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    w = PlainTextEdit()
    w.show()

    sys.exit(app.exec_())

Another possible option is to implement a use of the mousePressEvent and mouseMoveEvent events of the QScrollBar:

from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtWidgets import QApplication, QPlainTextEdit, QScrollBar


class ScrollBar(QScrollBar):
    last_pos = QPoint()

    def mousePressEvent(self, event):
        self.last_pos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        if event.pos().y() > self.last_pos.y():
            print("down")
        else:
            print("up")
        self.last_pos = event.pos()


class PlainTextEdit(QPlainTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.vertical_scrollbar = ScrollBar(Qt.Vertical)
        self.setVerticalScrollBar(self.vertical_scrollbar)


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    w = PlainTextEdit()
    w.show()

    sys.exit(app.exec_())

OR:

from PyQt5.QtCore import QEvent, QPoint
from PyQt5.QtWidgets import QApplication, QPlainTextEdit


class PlainTextEdit(QPlainTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.last_pos = QPoint()
        self.verticalScrollBar().installEventFilter(self)

    def eventFilter(self, obj, event):
        if obj is self.verticalScrollBar():
            if event.type() == QEvent.MouseButtonPress:
                self.last_pos = event.pos()
            elif event.type() == QEvent.MouseMove:
                if event.pos().y() > self.last_pos.y():
                    print("down")
                else:
                    print("up")
                self.last_pos = event.pos()
        return super().eventFilter(obj, event)


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    w = PlainTextEdit()
    w.show()

    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Mh, this presents some inconvenience, since it could be triggered in various ways, most importantly when the viewport area changes (due to the scroll area resizing or content change). Wouldn't it be better to use `sliderMoved`? – musicamante Sep 13 '21 at 03:33
  • @musicamante I had not realized it, you are correct. I already update the solution – eyllanesc Sep 13 '21 at 03:37
  • Note that my suggestion is based on the assumption that the required behavior is about the user actually *moving* the slider down (and, honestly, I don't remember if `sliderDown` is also considered for page/step buttons, and if it works in the same way for all styles, since I've seen different ways of implementing input events for sliders in the style sources). I asked further details to the OP, as it's possible that they just didn't explain it clearly, or don't understand the underlying differences. – musicamante Sep 13 '21 at 03:46
  • @musicamante thanks a lot for your reply. Maybe I didn't explain it clearly. My purpose is to use the scroll down action to trigger a fetchmore method of a QSqlQueryModel. – Lisen Sep 13 '21 at 04:20
  • @eyllanesc I don't remember if QPlainTextEdit works differently, but the event filter is already installed on all QAbstractScrollArea scrollbars by default. – musicamante Sep 13 '21 at 06:20
  • The relative position *change* isn't relavant here. What matters is how close the current scroll position is to its maximum value. When it reaches the maximum (or gets within within a few percent of it), more content must be fetched. Another issue is what happens when the window is resized larger. This could create a blank area at the bottom of the text-edit that needs to be filled with more content. However, the scroll-bar won't trigger that, so the resize-event would need to be used instead. – ekhumoro Sep 13 '21 at 12:02