[PyKDE] Updating GUI from another thread

Jeffrey Barish jeff_barish at earthlink.net
Thu Apr 7 04:04:10 BST 2005


This problem seems to come up regularly, but I am wondering whether the
solution I found for my situation is safe.  I have a program in which I
run a time-consuming task in its own thread.  Every time the task
reaches a milestone, I want it to deselect a listbox item.  In the test
program below, the GUI thread creates a listbox with 3 items.  The user
can select one and then push the button.  The object is to deselect the
selected item from another thread.  I tried using a custom event.  I
found that I need a timer in the GUI thread to trigger a processEvents
call.  Then I discovered that I don't seem to need the custom event: I
can simply deselect the item from the task thread, although the
processEvents call is still necessary.  Is the issue that something bad
could happen if I happen to try selecting the item at the same time the
task thread is trying to deselect it?  Is the solution with the custom
event optimal?  The timer seems inelegant.  I had hoped to find a
solution where the thread could somehow order the GUI thread to
deselect and update.

# The purpose of this test is to explore the problem of deselecting a
# listbox item from another thread.

import sys
import thread
from qt import *

class MainWindow(QMainWindow):
    def __init__(self, *args):
        QMainWindow.__init__(self, *args)

        self.mainWidget = QWidget(self)
        self.setCentralWidget(self.mainWidget)
        self.lb = QListBox(self.mainWidget)
        self.button = QPushButton("Clear selections", self.mainWidget)
        QObject.connect(self.button, SIGNAL('clicked()'),
                        self.clearSelections)
        for i in range(3): QListBoxText(self.lb, "some text %d" % i)
        self.vlayout = QVBoxLayout(self.mainWidget)
        self.vlayout.addWidget(self.lb)
        self.vlayout.addWidget(self.button)

        self.timer = QTimer()
        QObject.connect(self.timer, SIGNAL("timeout()"),
                        self.periodicCall)
        self.timer.start(500)

    def periodicCall(self):
        app.processEvents()

    def clearSelections(self):
        thread.start_new_thread(self.do_clearSelections, ())

    def customEvent(self, e):
        if e.type() == 65432:
            lbi = e.lbi
            lb = e.lb
            lb.setSelected(lbi, False)

    def do_clearSelections(self):
        lbi = self.lb.firstItem()
        while lbi:
            print "lbi.selected() =", lbi.selected()
            if lbi.selected():
##                e = MyEvent(self.lb, lbi)
##                app.postEvent(self, e)
                self.lb.setSelected(lbi, False)
            lbi = lbi.next()

class MyEvent(QCustomEvent):
    def __init__(self, lb, lbi):
        QCustomEvent.__init__(self, 65432)
        self.lb = lb
        self.lbi = lbi

    def lbi(self):
        return self.lbi

app = None

def main(args):
    global app

    app = QApplication(args)
    win = MainWindow()
    app.setMainWidget(win)
    win.show()
    app.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
    app.exec_loop()

if __name__ == "__main__":
    main(sys.argv)
-- 
Jeffrey Barish




More information about the PyQt mailing list