Stephen
Stephen

Reputation: 673

Create loop that doesn't lock up the window Pyqt4

I'm using PyQt4 and I've created a window with a toggle button. When I select the button, I would like to start a loop that updates the screen as quickly as possible. Currently, it locks up my windows and I cannot select the button again to turn off the loop or even move the window. I was hoping to find a way to do this without threads. I can't post my actual code, but this is basically what I have right now.

self.connect(self.pushButton,SIGNAL("toggled(bool)"),self.testLoop)

def testLoop(self, bool):
    while bool:
        print "looping"

Upvotes: 2

Views: 3746

Answers (2)

BrtH
BrtH

Reputation: 2664

Use a separate thread for the looping.

import threading

self.connect(self.pushButton,SIGNAL("toggled(bool)"),self.on_testLoop)

def on_testLoop(self, bool):
    threading.Thread(target=self.testLoop, args=(bool,)).start()

def testLoop(self, bool):
    while bool:
        print "looping"

Another method I found very useful was to make the function into a generator. This enables you to do stuff after each iteration.

self.connect(self.pushButton,SIGNAL("toggled(bool)"),self.on_testLoop)

def on_testLoop(self, bool):
    for _ in testLoop(bool):
        # do some stuff, update UI elements etc.

def testLoop(self, bool):
    while bool:
        print "looping"
        yield

I don't know how good it is to use a generator for this, as I'm quite new to PyQt and gui programming, but this worked quite well for me.

Upvotes: 2

ekhumoro
ekhumoro

Reputation: 120678

The approach you take will be mostly determined by the nature of the task that your loop is carrying out.

If you can break the task down into very small steps, it may be possible to simply start your loop with a timer and then call processEvents on every step to update your gui:

from PyQt4 import QtGui, QtCore

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.label = QtGui.QLabel('Count = 0', self)
        self.button = QtGui.QPushButton('Start', self)
        self.button.clicked.connect(self.handleButton)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.button)
        self._active = False

    def handleButton(self):
        if not self._active:
            self._active = True
            self.button.setText('Stop')
            QtCore.QTimer.singleShot(0, self.runLoop)
        else:
            self._active = False

    def closeEvent(self, event):
        self._active = False

    def runLoop(self):
        from time import sleep
        for index in range(100):
            sleep(0.05)
            self.label.setText('Count = %d' % index)
            QtGui.qApp.processEvents()
            if not self._active:
                break
        self.button.setText('Start')
        self._active = False

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Upvotes: 4

Related Questions