当我在doWork方法中运行代码时,通过点击button1,进度条将按预期工作。
doWork
button1
然而,当我通过列表中的doWork其他方法的方法(即btn2,btn3),进度条刚跳到100%它启动后。
btn2
btn3
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * import sys from selenium import webdriver class SeleniumWorker(QtCore.QObject): progressChanged = QtCore.pyqtSignal(int) def doWork(self, lst=['http://www.somesite.com/', 'http://www.somesite.com/page2', 'http://www.somesite.com/page3']): progress = 0 browser = webdriver.Firefox() links = lst for link in links: browser.get(link) progress += 100 / len(links) self.progressChanged.emit(progress) browser.close() class Widget(QtWidgets.QWidget): def __init__(self, *args, **kwargs): QtWidgets.QWidget.__init__(self, *args, **kwargs) lay = QtWidgets.QHBoxLayout(self) progressBar = QtWidgets.QProgressBar() progressBar.setRange(0, 100) button1 = QtWidgets.QPushButton("Start1") button2 = QtWidgets.QPushButton("Start2") button3 = QtWidgets.QPushButton("Start3") lay.addWidget(progressBar) lay.addWidget(button1) lay.addWidget(button2) lay.addWidget(button3) self.thread = QtCore.QThread() self.worker = SeleniumWorker() self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.doWork) button1.clicked.connect(self.thread.start) button2.clicked.connect(self.btn2) button3.clicked.connect(self.btn3) self.worker.progressChanged.connect(progressBar.setValue) def btn2(self): self.lst2 = ['http://www.somesite.com/page4', 'http://www.somesite.com/page5', 'http://www.somesite.com/page6'] self.worker.doWork(self.lst2) def btn3(self): self.lst3 = ['http://www.somesite.com/page7', 'http://www.somesite.com/page8', 'http://www.somesite.com/page9'] self.worker.doWork(self.lst3) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) w = Widget() w.show() sys.exit(app.exec_())
看来您不了解我先前解决方案的逻辑,我将详细说明该过程:
[1] self.thread = QtCore.QThread() [2] self.worker = SeleniumWorker() [3] self.worker.moveToThread(self.thread) [4] self.worker.progressChanged.connect(progressBar.setValue, QtCore.Qt.QueuedConnection) [5] self.thread.started.connect(self.worker.doWork)
QThread 是线程处理程序,因此该类的对象使我们可以在除主线程(称为GUI线程)之外的其他线程上执行任务。
QThread
您正在创建一个具有doWork方法的SeleniumWorker对象,该对象是不应在GUI线程中执行的阻塞任务,并且将使用先前的QThread来实现。
由于该函数必须在另一个线程中执行,因此具有该方法的对象必须移至该另一个线程。
另一个线程中的对象的信号连接到QProgressBarGUI线程中的对象,该连接必须使用flag进行QtCore.Qt.QueuedConnection。
QProgressBar
QtCore.Qt.QueuedConnection
当线程启动时,它将调用doWork函数,并且由于self.worker对象在另一个线程中,因此该函数也将在另一个线程中执行。
对于您在下一部分显示的代码中,您doWork在线程尚未启动时正在调用该函数,因此它将在主线程中执行。
def btn2(self): ... # main thread self.worker.doWork(self.lst2)
传递url的一种方法是通过setter方法,然后启动线程。线程启动时,将调用doWork,doWork执行时,将progressChanged发出信号。
progressChanged
通过以上所有操作,我们获得了以下内容:
import sys from PyQt5 import QtCore, QtGui, QtWidgets from selenium import webdriver class SeleniumWorker(QtCore.QObject): progressChanged = QtCore.pyqtSignal(int) started = QtCore.pyqtSignal() finished = QtCore.pyqtSignal() def setUrls(self, urls=['http://www.somesite.com/', 'http://www.somesite.com/page2', 'http://www.somesite.com/page3']): self.urls = urls def doWork(self): self.started.emit() progress = 0 self.progressChanged.emit(progress) browser = webdriver.Firefox() links = self.urls for link in links: browser.get(link) progress += 100 / len(links) self.progressChanged.emit(progress) browser.close() self.finished.emit() class Widget(QtWidgets.QWidget): def __init__(self, *args, **kwargs): QtWidgets.QWidget.__init__(self, *args, **kwargs) lay = QtWidgets.QHBoxLayout(self) self.progressBar = QtWidgets.QProgressBar() self.progressBar.setRange(0, 100) button1 = QtWidgets.QPushButton("Start1") button2 = QtWidgets.QPushButton("Start2") button3 = QtWidgets.QPushButton("Start3") lay.addWidget(self.progressBar) lay.addWidget(button1) lay.addWidget(button2) lay.addWidget(button3) self.thread = QtCore.QThread() self.worker = SeleniumWorker() self.worker.moveToThread(self.thread) self.worker.progressChanged.connect(self.progressBar.setValue, QtCore.Qt.QueuedConnection) self.thread.started.connect(self.worker.doWork) button1.clicked.connect(self.btn1) button2.clicked.connect(self.btn2) button3.clicked.connect(self.btn3) self.worker.finished.connect(self.on_finished) self.worker.started.connect(lambda: self.buttons_setEnable(False)) def on_finished(self): self.buttons_setEnable(True) if self.thread.isRunning(): self.thread.quit() self.thread.wait() def buttons_setEnable(self, enable): for btn in self.findChildren(QtWidgets.QPushButton): btn.setEnabled(enable) def btn1(self): self.worker.setUrls() self.thread.start() def btn2(self): lst2 = ['http://www.somesite.com/page4', 'http://www.somesite.com/page5', 'http://www.somesite.com/page6'] self.worker.setUrls(lst2) self.thread.start() def btn3(self): lst3 = ['http://www.somesite.com/page7', 'http://www.somesite.com/page8', 'http://www.somesite.com/page9'] self.worker.setUrls(lst3) self.thread.start() if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) w = Widget() w.show() sys.exit(app.exec_())
注意 :我添加了在按钮运行时禁用按钮的功能,因为适当的是,线程在运行时不要再次启动。