Here is a solution that i found for multi-threaded PyGTK+ GUI hang/Crash. After a long search i found this code in some where on the internet, but i can't remember where is it really placed.
For What:
GTK+ is not thread safe, So we have to use "gtk.gdk.threads_enter" and "gtk.gdk.threads_leave" for that. This will make threading safe on GTK+, Like this
#!/usr/bin/env python
# -*- coding: utf-8 -*-
gtk.gdk.threads_enter()
#Handling GTK+ GUI
gtk.gdk.threads_leave()
but some time GUI hangs when same thread acquire this lock for more than one time like this.
So we have to find is this thread has it's lock already or not and make lock.gtk.gdk.threads_enter()
#Handling GTK+ GUI
gtk.gdk.threads_enter() #************
#again Handling GTK+ GUI
gtk.gdk.threads_leave()
here is the code that will track threads and find is that really has it lock or not and make correct Locks.
Code:Download
I used this code for my project RoxBird Download Manager.class Check_For_Lock:
def __init__(self):
self.lock = threading.Lock()
self.thread = None
self.locked = 0
def __enter__(self):
with self.lock: DoLock = (thread.get_ident()!=self.thread)
if DoLock:
gtk.gdk.threads_enter()
with self.lock: self.thread = thread.get_ident()
self.locked += 1
return None
def __exit__(self, exc_type, exc_value, traceback):
with self.lock:
self.locked -= 1
if self.thread!=thread.get_ident():
print "!ERROR! Thread freenot locked lock!"
sys.exit(0)
else:
if self.locked == 0:
self.thread = None
#Added BY me SEE WHY?:
#http://library.gnome.org/devel/gtk-faq/stable/x491.html
gtk.gdk.flush()
gtk.gdk.threads_leave()
return None
TLocker = Check_For_Lock()
def TLocked(f):
def wraps(f):return f
#@wraps(f)
def wrapper(*args, **kwds):
with TLocker.lock:
if TLocker.thread == None or TLocker.thread==thread.get_ident():
TLocker.thread = thread.get_ident()
TLocker.locked += 1
WeHold = True
else:
print "***ERROR: GtkLocked for non-owned thread!"
WeHold = False
ret = f(*args, **kwds)
if WeHold:
with TLocker.lock:
TLocker.locked -= 1
if TLocker.locked == 0: TLocker.thread = None
return ret
return wrappr
I got so many crashes on RoxBird. after that i removed gdk.threads_enter()/leave() and used this, Now RoxBird have a stable GUI.
you can use it simply like this
with TLocker:
#Make changes on GTK+ GUI.
window.set_size_request(400, 300)
#Connecting Signal via threads
widget.connect('signal_name', TLocked(CallFunction), UsrArg1, UsrArg2, ...)
Notes:
- You should create "gtk.gdk.threads_init()" after importing "gtk" module.
- You should use only one Instance of TLocker for the all application. (you can use reference for TLocker)
- In threads all GUI handling should be inside of the "with TLocker" block.
- "widget.connect" should called with TLocked like above, and connect statement should not inside of the "with TLocker"
if i miss something on code or notes, feel free to comment.