Seite 1 von 1

Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 15:38
von Faba
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...

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 15:47
von snafu
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...

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 15:51
von 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.)

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 15:57
von 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)

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 16:07
von 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.

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 16:21
von Faba
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!!!

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 16:23
von deets
@Faba

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

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 16:34
von BlackJack
@deets: Hihi, das Problem habe ich bei mir „aus versehen” umgangen. :-D

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 16:40
von deets
@BlackJack

Ja, ist mir auch schon aufgefallen ;)

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 20:29
von Faba
Ist wahrscheinlich ein reservierter Methodenname... oder ein reserviertes Wort...

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 20:44
von pillmuncher
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.

Re: Threading Event-Problem

Verfasst: Sonntag 4. März 2012, 21:26
von 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.

Re: Threading Event-Problem

Verfasst: Montag 5. März 2012, 00:22
von Faba
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!