Threading in Gtk python

后端 未结 3 1374
甜味超标
甜味超标 2020-12-03 13:21

So I\'m busy writing an application that needs to check for updates from a website after a certain amount ouf time, I\'m using python with Gtk +3

main.py file

<
相关标签:
3条回答
  • 2020-12-03 13:33

    thread.start_new_thread(update()) is wrong. It calls update() immediately in the main thread and you shouldn't use thread module directly; use threading module instead.

    You could call threading.current_thread() to find out which thread executes update().

    To simplify your code you could run all gtk code in the main thread and use blocking operations to retrieve web-pages and run them in background threads.

    Based on the extended example from GTK+ 3 tutorial:

    #!/usr/bin/python
    import threading
    import urllib2
    from Queue import Queue
    
    from gi.repository import Gtk, GObject
    
    UPDATE_TIMEOUT = .1 # in seconds
    
    _lock = threading.Lock()
    def info(*args):
        with _lock:
            print("%s %s" % (threading.current_thread(), " ".join(map(str, args))))
    
    class MyWindow(Gtk.Window):
    
        def __init__(self):
            Gtk.Window.__init__(self, title="Hello World")
    
            self.button = Gtk.Button(label="Click Here")
            self.button.connect("clicked", self.on_button_clicked)
            self.add(self.button)
    
            self.updater = Updater()
            self._update_id = None
            self.update()
    
        def on_button_clicked(self, widget):
            info('button_clicked')
            self.update()
    
        def update(self):
            if self._update_id is not None: 
                GObject.source_remove(self._update_id)
    
            self.updater.add_update(self.done_updating) # returns immediately
            # call in UPDATE_TIMEOUT seconds
            self._update_id = GObject.timeout_add(
                int(UPDATE_TIMEOUT*1000), self.update)
    
        def done_updating(self, task_id):
            info('done updating', task_id)
            self.button.set_label("done updating %s" % task_id)
    
    
    class Updater:
        def __init__(self):
            self._task_id = 0
            self._queue = Queue(maxsize=100) #NOTE: GUI blocks if queue is full
            for _ in range(9):
                t = threading.Thread(target=self._work)
                t.daemon = True
                t.start()
    
        def _work(self):
            # executed in background thread
            opener = urllib2.build_opener()
            for task_id, done, args in iter(self._queue.get, None):
                info('received task', task_id)
                try: # do something blocking e.g., urlopen()
                    data = opener.open('http://localhost:5001').read()
                except IOError:
                    pass # ignore errors
    
                # signal task completion; run done() in the main thread
                GObject.idle_add(done, *((task_id,) + args))
    
        def add_update(self, callback, *args):
            # executed in the main thread
            self._task_id += 1
            info('sending task ', self._task_id)
            self._queue.put((self._task_id, callback, args))
    
    GObject.threads_init() # init threads?
    
    win = MyWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    
    Gtk.main()
    

    Note: GObject.idle_add() is the only gtk-related function that is called from different threads.

    See also Multi-threaded GTK applications – Part 1: Misconceptions.

    0 讨论(0)
  • 2020-12-03 13:42

    Threading is the first way to solve the problem. You can create thread and run long-running blocking function inside that thread (and your GUI won't hang up).

    Another way is to use asynchronous networking e.g. using python-gio (GObject-IO) or another library that has a possibility to work with GLib's main loop (like they do with Twisted). This approach is a bit different and uses non-blocking socket operations. Your main loop will make a callback when data from socket (site you're polling) will be available to read. Unfortunately GIO has no high-level HTTP API, so you can use GSocketClient and manually create HTTP requests structure.

    0 讨论(0)
  • 2020-12-03 13:49

    So I finally managed to get it to work. I needed to say:

    from gi.repository import Gtk,GObject
    
    GObject.threads_init()
    Class Gui:
        .....
        ......
        def on_update_click():
                Thread(target=update).start()
    

    At first I used:

    thread.start_new_thread(update())
    

    in the on_update_click function. As mentioned my J.F Sebastian this was incorrect as this would immediately call this thread. This froze my whole computer.

    I then just added:

    Thread(target=update).start()
    

    The on_update_clicked function only worked once the main Thread Gtk.main() was closed. So the threads were not running simultaneously.

    by adding: GObject.threads_init()

    this allowed for the threads to run serially to the python interpreter: Threads in Gtk!

    0 讨论(0)
提交回复
热议问题