Rt Rtt
Rt Rtt

Reputation: 617

PyQt5: How to 'communicate' between QThread and some sub-class?

To this question I am referring to the answer from @eyllanesc in PyQt5: How to scroll text in QTextEdit automatically (animational effect)?

There @eyllanesc shows how to make the text auto scrolls smoothly using verticalScrollBar(). It works great.

For this question, I have added some extra lines, to use QThread to get the text.

What I want to achieve here: to let the QThread class 'communicate' with the AnimationTextEdit class, so that the scrolling time can be determined by the text-length. So that the programm stops, when the scrolling process ends.

I must say it is very very tricky task for me. I would like to show the programm flow first, as I imagined.

enter image description here

UPDATE: My code is as follows. It works, but...

Problem with the code: when the text stops to scroll, the time.sleep() still works. The App wait there till time.sleep() stops.

What I want to get: When the text stops to scroll, the time.sleep() runs to its end value.

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
import time
import sqlite3


class AnimationTextEdit(QTextEdit):
    # signal_HowLongIsTheText = pyqtSignal(int)  # signal to tell the QThread, how long the text is

    def __init__(self, *args, **kwargs):
        QTextEdit.__init__(self, *args, **kwargs)
        self.animation = QVariantAnimation(self)
        self.animation.valueChanged.connect(self.moveToLine)

    # def sent_Info_to_Thread(self):
    #     self.obj_Thread = Worker()
    #     self.signal_HowLongIsTheText.connect(self.obj_Thread.getText_HowLongIsIt)
    #     self.signal_HowLongIsTheText.emit(self.textLength)
    #     self.signal_HowLongIsTheText.disconnect(self.obj_Thread.getText_HowLongIsIt)


    @pyqtSlot()
    def startAnimation(self):
        self.animation.stop()
        self.animation.setStartValue(0)

        self.textLength = self.verticalScrollBar().maximum()
        # self.sent_Info_to_Thread()


        self.animation.setEndValue(self.textLength)
        self.animation.setDuration(self.animation.endValue()*4)
        self.animation.start()

    @pyqtSlot(QVariant)
    def moveToLine(self, i):
        self.verticalScrollBar().setValue(i)



class Worker(QObject):
    finished = pyqtSignal()
    textSignal = pyqtSignal(str)

    # @pyqtSlot(int)
    # def getText_HowLongIsIt(self, textLength):
    #     self.textLength = textLength

    @pyqtSlot()
    def getText(self):
        longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])

        self.textSignal.emit(longText)

        time.sleep(10)
        # time.sleep(int(self.textLength / 100))
        # My question is about the above line: time.sleep(self.textLength)
        # Instead of giving a fixed sleep time value here,
        # I want let the Worker Class know,
        # how long it will take to scroll all the text to the end.

        self.finished.emit()


class MyApp(QWidget):
    def __init__(self):
        super(MyApp, self).__init__()
        self.setFixedSize(600, 400)
        self.initUI()
        self.startThread()

    def initUI(self):
        self.txt = AnimationTextEdit(self)
        self.btn = QPushButton("Start", self)

        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.txt)
        self.layout.addWidget(self.btn)

        self.btn.clicked.connect(self.txt.startAnimation)

    def startThread(self):
        self.obj = Worker()
        self.thread = QThread()

        self.obj.textSignal.connect(self.textUpdate)
        self.obj.moveToThread(self.thread)
        self.obj.finished.connect(self.thread.quit)
        self.thread.started.connect(self.obj.getText)
        self.thread.finished.connect(app.exit)
        self.thread.start()

    def textUpdate(self, longText):
        self.txt.append(longText)
        self.txt.moveToLine(0)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

Thanks for the help and hint. What have I done wrong?

Upvotes: 1

Views: 584

Answers (1)

eyllanesc
eyllanesc

Reputation: 243947

Although in the animation the duration is established it is necessary to understand that this is not exact, this could vary for several reasons, so calculating using the sleep to wait for it to end in a certain time and just closing the application may fail.

If your main objective is that when the animation is finished the program execution is finished then you must use the finished QVariantAnimation signal to finish the execution of the thread, this signal is emited when it finishes executing.

class AnimationTextEdit(QTextEdit):
    def __init__(self, *args, **kwargs):
        QTextEdit.__init__(self, *args, **kwargs)
        self.animation = QVariantAnimation(self)
        self.animation.valueChanged.connect(self.moveToLine)

    @pyqtSlot()
    def startAnimation(self):
        self.animation.stop()
        self.animation.setStartValue(0)
        self.textLength = self.verticalScrollBar().maximum()
        self.animation.setEndValue(self.textLength)
        self.animation.setDuration(self.animation.endValue()*4)
        self.animation.start()

    @pyqtSlot(QVariant)
    def moveToLine(self, i):
        self.verticalScrollBar().setValue(i)


class Worker(QObject):
    textSignal = pyqtSignal(str)
    @pyqtSlot()
    def getText(self):
        longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
        self.textSignal.emit(longText)


class MyApp(QWidget):
    def __init__(self):
        super(MyApp, self).__init__()
        self.setFixedSize(600, 400)
        self.initUI()
        self.startThread()

    def initUI(self):
        self.txt = AnimationTextEdit(self)
        self.btn = QPushButton("Start", self)

        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.txt)
        self.layout.addWidget(self.btn)

        self.btn.clicked.connect(self.txt.startAnimation)

    def startThread(self):
        self.obj = Worker()
        self.thread = QThread()

        self.obj.textSignal.connect(self.textUpdate)
        self.obj.moveToThread(self.thread)
        self.txt.animation.finished.connect(self.thread.quit)
        self.thread.started.connect(self.obj.getText)
        self.thread.finished.connect(app.exit)
        self.thread.start()

    def textUpdate(self, longText):
        self.txt.append(longText)
        self.txt.moveToLine(0)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

Upvotes: 3

Related Questions