You are not logged in.

#1 2011-06-20 22:45:22

Padfoot
Member
Registered: 2010-09-03
Posts: 381

[SOLVED] Python3, Gtk3 and threading - help needed

Hi,

I am new to threaded programming (just something I have been able to avoid until now.....hehehe)

The issue I have, I believe is very simple to implement, but I am just not using the right pieces in the right places.

Ok, quite simply, i have my main program 'main.py' which controls the gtk for my app. This also imports a custom module 'mymod.py' which runs scripts to generate data to populate the gtk widgets.

The problem I have is whenever I call any methods in the module, the gtk freezes until that method is done. I really want to be able to dynamically update gtk (ie, a simple progress bar) from within the module, based on the output of the scripts/processes it runs.

Following is a stripped down code example of what I have implemented:

I must note, the 'GLib.thread_init(None) call generates the following error.... "glib.GError: Could not locate g_thread_init: `g_thread_init': /usr/lib/libglib-2.0.so.0: undefined symbol: g_thread_init"

Cheers for all you help!

main.py

#!/usr/bin/python

from gi.repository import Gtk, GdkPixbuf, Gdk, GLib
import os, sys
from subprocess import *
from pymymod import mymod

UI_FILE = '/path/to/my/ui'
WIDGET_LIST = ['main_window', 'message_dialog', 'all', 'widgets', 'need', 'to', 'access']

class MyClass:
    def __init__(self):
        self.build_gtk()
        self.mymod = mymod()
        self.mymod.start()
        self.gui['message_dialog'].show_all()
        self.mymod.refresh(self.gui['message_dialog'])

    def build_gtk(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file(UI_FILE)
        self.builder.connect_signals(self)
        self.gui = {}
        for widget in WIDGET_LIST:
            self.gui[widget] = self.builder.get_object(widget)
        self.gui['main_window'].show_all

    def destroy(widget, self):
        Gtk.main_quit()

def main():
    GLib.thread_init(None)
    app = MyClass()
    Gdk.threads_enter()
    Gtk.main()
    Gdk.threads_leave()
        
if __name__ == "__main__":
    sys.exit(main())

mymod.py

#!/usr/bin/python

from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys
from subprocess import *
from threading import Thread

class mymod (Thread):

    def __init__(self):
        # General initialisation stuff here then.....
        Thread.__init__(self)

    def refresh(self, gtk_widget):
        #running a subprocess call here and using output to update gtk.

        process = Popen(query, stdout=PIPE, stderr=PIPE)

        while not process.poll():
            output = process.stdout.readline()
            if not output:
                break
            else:
                # update gtk here

Last edited by Padfoot (2011-06-24 09:51:19)

Offline

#2 2011-06-21 05:26:11

whitie
Member
Registered: 2011-03-13
Posts: 20

Re: [SOLVED] Python3, Gtk3 and threading - help needed

Hello Padfoot,
I don't know Gtk3, but in all GUI Toolkits you should not touch the GUI-Thread from another thread. Try giving a callback function to the "subprocess" thread which gets called with the new data as argument if the thread has finished.

Whitie

Offline

#3 2011-06-21 05:43:01

ewaller
Administrator
From: Pasadena, CA
Registered: 2009-07-13
Posts: 17,199

Re: [SOLVED] Python3, Gtk3 and threading - help needed

You might want to consider the multiprocessing module in lieu of the threading module.  In CPython, only one thread can run at a time and you may be seeing thread blocking.  And whitie is spot on -- All things GUI must be in a single thread.  Most of the GUI toolkits are not re-entrant.


Nothing is too wonderful to be true, if it be consistent with the laws of nature -- Michael Faraday
Sometimes it is the people no one can imagine anything of who do the things no one can imagine. -- Alan Turing
---
How to Ask Questions the Smart Way

Offline

#4 2011-06-21 07:11:45

Padfoot
Member
Registered: 2010-09-03
Posts: 381

Re: [SOLVED] Python3, Gtk3 and threading - help needed

@ whitie & ewaller

Thanks for your response guys.

I just need to know how I can have a gtk element updated while a call to another module is running.

I should point out, I was using a call to main.py from mymod.py while the subprocess was running to pass the output. This call in main.py was taking the output and applying it to the appropriate gtk element. So all the gtk is in it's own thread.

So in the example code above (forgetting the threading stuff) when

self.mymod.refresh

is called in main.py, I would like the text label in the message dialog to display the results while the call to

self.mymod.refresh

is running.

At the moment, the gtk is frozen until the call completes, and then only the last line of the generated output is displayed.

I'll have a look at multiprocessing to see if that helps.

I am sure it is something very simple I am missing.

Anywhere you can point me, or example code, is greatly appreciated.

Cheers.

Last edited by Padfoot (2011-06-21 07:21:56)

Offline

#5 2011-06-22 12:51:04

KenjiTakahashi
Member
From: outside the universe
Registered: 2011-06-22
Posts: 24
Website

Re: [SOLVED] Python3, Gtk3 and threading - help needed

Hi,

Threads work like that:

class Mmod(Thread):
    def __init__(self):
        Thread.__init__(self)
    def run(self):
    #Here you put your threaded code.
    #It has to be named run!
    pass

Then, when you execute Mmod().start(), your run() method will be executed in a separate thread.
Btw. Python convention says that class names should be Capitalized and I think it's good to follow it.

That said, I'm pretty sure there is a native threads implementation designed for GTK, which should have some callbacks (or signals or whatever it's called there) that will help to indicate the state of a thread to the GUI.
And they should be easier to use here than default Python module.

Last thing, as already stated, is that you should never ever run any GUI related code outside main thread. You should use an callback (or sth) to "get back" to your main thread and update the widget there.

Offline

#6 2011-06-23 09:57:44

Padfoot
Member
Registered: 2010-09-03
Posts: 381

Re: [SOLVED] Python3, Gtk3 and threading - help needed

Thanks for your help KenjiTakahasi.

I have re-configured my code so all functions in the module are accessed from the run() method.

Now my issue (after plugging in many print statements to see where the 'freeze' is happening) seems to be the gtk.main() call. Following is a code step through showing what happens:

import Module
mod = Module()
mod.start()

Ok, an instance of the thread is created (it's __init__() method calls Thread.__init__(self))
The module's run() method is started - confirmed by a stream of console output (the run() method is an infinite loop until a flag is received by calling the module's quit() method to clean up and exit the thread)

Gdk.threads_init()
if __name__ == "__main__":
    sys.exit(main())

def main():
        app = Main()
        Gdk.threads_enter()
        Gtk.main()
        Gdk.threads_leave()

Now, the main loop is entered. An instance of the main application is created (the module's run() method is still streaming to the console)
Lastly we enter the Gtk main loop, and the module's run() method freezes.

I have hooked up a button in the main application to display the result of mod.is_alive() which returns True.

I have hooked up another button in the main app to a signal handler in the module which triggers another flag to execute different code in the run() method. This signal fires and exits correctly, yet is not registered in the run() method as this is still frozen.

Now, if the main app gets a destroy signal, the following code is executed:

        Gdk.threads_leave()
        mod.quit()
        mod.join()
        Gdk.threads_enter()
        Gtk.main_quit()

As soon as the destroy signal is triggered, the module's run() method unfreezes, but of course, it's too late as the thread is shut down, followed by the main app.

The other problem I am getting (randomly, not all the time) is a lot of zombie threads are being created.

HELP!

Cheers smile

[UPDATE]

Ok, I can now confirm from the above setup, whenever a gtk signal is received and acted upon by the app, the module's run() method unfreezes while in the signal. As soon as the signal has exited, the run() method freezes again. Also, because of this, any callback to the main app to update gtk will cause a lock up when inside a Gdk.threads_enter() Gdk.threads_leave() pair, as it seems to only execute the run() method while in a signal (even though I do not call it directly in any way from a signal. The onlycall to run() comes from the start() call at the beginning!)

Last edited by Padfoot (2011-06-23 10:20:56)

Offline

#7 2011-06-24 09:50:19

Padfoot
Member
Registered: 2010-09-03
Posts: 381

Re: [SOLVED] Python3, Gtk3 and threading - help needed

SOLVED

I knew it was something simple I was missing.

Finally getting my mind into the gdk docs, I found that Gdk.threads_init() should not be called until GLib.thread_init() has been called.

Funny, I remember using that originally, but GLib.thread_init() is actually broken. It complains of a missing symbol in libglib.

After plugging that error into google, and a lot of reading, I found someone with the same issue, and the solution is to initialise threads from GObject instead using GObject.threads_init().

Now my code works and no more zombie processes are being generated!

Cheers.

Offline

Board footer

Powered by FluxBB