2 Funktionen gleichzeitig ausführen

Fragen zu Tkinter.
Antworten
Jnsschstr
User
Beiträge: 1
Registriert: Montag 9. Dezember 2019, 11:09

Hi,
ich bin ein ziemlicher Programmier-Anfänger und habe ein Problem mit meinem ersten Python-Projekt, welches auf einem Raspberry Pi laufen soll. Dabei handelt es sich um eine einfache Software für einen "Getränkeautomaten" Dabei soll per GUI ein Mischgetränk ausgewählt und dann durch die GPIO-Pins Relais gesteuert werden. So weit so gut, jetzt aber zu meinem (bestimmt ziemlich einfach zu lösenden) Problem. Eine Einfache GUI mit Tkinter habe ich bereits, jedoch schaff ich es nicht durch meine Google-Skills herauszufinden wie man mit einem Button 2 Funktionen (am besten Gleichzeitig) aufrufen kann. Zum einem wäre das dann die Funktion mit der GPIO-Steuerung und zum anderen wollte ich das Quasi eine 2.Wahl verhindert wird, dass ich durch das Öffnen eines 2. Frames über den Hauptframe umsetzte. Dieses 2. Frame soll dann nach dem Beenden der GPIO-Funktion auch schließen sodass das nächste Getränk ausgewählt werden kann. Da es mir unmöglich erscheint die GUI und GPIO Steuerung in einer Funktion ausführen kann habe ich im Internet Sachen zu Treading gefunden wovon ich in meiner jetzigen Lage über die Praktische Ausführung noch Bahnhof verstehe und ein Aufruf von 2 Funktionen über den command Parameter des Buttons führte bislang auch zu keinem Erfolg. Vielleicht gibt es ja auch eine simple Lösung?!

Vielleicht kann mir von euch wer helfen?!
Danke
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jnsschstr: Es gibt dafür keine einfache Lösung. Also zumindest nicht für die nebenläufige Ausführung. Da wirst Du entweder die Funktion zum steuern der Relais so zerlegen müssen, dass sie mit `after()` in einzelnen Schritten die immer nur kurz etwas tun, ausgeführt wird, oder das `threading`-Modul mit `after()` und einer `queue.Queue` kombinieren müssen. Wichtig bei Threads: Die dürfen die GUI nicht verändern, das darf nur im Hauptthread in dem die GUI-Hauptschleife läuft passieren.

„Frame öffnen“ ist eine komische Formulierung, denn ”öffnen” bezieht sich bei GUIs auf Fenster, also `Tk` für das Hauptfenster und `Toplevel` für weitere Fenster. Man kann mit dem `grid()`-Layout mehrere Elemente übereinander legen und immer eines davon nach vorne holen, so das es die anderen verdeckt. Das wäre in der Tat eine Lösung. Oder man deaktiviert einfach die Eingabeelemente solange sie nicht bedient werden können sollen. Oder man macht einen modalen Dialog auf der den Benutzer informiert das gerade ein Getränk gemischt wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
pyranha
User
Beiträge: 4
Registriert: Mittwoch 4. Dezember 2019, 17:13

Servus zusammen,

eventuell kann man das auch mit einer Art Wrapper lösen. Damit könnte man dann jede Methode die nebenläufig ausgeführt werden soll mit einem Klassendekorator in einen eigenen Thread packen. In etwa so:

Code: Alles auswählen

from threading import Thread
import tkinter as tk

class ThreadWrapper:
	def __init__(self, func):
		self.function = func
		
	def __get__(self, instance, owner):
		self.calling_instance = instance
		return self
		
	def __call__(self):
		new_Thread = Thread(target=self.function, args=(self.calling_instance, ))
		new_Thread.start()
		print('Neuer Thread gestartet')			#diese Zeile ist nur zu Demo-Zwecken
		
class GUI():
	def __init__(self):
		self.window = tk.Tk()
		self.button = tk.Button(self.window, text='Klick mich!', command=self.do_something)
		self.button.pack()
		
	def run(self):
		self.window.mainloop()
		
	@ThreadWrapper										#die folgende Methode wird in einen eigenen Thread gepackt
	def do_something(self):
		#Programmblock der nebenläufig ausgeführt werden soll
		pass

if __name__ == '__main__':
	new = GUI()
	new.run()
		
Ich weiss allerdings nicht, ob das so eine gute Idee ist bzw. ob Dir das weiter hilft. Vermutlich gibt es auch bessere und simplere Lösungen. :wink:
"He who laughs most, learns best" - John Cleese
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist nicht gut. Statt sich *global* eine calling-Instance zu merken, solltest du in __get__ einen Wrapper zurueckgeben. Und der kann dann auch gleich das zweite Problem loesen: der Thread verschwindet im Nirvana, und es ist nicht moeglich, darauf zB per join zu warten.

Der entscheidende Grund aber warum das mE dem TE nicht hilft: er hat gefragt, wie man Holz zerkleinert, und du wirfst ihm eine laufende Kettensaege zu und sagst "fang". Threads sind SCHWER. Sehr schwer. __blackjack__ hat schon gesagt, wo da im speziellen die Gefahren lauern. Der TE wird nun deinen Code nehmen, und sich in den Fuss schiessen. Oder die Haende zersaegen, um bei dem Bild zu bleiben.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@pyranha: zusätzlich zu dem, was __deets__ geschrieben hat, mit Deinem Dekorator bindest Du die Threads sehr eng an die GUI, statt sie möglichst weit davon entfernt zu halten.
Letztlich reduziert der Dekorator genau das Erzeugen eines Thread-Objekts, das man besser explizit da stehen hat.

Eingerückt wird in Python immer mit 4 Leerzeichen pro Ebene, keine Tabs.
Benutzeravatar
pyranha
User
Beiträge: 4
Registriert: Mittwoch 4. Dezember 2019, 17:13

Servus zusammen,
__deets__ hat geschrieben: Montag 9. Dezember 2019, 17:43 Das ist nicht gut. Statt sich *global* eine calling-Instance zu merken, solltest du in __get__ einen Wrapper zurueckgeben. Und der kann dann auch gleich das zweite Problem loesen: der Thread verschwindet im Nirvana, und es ist nicht moeglich, darauf zB per join zu warten.
@_deets__ Ja, da hast Du natürlich Recht. Durch den Dekorator ist der Thread nicht referenziert.
__deets__ hat geschrieben: Montag 9. Dezember 2019, 17:43 Der entscheidende Grund aber warum das mE dem TE nicht hilft: er hat gefragt, wie man Holz zerkleinert, und du wirfst ihm eine laufende Kettensaege zu und sagst "fang". Threads sind SCHWER. Sehr schwer. __blackjack__ hat schon gesagt, wo da im speziellen die Gefahren lauern. Der TE wird nun deinen Code nehmen, und sich in den Fuss schiessen. Oder die Haende zersaegen, um bei dem Bild zu bleiben.
:lol: :lol: :lol: Sehr schönes Bild! Ich wollte freilich niemanden verstümmeln :)
@Jnsschstr: Python gefährdet die Gesundheit :lol: Nein, im Ernst, vermutlich fährst Du auf andere Art und Weise besser!
Sirius3 hat geschrieben: Montag 9. Dezember 2019, 18:34 [...] mit Deinem Dekorator bindest Du die Threads sehr eng an die GUI, statt sie möglichst weit davon entfernt zu halten.[...]
Ja, explizit ist das natürlich nicht unbedingt. Aber interessehalber (und OT) die Frage, ist die enge Bindung an die GUI ungünstig? Da die nebenläufige Methode in der GUI-Klasse steht, ist eine gewisse Nähe von vornherein gegeben!?

Schönen Abend allen zusammen!
"He who laughs most, learns best" - John Cleese
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du bist ja derjenige, der die nebenlaeufige Methode in die GUI gepackt hat. Hin *muessen* tut sie da nicht. Eine Klasse "CocktailMischer" kann ganz ohne GUI auskommen, wuerde eine Benachrichtigung ueber den zu mischenden Cocktail ueber eine Queue bekommen, und liefert ueber eine weitere den Fortschritt zurueck. Oder man separiert noch weiter, hat eine CocktailMischer-Instanz, und die hat eine Methode "update" die einfach nur regelmaessig aufgerufen werden muss. Darin stellt sie fest, ob und was getan werden muss, und tut das. Und dann kann man von aussen entscheiden, ob man die per timer oder per Thread treiben will.
Antworten