Threading Event-Problem

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Faba
User
Beiträge: 5
Registriert: Dienstag 7. Oktober 2008, 15:24

Hallo,
ich arbeite zum ersten Mal intensiv mit Threads und hab da so meine Probleme.
Genauer gesagt geht es um die Synchronisation der Threads mittels des Event-Objekts.
Hier mein Problem: Ich habe eine Hauptklasse für meine GUI und eine Klasse, die ich als Thread starten möchte, um da dann später Sounds mit pyaudio aufzunehmen.
Das Thread starten ist auch nicht sonderlich mein Problem, sondern mehr das Beenden. Also es funktioniert, aber ich verstehe nicht, wieso, die Lösung war mehr Try and error.
Hier grob mein Code:

Code: Alles auswählen

class GUIFrame(Frame):
    def record(self):
        self.rec.startRec()
    
    def stop(self):
        self.rec.stopRec()

    def createWidgets(self):
        self.start = Button(self)
        self.start["text"] = "Start"
        self.start["command"] =  self.record

        self.start.grid(row=0, column=0)

        self.stop = Button(self)
        self.stop["text"] = "Stopp"
        self.stop["command"] = self.rec.stopRec
        self.stop.grid(row=0, column=1)

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.rec = Recorder()
        self.rec.start()
        self.pack()
        self.createWidgets()

BitRate16 = pyaudio.paInt16

class Recorder(threading.Thread):

	def __init__(self):
		threading.Thread.__init__(self)
		self.audio = pyaudio.PyAudio()
		self.recEvent = threading.Event()
		self.recording = False

	def startRec(self):
		self.recEvent.set()

	def stopRec(self):
		print "stop"
		self.recEvent.clear()

	def record(self, chunkSize, format, rate):
		while True:
			self.recEvent.wait()
			print "aufnehmen..."

	def run(self):
		self.recording = True
		self.record(1024, BitRate16, 44100)
Wenn ich das so aufrufe, dann geht alles. Allerdings will ich beim Klick auf den Stop-Button noch weitere Aktionen durchführen und wollte deshalb die Methode GUIFrame.stop() als command-Objekt nutzen, nur dann kriege ich die Schleife nicht pausiert und das ist genau meine Frage: Warum funktioniert das nicht?
Bin für jede Hilfe dankbar und hoffe, das alles detailliert genug geschildert ist...
Benutzeravatar
snafu
User
Beiträge: 6908
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Auch wenn ich nicht direkt auf die Frage antworte: Bist du sicher, dass dein GUI-Toolkit keine Funktionalität für's Threading mitbringt? Denn überall, wo ein "fremder" Mainloop im Spiel ist, sollte man eher nicht mit Python-eigenem Threading dazwischen pfuschen...
BlackJack

@Faba: Vor den Quelltexten sagst Du es funktioniert, und danach es funktioniert nicht. Was denn nun?

Ob die Methode selbst, oder eine die nur die Methode aufruft, als `command` für die Schaltfläche hinterlegst, dürfte keinen Unterschied machen. Du schreibst, Du würdest da „noch weitere Aktionen durchführen” — vielleicht liegt ja darin ein Problem?

Es wäre grundsätzlich günstiger wenn Du minimal lauffähige Beispiele zeigst, die das Problem auch tatsächlich aufweisen, anstelle von vereinfachtem Quelltext, den wir a) nicht ausprobieren können, und wo b) vielleicht genau die entscheidenden Stellen fehlen könnten. Also hier zum Beispiel die beiden Klassen in einem Fragment in umgekehrter Reihenfolge, Schaltflächen mit Texten statt Bildern aus externen Dateien usw.

(@snafu: Es sieht nach `Tkinter` aus.)
deets

Funktionier fuer mich in etwas vereinfachter Form einwandfrei:

Code: Alles auswählen


from threading import Thread, Event
import time

class Worker(Thread):

    def __init__(self):
        super(Worker, self).__init__()
        self.event = Event()


    def start_recording(self):
        self.event.set()


    def stop_recording(self):
        self.event.clear()



    def run(self):
        while True:
            self.event.wait()
            print "recording"
            time.sleep(.25)



w = Worker()
w.start()

time.sleep(2)
w.start_recording()
time.sleep(2)
w.stop_recording()
time.sleep(2)
w.start_recording()
time.sleep(5)
BlackJack

Ich habe es mal mit GUI gemacht: Funktioniert auch.

Code: Alles auswählen

import threading
import time
import Tkinter as tk


class Recorder(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.recording_event = threading.Event()

    def start_recording(self):
        self.recording_event.set()

    def stop_recording(self):
        print 'stop'
        self.recording_event.clear()

    def record(self, chunk_size, format_, rate):
        while True:
            self.recording_event.wait()
            print 'aufnehmen...'
            time.sleep(1)

    def run(self):
        self.record(1024, None, 44100)


class GUIFrame(tk.Frame):

    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.recorder = Recorder()
        self.recorder.setDaemon(True)
        self.recorder.start()
        
        self.start = tk.Button(self, text='Start', command=self.record)
        self.start.pack(side=tk.LEFT)
        
        self.stop = tk.Button(self, text='Stop', command=self.stop)
        self.stop.pack(side=tk.LEFT)
    
    def record(self):
        self.recorder.start_recording()
    
    def stop(self):
        self.recorder.stop_recording()


def main():
    root = tk.Tk()
    gui_frame = GUIFrame(root)
    gui_frame.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
Edit: Widgets sollten sich in der `__init__()` nicht selbst „layouten”. Damit nimmt man dem Aufrufer/Benutzer die Möglichkeit zu entscheiden wie das Element verwendet werden soll.
Faba
User
Beiträge: 5
Registriert: Dienstag 7. Oktober 2008, 15:24

Hallo,
habe den Fehler jetzt gefunden. Der Fehler war, dass ich in der GUIFrame-Klasse die Methode nicht stop nennen darf. Nachdem ich sie umbenannt habe, geht's.
Vielen Dank für die schnelle Hilfe, das war super!!!
deets

@Faba

du weisst aber, *warum* du das Problem hattest, oder?
BlackJack

@deets: Hihi, das Problem habe ich bei mir „aus versehen” umgangen. :-D
deets

@BlackJack

Ja, ist mir auch schon aufgefallen ;)
Faba
User
Beiträge: 5
Registriert: Dienstag 7. Oktober 2008, 15:24

Ist wahrscheinlich ein reservierter Methodenname... oder ein reserviertes Wort...
Benutzeravatar
pillmuncher
User
Beiträge: 1532
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Faba hat geschrieben:Ist wahrscheinlich ein reservierter Methodenname... oder ein reserviertes Wort...
The Zen of Python meint dazu:

In the face of ambiguity, refuse the temptation to guess.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@Faba: Schau doch einfach mal welche Attribute *Du* alle `stop` genannt hast. Und/oder lass Dir doch mal mit ``print`` an den verschiedenen Stellen im Programm ausgeben was an `self.stop` gebunden ist.
Faba
User
Beiträge: 5
Registriert: Dienstag 7. Oktober 2008, 15:24

oh man, manchmal ist man echt zu blöd für die einfachsten Sachen und gucken kann man auch nicht -.- Ein Glück programmiert man ja nicht schon seit 7 Jahren... -.- Danke trotzdem!
Antworten