在 QThread 中使用定时器

Using Timers with QThread

我遇到了这样的错误:

QObject::startTimer: Timers can only be used with threads started with QThread

我的全部代码:

from PyQt5.QtWidgets import *
from newCode import mp3Download
from PyQt5.QtCore import QTimer,QThread
from threading import Thread
import sys


class ListDownload(QWidget):
    def __init__(self,path):
        super().__init__()
        self.path = path
        self.time = 100
        self.initUI()
    def initUI(self):
        self.VerticalLayout = QVBoxLayout()

        self.SeasonOfWitch = QLabel()
        self.SeasonOfWitch.setText("Enter the URLs : ")

        self.URLs = QTextEdit()
        self.URLs.setText("""enter the addresses in the here .a row for each url.
        Delete this message before entering the URLs.""")

        self.horizontalControlLayout = QHBoxLayout()
        self.download = QPushButton("Download")
        self.download.clicked.connect(self.videoDownload)

        self.Cancel = QPushButton("Cancel")
        self.Cancel.clicked.connect(self.cancelFunc)
        self.horizontalControlLayout.addWidget(self.download)
        self.horizontalControlLayout.addWidget(self.Cancel)


        self.VerticalLayout.addWidget(self.SeasonOfWitch)
        self.VerticalLayout.addWidget(self.URLs)
        self.VerticalLayout.addLayout(self.horizontalControlLayout)

        self.setLayout(self.VerticalLayout)        

    def cancelFunc(self):
        self.close()

    def videoDownload(self):
        self.urlList = self.URLs.toPlainText().split("\n")
        row = 1
        errorList = list()
        for url in self.urlList:
            if 'www.youtube.com' in url.split("/") and (url.startswith("https://") or url.startswith("http://")):
                row+=1

            else:
                errorList.append(row)
                row+=1

        decrease = 0#Each element deleting index changes the lenght of the list.Cause of that.
        for row in errorList:
            self.urlList.pop(row-1-decrease)
            decrease += 1

        messageObj = Thread(target=self.messageAnimation,name="message")
        downloadObj = Thread(target=self.downloadFunc,name="download")


        messageObj.start()
        downloadObj.start()

        while not(downloadObj.is_alive()):
            messageObj._stop()

    def downloadFunc(self):
        mp3Download(self.urlList,self.path)

    def messageAnimation(self):
        def timerFunc():
            self.animatedMessageFunc("Downloading ....")

        self.timer = QTimer()
        self.timer.timeout.connect(timerFunc)
        self.timer.start(1000)    


    def remove_widget(self,layout,widget_name):
        layout.removeWidget(widget_name)
        widget_name.deleteLater()
        widget_name = None

    def animatedMessageFunc(self,message):
        animatedMessage = message
        self.URLs.clear()
        iterator = iter(range(len(animatedMessage)))
        for i in range(len(animatedMessage)):
            QTimer.singleShot(self.time,lambda :self.URLs.setText(self.URLs.toPlainText()+animatedMessage[next(iterator)]))
            self.time += 50
        self.time = 100

问题出在计时器上。在animatedMessageFunc()。我想启动两个功能 在同一个 time.I 中使用 threading 模块中的 Thread class。我想要它是因为 当 self.downloadFunc() 结束时,我需要停止 self.messageAnimation() 函数。我尝试使用 QThread 而不是 Thread。但我无法弄清楚如何使用此 class.There 是错误,因为 mine.But java 或其他一些 language.I 中的错误无法找到我的解决方案问题。

计时器不必在另一个线程中,当线程启动时启动并在线程结束其任务时停止它就足够了,为此我们创建了 2 个信号:startedfinished 将分别在调用 mp3Download() 函数之前和之后发出。

import sys
from itertools import cycle
from threading import Thread
from PyQt5 import QtCore, QtGui, QtWidgets

from newCode import mp3Download


class ListDownload(QtWidgets.QWidget):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()

    def __init__(self, path):
        super().__init__()
        self.path = path
        self.initUI()

    def initUI(self):
        VerticalLayout = QtWidgets.QVBoxLayout(self)

        SeasonOfWitch = QtWidgets.QLabel("Enter the URLs : ")

        self.URLs = QtWidgets.QTextEdit()
        self.URLs.setText("""enter the addresses in the here .a row for each url.
        Delete this message before entering the URLs.""")

        horizontalControlLayout = QtWidgets.QHBoxLayout()
        download = QtWidgets.QPushButton("Download")
        download.clicked.connect(self.videoDownload)

        Cancel = QtWidgets.QPushButton("Cancel")
        Cancel.clicked.connect(self.close)
        horizontalControlLayout.addWidget(download)
        horizontalControlLayout.addWidget(Cancel)

        VerticalLayout.addWidget(SeasonOfWitch)
        VerticalLayout.addWidget(self.URLs)
        VerticalLayout.addLayout(horizontalControlLayout)

        self.started.connect(self.messageAnimation)

    def videoDownload(self):
        lines = self.URLs.toPlainText().split("\n")
        urls = []
        for line in lines:
            if 'www.youtube.com' in line.split("/") and (line.startswith("https://") or line.startswith("http://")):
                urls.append(line)
        if urls:
            Thread(target=self.downloadFunc, args=(urls,), name="download", daemon=True).start()

    def downloadFunc(self, urls):
        self.started.emit()
        mp3Download(urls, self.path)
        self.finished.emit()

    def messageAnimation(self):
        self.URLs.clear()
        text = "Downloading ...."
        timer = QtCore.QTimer(self, interval=50)
        it = cycle(text+"\n")
        timer.timeout.connect(lambda: self.appendLetter(next(it)))
        timer.start()
        self.finished.connect(timer.stop)
        self.finished.connect(self.URLs.clear)

    def appendLetter(self, letter):
        if letter == "\n":
            self.URLs.clear()
        else:
            self.URLs.moveCursor(QtGui.QTextCursor.End)
            self.URLs.insertPlainText(letter)
            self.URLs.moveCursor(QtGui.QTextCursor.End)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    path = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.DownloadLocation)
    w = ListDownload(path)
    w.show()
    sys.exit(app.exec_())