PyQt5:如何在 QThread 和一些子 class 之间 'communicate'?

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

关于这个问题,我指的是@eyllanesc 在

中的回答

@eyllanesc 展示了如何使用 verticalScrollBar() 使文本自动平滑滚动。效果很好。

对于这个问题,我添加了一些额外的行,以使用 QThread 来获取文本。

这里我想实现的是:QThread class'communicate'跟AnimationTextEdit class,这样滚动时间就可以了由文本长度决定。以便程序在滚动过程结束时停止。

我必须说这对我来说是非常棘手的任务。我想先展示一下程序流程,和我想象的一样。

更新: 我的代码如下。它有效,但是...

代码问题: 当文本停止滚动时,time.sleep() 仍然有效。该应用程序在那里等待,直到 time.sleep() 停止。

我想得到的: 当文本停止滚动时,time.sleep() 运行到它的结束值。

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_())

感谢您的帮助和提示。我做错了什么?

虽然在动画中确定了持续时间,但有必要了解这并不准确,这可能会因多种原因而有所不同,因此计算使用睡眠等待它在特定时间结束并关闭申请可能会失败。

如果你的mainobjective是动画结束时程序执行结束那么你必须使用finishedQVariantAnimation信号来完成线程的执行,这个信号是在什么时候发出的它完成执行。

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_())