Streams in Gtk python

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

main.py file

class Gui:
    ...
    def on_update_click():
        update()

app=Gui()
Gtk.main()

      

update.py file

def update():
    #check site for updates
    time.sleep(21600) #check again in 6hrs

      

I suspect I will have to use streams. my thinking:

Gtk.main () starts the main thread.

when the user clicks the update button, update () runs in the background. #thread 2

Am I correct or am I missing something?

EDIT: Ok, the
      on_update_click function:

            Thread(target=update).start(). 

      

K, the computer no longer freezes: D

so what happens now, only when I close Gtk.main (), only the update thread is started. It's nice that it keeps updating when the UI is closed, but I would also like to start it when the UI is enabled.

+2


source to share


3 answers


So finally I 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()

      

First I used:

thread.start_new_thread(update())

      

in the on_update_click function. As my JFSebastian already mentioned, this was incorrect, as it would bring up this thread right away. This froze my entire computer.



Then I just added:

Thread(target=update).start()

      

The on_update_clicked function only worked after the main thread Gtk.main () was closed. This way the threads were not started at the same time.

adding: GObject.threads_init ()

this allowed threads to run the python interpreter sequentially: Themes in Gtk !

+6


source


thread.start_new_thread(update())

wrong. It calls update()

immediately on the main thread, and you don't have to use the module thread

directly; use a module instead threading

.

You can call threading.current_thread()

to find out which thread is executing update()

.

To keep your code simpler, you can run all gtk code on the main thread and use blocking operations to fetch web pages and run them on a background thread.

Based on an extended example from the 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 called from different threads.

See also Multithreaded GTK Applications - Part 1: Misconceptions .

+4


source


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

Another way is to use an asynchronous network for example. using python-gio (GObject-IO) or another library that has the ability to work with the main GLib loop ( as it does with Twisted ). This approach is slightly different and uses non-blocking socket operations. Your main loop will make a callback when data from the socket (the site you are polling from) is readable. Unfortunately GIO does not have a high level HTTP API, so you can use GSocketClient

and manually create the HTTP request structure.

0


source







All Articles