PyQt启动后进度跳到100%
PyQt progress jumps to 100% after it starts
当我 运行 doWork
方法中的代码时,通过单击 button1
,进度条按预期工作。
但是,当我将列表从其他方法(即 btn2
、btn3
)传递给 doWork
方法时,进度条在启动后跳到 100%。
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
是线程处理程序,因此 class 的对象允许我们在主线程以外的线程上执行任务,称为 GUI 线程。
你正在创建一个SeleniumWorker对象,它有doWork方法,这是一个阻塞任务,不应该在GUI线程中执行,这将通过之前的QThread来实现。
由于函数必须在另一个线程中执行,因此具有该方法的对象必须移动到另一个线程。
其他线程中对象的信号连接到GUI线程中的QProgressBar
,此连接必须与标志QtCore.Qt.QueuedConnection
建立.
当线程启动时,它会调用 doWork 函数,因为 self.worker 对象在另一个线程中,所以该函数也将在另一个线程中执行。
在您的情况下,在下一部分显示的代码中,您在线程尚未启动时调用 doWork
函数,因此它将在主线程中执行。
def btn2(self):
...
# main thread
self.worker.doWork(self.lst2)
传递 url 的一种方法是通过 setter 方法,然后启动线程。当线程启动时,会调用doWork
,当doWork
执行时,会发出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_())
注意:我已经添加了在 运行 时禁用按钮的功能,因为线程不会再次启动是合适的当它工作时。
在我看来,你的程序非常快。
慢一点。
QThread.msleep(500)
。
...
for link in links:
browser.get(link)
progress += 100 / len(links)
self.progressChanged.emit(progress)
QThread.msleep(500) # !!!
当我 运行 doWork
方法中的代码时,通过单击 button1
,进度条按预期工作。
但是,当我将列表从其他方法(即 btn2
、btn3
)传递给 doWork
方法时,进度条在启动后跳到 100%。
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
是线程处理程序,因此 class 的对象允许我们在主线程以外的线程上执行任务,称为 GUI 线程。你正在创建一个SeleniumWorker对象,它有doWork方法,这是一个阻塞任务,不应该在GUI线程中执行,这将通过之前的QThread来实现。
由于函数必须在另一个线程中执行,因此具有该方法的对象必须移动到另一个线程。
其他线程中对象的信号连接到GUI线程中的
QProgressBar
,此连接必须与标志QtCore.Qt.QueuedConnection
建立.当线程启动时,它会调用 doWork 函数,因为 self.worker 对象在另一个线程中,所以该函数也将在另一个线程中执行。
在您的情况下,在下一部分显示的代码中,您在线程尚未启动时调用 doWork
函数,因此它将在主线程中执行。
def btn2(self):
...
# main thread
self.worker.doWork(self.lst2)
传递 url 的一种方法是通过 setter 方法,然后启动线程。当线程启动时,会调用doWork
,当doWork
执行时,会发出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_())
注意:我已经添加了在 运行 时禁用按钮的功能,因为线程不会再次启动是合适的当它工作时。
在我看来,你的程序非常快。 慢一点。
QThread.msleep(500)
。 ...
for link in links:
browser.get(link)
progress += 100 / len(links)
self.progressChanged.emit(progress)
QThread.msleep(500) # !!!