Ich beschäftige mich zur Zeit mit dem Rewrite von Fillplayer. Es sollte eine GUI mit GTK/Glade erhalten und die Daten in SQLite speichern. Nun zeigte sich das Problem, dass wenn in einem einfachen Callback die MP3-Sammlung durchsucht wurde, die GUI hing und das Programm abgestürzt schien. Das ist für mich natürlich nicht akzeptabel :) Die Lösung sind Threads, also quasi das Musiksammlung scannen in den Hintergrund zu verlegen. Leider ist das einfacher gesagt als getan. Das »threading« Modul scheint da aktuellste zu sein, also nahm ich das. Die ersten Versuche bestanden darin, das Scannen in eine seperate Funktion zu verlegen und als Thread zu starten:
from threading import Thread
t = Thread(target=scanfunction,args=('verzeichnis',))
t.start()
Dies führte dazu, dass die scanfunction immer nach dem beenden der GUI ausgeführt wurde, also nicht das war was ich wollte. ein zusätzliches t.join() führte die Funktion zwar zum richtigen Zeitpunkt aus, blockierte aber erwartungsgemäß wieder die GUI.
Durch Googlen und Stöbern in der PyGtk-Dokumentation bin ich dann auf die Funktionen gtk.gdk.threadsinit(), gtk.threadsenter() und gtk.threads_leave() gestoßen. Da ich nicht gleich das richtige Stück Dokumentation erwischte und die Dokumentation richtig interpretierte, ging ich fälschlicherweise davon aus, dass es sich hierbei um einen ganz anderen Ansatz handelte. Doch verschiedenste Experimente mit diesen drei Funktionen brachten keine Ergebnisse.
Nach weiterem Googeln, entdeckte ich dann in einem Code-Beispiel, wie das alles richtig zusammengehört:
gtk.gdk.threads_init() # Gtk threadingfähig machen
gtk.main() # Mainloop starten
Thread(target=scanfunktion,args=('verzeichnis',)).start() # Thread starten
def scanfunction(verzeichnis):
while busy:
# mache irgendwas ganz aufwändiges
gtk.threads_enter() # Beginne Bereich, in dem Code ausgeführt wird, auf den nur ein Thread zugreifen darf (zB GUI)
xml.get_widget('progressbar').pulse()
gtk.threads_leave() # Ende des Bereichs
Das sind nur die für Threads relevanten Bereiche. Den kompletten Code kann man im SVN einsehen.
Mir hat die Zeile:
beste Dienste geleistet. Ein stümperhaftes Beispiel eines Python-Einsteigers:
import gtk import pygtk import threading
class Gui(threading.Thread):
def deleteevent(self, widget, event, data = None): gtk.mainquit() return False
def start_work(self, widget, data = None): print 'Klick auf Start' self.worker = Worker() self.worker.start()
def stop_work(self, widget, data = None): print 'Klick auf Stop' self.worker.interrupted = True del self.worker
def init(self): threading.Thread.init(self)
def run(self): self.window = gtk.Window(gtk.WINDOWTOPLEVEL) self.window.settitle('Multi-Threading')
class Worker(threading.Thread):
def init(self): threading.Thread.init(self) self.interrupted = False
def run(self): while(self.interrupted == False): print '... working ...' else: print '!!! STOP !!!'
if name == 'main': gui = Gui() gui.start()