GTK+ 3 and Threading

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Nebelhom
User
Beiträge: 155
Registriert: Mittwoch 19. Mai 2010, 01:31

Hi folks,

Das hier ist wahrscheinlich eher ein generelles Problem, aber es hat mit GTK+ zu tun, weshalb ich es mal hier rein verfrachte. Ich bin gerade dabei mein ProgressBar Widget zum laufen zu bringen. Dafür habe ich den code in diesem link von pygtk auf GTK+ 3 umgeschrieben (siehe unten). Das klappt auch wunderbar.

Jetzt möchte ich aber, dass mein WorkerThread auch Argumente annehmen kann. Am besten wäre eine Variable Anzahl, so dass ich nicht für jede Anzahl von Argumenten eine neue WorkerThread Klasse schreiben muss. Ich stelle mir etwas wie zum Beispiel folgendes vor.

Code: Alles auswählen

wt = WorkerThread(self, self.work, arg1, arg2,... argX)
Was dann eine Funktion aufruft, die ungefähr folgendermaßen aussehen würde:

Code: Alles auswählen

def work(self, *args):
Hat da irgendjemand eine Ahnung, wie man das verwirklichen könnte? Ich bin für jeden Hinweis dankbar, denn die Versuche der letzten tage haben eindeutig gezeigt, dass ich keine Ahnung habe, wie man das anstellen könnte ;) Danke schonmal für Eure Hilfe. Falls ich mich schlecht ausgedrückt habe, dann bitte ich um Entschuldigung und werde versuchen es besser zu erklären.

Code: Alles auswählen

import threading
import random, time

from gi.repository import Gtk, GObject 

class MainThread:

    def __init__(self):
        GObject.threads_init()
        self.fs = None

    def main_quit(self, obj):
        if not self.fs == None: self.fs.stop()
        Gtk.main_quit()
        
    def pulse(self):
        self.progressbar.pulse()
        return self.still_working # 1 = repeat, 0 = stop

    def work(self): ###### This would be the actual time-consuming workload
        time.sleep(200) 

    def main(self):
        window = Gtk.Window()
        vbox = Gtk.VBox()
        self.progressbar = Gtk.ProgressBar()
        vbox.pack_start(self.progressbar, True, True, 0)
        window.add(vbox)
        window.show_all()
        window.connect('destroy', self.main_quit)
        
        wt = WorkerThread(self.work, self)
        wt.start()
        GObject.timeout_add(100, self.pulse)
        
        Gtk.main()


class WorkerThread(threading.Thread):
    def __init__ (self, function, parent):
        threading.Thread.__init__(self)
        self.function = function
        self.parent = parent
    
    def run(self): # when does "run" get executed?
        self.parent.still_working = True
        self.function()
        self.parent.still_working = False
    
    def stop(self):
        self = None
        
if __name__ == '__main__':
    mt = MainThread()
    mt.main()
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo Nebelhom,

wie eine variable Anzahl an Argumenten übergeben werden kann, hast Du ja schon in Deinem Beispiel geschrieben:

Code: Alles auswählen

def work(self, *args):
    blabla

#Aufruf:
self.work(*args)
Dein WorkerThread ist eine Klasse ohne irgendwelchen Mehrwert, die Querabhängigkeit zu still_working zum Davonlaufen:

Code: Alles auswählen

import threading
import random, time

from gi.repository import Gtk, GObject

class MainThread:

    def __init__(self):
        GObject.threads_init()
        self.fs = None

    def main_quit(self, obj):
        if self.fs is not None: self.fs.stop()
        Gtk.main_quit()
       
    def pulse(self):
        self.progressbar.pulse()
        return self.is_alive() # 1 = repeat, 0 = stop

    def work(self): ###### This would be the actual time-consuming workload
        time.sleep(200)

    def main(self):
        window = Gtk.Window()
        vbox = Gtk.VBox()
        self.progressbar = Gtk.ProgressBar()
        vbox.pack_start(self.progressbar, True, True, 0)
        window.add(vbox)
        window.show_all()
        window.connect('destroy', self.main_quit)
       
        self.wt = threading.Thread(target=self.work, args=()) # <- put arguments here
        self.wt.start()
        GObject.timeout_add(100, self.pulse)
       
        Gtk.main()


if __name__ == '__main__':
    mt = MainThread()
    mt.main()
Nebelhom
User
Beiträge: 155
Registriert: Mittwoch 19. Mai 2010, 01:31

*facepalm*

Das kommt davon, wenn man einen Tunnelblick hat. Ich war so sehr auf dieses Beispiel fixiert, dass ich den Wald vor lauter Bäumen nicht mehr gesehen habe :(.

@Sirius3: Danke für die Hilfe.
Nebelhom
User
Beiträge: 155
Registriert: Mittwoch 19. Mai 2010, 01:31

Noch eine follow-up Frage zu dem selben Thema.

Ich habe jetzt das Problem, dass, wenn im Worker Thread eine exception auftritt, ich diese im Main Thread nicht abfangen kann :( Der Traceback sieht dann ungefähr so aus (wobei PasswordError eine eigens geschriebene Exception ist):

Code: Alles auswählen

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 504, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/nebelhom/SpyderWorkspace/pydf-chain/main_gui.py", line 328, in merge_pdfs
    pdf_ops.merge_pdf(save_path, pdfs, encryp, user_pw, owner_pw, lvl)
  File "/home/nebelhom/SpyderWorkspace/pydf-chain/pdf_operations.py", line 24, in merge_pdf
    raise PasswordError
PasswordError
Weiss jemand von euch, wie man das am besten anstellt. Ich habe bisher nur diesen Eintrag gefunden und den... verstehe ich leider nicht 100%-ig und kriege den code bei mir auch nicht wirklich zum laufen :oops:
Antworten