4

I built a PyQt5 GUI to do some Selenium testing. Everything works as expected, except for the PyQt progress bar.

In the first example below, where I use the Selenium browser, the progress bar just jumps to 100%, at the end, when the browser closes. But, the Selenium works as expected.

def test(self):
        self.completed = 0
        browser = webdriver.Firefox()
        links = ['http://www.somesite.com/', 'http://www.somesite.com/page2',
                 'http://www.somesite.com/page3']
        for link in links:
            browser.get(link)
            self.completed += 100 / len(links)
            time.sleep(2)
            print(link)
            self.progressBar.setValue(self.completed)
        browser.close()

But, in this version below, with the Selenium browser commented out, the progress bar works as expected.

def test(self):
        self.completed = 0
        #browser = webdriver.Firefox()
        links = ['http://www.somesite.com/', 'http://www.somesite.com/page2',
                 'http://www.somesite.com/page3']
        for link in links:
            #browser.get(link)
            self.completed += 100 / len(links)
            time.sleep(2)
            print(link)
            self.progressBar.setValue(self.completed)
        #browser.close()
Joe T. Boka
  • 6,554
  • 6
  • 29
  • 48

1 Answers1

4

The blocking tasks are not friendly with the event loop where the GUI is executed as they prevent the normal tasks that the GUI performs such as ticket checking, redrawing, etc. from being executed.

The solution in these cases is to use thread to execute the blocking task and use the signals to send the information.

import sys

from PyQt5 import QtCore, QtWidgets

from selenium import webdriver

class SeleniumWorker(QtCore.QObject):
    progressChanged = QtCore.pyqtSignal(int)
    def doWork(self):
        progress = 0
        browser = webdriver.Firefox()
        links = ['http://www.somesite.com/',
        'http://www.somesite.com/page2',
        'http://www.somesite.com/page3']
        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)
        button = QtWidgets.QPushButton("Start")
        lay.addWidget(progressBar)
        lay.addWidget(button)
        self.thread = QtCore.QThread()
        self.worker = SeleniumWorker()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.doWork)
        button.clicked.connect(self.thread.start)
        self.worker.progressChanged.connect(progressBar.setValue, QtCore.Qt.QueuedConnection)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks for this. This works great! But, unfortunately, I still can't use it because I need to use the progress bar for several different functions. Not just one. Can you please advise me on how I can use this solution for other lists in other methods? – Joe T. Boka Apr 04 '18 at 03:34
  • @JoeT.Boka You understand that your current requirement is a little confusing, I could help you if you provide a [mcve], in addition to a clearer explanation as for example what percentage represents the execution of each function. – eyllanesc Apr 04 '18 at 03:39
  • Thanks. I will create and provide a new example that will demonstrate the current problem more clearly. I will let you know when the new question is posted. Thanks again. – Joe T. Boka Apr 04 '18 at 03:53
  • I posted an example of the current problem as discussed. https://stackoverflow.com/questions/49647205/pyqt-progress-jumps-to-100-after-it-starts Hope it makes sense. Please take a look. – Joe T. Boka Apr 04 '18 at 09:20