【问题标题】:PyQt progress jumps to 100% after it startsPyQt 启动后进度跃升至 100%
【发布时间】:2018-04-04 09:18:28
【问题描述】:

当我在doWork 方法中运行代码时,通过单击button1,进度条按预期工作。

但是,当我将列表从其他方法(即btn2btn3)传递给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_())

【问题讨论】:

  • 按什么顺序点击按钮?不同的顺序是否会产生不同的结果(即不先单击 button1)?您是否尝试过从工作线程延迟和调试 progress 打印?
  • @handle 订单没有任何区别,只是当我第二次点击button1时它不会执行。但是,我可以点击button2button3 任意次数,以任意顺序,我总是会得到相同的结果。
  • 我刚刚安装了pyqt5和selenium plus geckodriver;进度条保持在 100%。可能与将对象移动到线程有关。我会玩一会儿...
  • 要在线程中运行代码,您需要通过QThread.start()
  • @JoeT.Boka 在我的回答中,我已经用工作人员解释了 QThread 的操作,请检查它。 :D

标签: python selenium pyqt pyqt5 qprogressbar


【解决方案1】:

看来你还没有看懂我previous solution的逻辑,我再详细说明一下流程:

[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)
  1. QThread 是线程处理程序,因此该类的对象允许我们在主线程以外的称为 GUI 线程的线程上执行任务。

  2. 您正在创建一个具有 doWork 方法的 SeleniumWorker 对象,这是一个不应在 GUI 线程中执行的阻塞任务,而这将通过之前的 QThread 来实现。

  3. 由于函数必须在另一个线程中执行,具有该方法的对象必须移动到另一个线程。

  4. 另一个线程中对象的信号连接到GUI线程中的QProgressBar,此连接必须使用标志QtCore.Qt.QueuedConnection

  5. 当线程启动时,它会调用 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_())

注意:我添加了在运行时禁用按钮的功能,因为线程在工作时不会再次启动是适当的。 p>

【讨论】:

  • 再次感谢。是的,我绝对没有完全理解您之前解决方案中的逻辑。我刚刚运行了您在此处提供的代码。当我单击任何按钮时,它第一次起作用。但是,当我第二次单击任何按钮时,什么都没有执行。
  • 您的意思是您将使用您提到的新实现来更新您的代码?因为,您当前的代码仍然存在同样的问题。我认为它还没有更新。
  • 我在 17 分钟前测试了你的最后一次更新。当我单击任何按钮时程序崩溃。它对你有用吗?
  • @JoeT.Boka 是的,我意识到了这个问题,我刚刚纠正了它并进行了测试,很抱歉给您带来不便,我有点分心了。
  • @JoeT.Boka 这个观察到还有另一个不便之处,我正在修复它,当我完成它时我注意到
【解决方案2】:

在我看来,你的程序非常快。 慢一点。

QThread.msleep(500)

。 ...

    for link in links:
        browser.get(link)
        progress += 100 / len(links)
        self.progressChanged.emit(progress)

        QThread.msleep(500)                  # !!!

【讨论】:

    猜你喜欢
    • 2019-04-09
    • 1970-01-01
    • 2021-07-02
    • 1970-01-01
    • 1970-01-01
    • 2017-01-14
    • 2015-07-20
    • 1970-01-01
    • 2013-05-14
    相关资源
    最近更新 更多