ees
ees

Reputation: 327

Updating progress bar from external script

I'm trying to update a PyQt4 progress bar live from an external python script as that external script runs through its loops. I've provided a minimal, working example of my progress so far; can anyone please guide me on best practices going forward?

GUI.py:

import sys, os
from pyface.qt import QtGui, QtCore
os.environ['ETS_TOOLKIT'] = 'qt4'
from traits.api import HasTraits,Instance,on_trait_change,Int
from traitsui.api import View,Item,VGroup
import external
#from PyQt4.QtCore import QThread


class P1(QtGui.QWidget):
    def __init__(self, parent=None):
        super(P1, self).__init__(parent)
        layout = QtGui.QGridLayout(self)

        def setProgress():
            if P1.progress.value() == 0:
                self.button.setDisabled(True)
                self.button.setText('Computing Data')

            external.op()

            if P1.progress.value() == 100:
                self.button_dist.setText('Data Complete')

        self.button = QtGui.QPushButton('Compute Data', self)
        self.connect(self.button, QtCore.SIGNAL('clicked()'), setProgress)
        layout.addWidget(self.button, 1, 2, 1, 1)
        self.button.setDisabled(False)
        self.button.show()

        P1.progress = QtGui.QProgressBar(self)
        P1.progress.setMinimum = 0
        P1.progress.setMaximum = 100
        P1.progress.setValue(0)        
        layout.addWidget(P1.progress, 1, 3, 1, 3)
        P1.progress.show()

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.window = P1(self) 
        self.setCentralWidget(self.window)
        self.show()

if __name__ == '__main__':
    app = QtGui.QApplication.instance()
    w = MainWindow()
    sys.exit(app.exec_())

external.py:

import sys, os, time
from pyface.qt import QtGui, QtCore
os.environ['ETS_TOOLKIT'] = 'qt4'
from GUI import P1
#from PyQt4.QtCore import QThread


def op():
    for i in range(1, 101):
        p1 = P1()
        p1.progress.setValue(i)
        time.sleep(0.01)
        print(i)

For this example to work as I intend, when you click the button Compute Data, the progress bar should fill in at the same rate that i in range(1,101) is printed in terminal.

Upvotes: 0

Views: 1563

Answers (1)

eyllanesc
eyllanesc

Reputation: 243917

You have a circular import that causes an infinite loop since GUI imports to external and external to GUI, that is a symptom of a bad design. On the other hand the progressbar must be a member of the class. In addition, the external task must be executed in a thread and to update them you must use a signal that will transport the data to the GUI thread.

external.py

import sys, os, time
from pyface.qt import QtGui, QtCore
os.environ['ETS_TOOLKIT'] = 'qt4'

def op(progress):
    for i in range(1, 101):
        progress.emit(i)
        time.sleep(0.01)
        print(i)

GUI.py

import sys, os
from pyface.qt import QtGui, QtCore
os.environ['ETS_TOOLKIT'] = 'qt4'
import threading
# from traits.api import HasTraits,Instance,on_trait_change,Int
# from traitsui.api import View,Item,VGroup
import external


class P1(QtGui.QWidget):
    progressChanged = QtCore.Signal(int)

    def __init__(self, parent=None):
        super(P1, self).__init__(parent)
        self.button = QtGui.QPushButton('Compute Data')
        self.button.clicked.connect(self.start_task)
        self.progress = QtGui.QProgressBar(minimum=0, maximum=100, value=0)
        self.progressChanged.connect(self.on_progressChanged) 

        layout = QtGui.QGridLayout(self)
        layout.addWidget(self.button, 1, 2, 1, 1)    
        layout.addWidget(self.progress, 1, 3, 1, 3)

    @QtCore.Slot()
    def start_task(self):
        t = threading.Thread(target=external.op, args=(self.progressChanged, ), daemon=True)
        t.start()

    @QtCore.Slot(int)
    def on_progressChanged(self, val):
        self.progress.setValue(val)
        if val == self.progress.maximum():
            self.button.setDisabled(False)
            self.button.setText('Data Complete')
        else:
            self.button.setText('Computing Data')
            self.button.setDisabled(True)

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.window = P1() 
        self.setCentralWidget(self.window)
        self.show()

if __name__ == '__main__':
    app = QtGui.QApplication.instance()
    if app is None:
        app = QtGui.QApplication([])
    w = MainWindow()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions