1

I'm quite new to this site, so I hope I follow all the rules whilst asking. On with the problem:

I'm making a GTK+ / Python3 program built with Glade. When the user clicks a button to Read a File, certain time-consuming functions are called. Here's a snippet to give you an idea:

def onReadFile(self, widget, data=None):

    ### I want the statusbar (had the same problem with a progressbar) 
    ### to push() and display at this point
    self.statusbar.push(self.contextID, "Reading file...")

    ### Unfortunately, my program waits for all this to finish
    ### before displaying the statusbar.push() far too late
    text = back.readFile(self.inFile)
    splitText = back.textSplit(text)
    choppedArray = back.wordChop(splitText)
    back.wordCount(choppedArray, self.currDict)
    currSortedArray = back.wordOrder(self.currDict)

    handles.printResults(currSortedArray, "resCurrGrid", self.builder)

The main idea is that things aren't happening in the order I expect them to, and I don't know why (the things still happen without errors). I read around (here and there and thought perhaps threading is my problem, but I wasn't sure as I didn't find anyone asking too similar a question as mine.

Why is the statusbar.push() waiting until the end to display its message?

Update

I've tried to sort it out following a threading example here, but I just can't 'fit' that lesson to the layout of my class-based program.

This is what it's looking like (clipped for brevity and relevance):

from gi.repository import Gtk, GLib, GObject
import threading
import back
import handles

class Application:

    def __init__(self):
        # I build from glade
        self.builder = Gtk.Builder()
        ...

        # I want this window to show later
        self.progWindow = self.builder.get_object("progWindow")

        self.window = self.builder.get_object("mainWindow")
        self.window.show()

        # I believe this code should go here in the __init__, though
        # I'm quite possibly wrong
        thread = threading.Thread(target=self.onReadFile)
        thread.daemon = True
        thread.start()

    ...

    def upProgress(self):
        self.progWindow.show()

        return False

    def onReadFile(self, widget, data=None):

        # Following the example I linked to, this to my understanding
        # should show the progWindow by asking the main Gtk thread
        # to execute it
        GLib.idle_add(self.upProgress)

        # Time-consuming operations
        text = back.readFile(self.inFile)
        splitText = back.textSplit(text)
        ...

if __name__ == "__main__":
    GObject.threads_init()

    main = Application()

    Gtk.main()

I do get the following error when I threading.Thread(target=self.onReadFile), but it's the closest to 'working' I can get it:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.2/threading.py", line 740, in _bootstrap_inner
self.run()
  File "/usr/lib/python3.2/threading.py", line 693, in run
self._target(*self._args, **self._kwargs)
TypeError: onReadFile() takes at least 2 arguments (1 given)

My only ideas are that:

  1. I need quite a bit different structure because I'm using a class (and my example in the link isn't).
  2. I'm failing to pass a necessary argument, but for the life of me I can't see what.
  3. I'm failing at life.

If you can help, fantastic, if you can't but can suggest a great tutorial, great. My hair can't take much pulling.

If I should post a full working example, let me know.

Community
  • 1
  • 1

1 Answers1

2

Because GTK+ redraws happen from the same thread of execution that runs the callbacks. So if you do something in a callback that requires a redraw, that won't actually happen until after the callback exits.

The solution is to de-couple the long-running operation, by using a thread or by using asynchronous I/O.

If you use threads, remember that only one thread can do GTK+ calls.

You can also manually work around this by letting GTK+ handle pending events from inside a callback, by using something like this from this answer:

while Gtk.events_pending(): Gtk.main_iteration()
Community
  • 1
  • 1
unwind
  • 391,730
  • 64
  • 469
  • 606
  • Great info, thanks. Seems I'll have to do some reading about threading and asynchronous I/O. I'll have a look, but if you could point me anywhere, it'd be greatly appreciated. – Matthew-matvei Apr 16 '15 at 11:30