Schleife beenden/abbrechen

  • Hallo Zusammen,


    ich bin momentan am überlegen, mir eine Bewässerungsanlage zu bauen.
    Hier stehe ich aber noch vor einem Problem das mich "quält".


    Ich sage gleich vorweg, ich habe noch keinen Code, ich überlege gerade wie ich das ganze angehen / lösen könnte.


    Zu meinem Frage:
    Ich plane das ganze in Tkinter umzusetzen.
    Es soll einen Button und eine Scala (Schieberegler) geben, mit dem ich die Zeit einstellen kann und per Button die Bewässerung starten kann.


    Wenn ich jetzt zB. einstelle, dass 10 Min bewässert werden soll, und ich merke nach ein paar Minuten, dass es schon reicht, wie kann ich dann das Bewässern abbrechen/beenden, ohne das ich das gesamte Script abbrechen muss?
    Quasi "nur" in die Schleife eingreifen in der die eingestellte Zeit zum bewässern eingestellt ist.


    Ganz kurz gesagt. Ich klicke auf den Button, Bewässerung startet bis zur vorgegebenen Zeit. Oder ich klicke nochmal auf den Button und die Bewässerung wird vorzeitig beendet.


    Ich hoffe es ist verständlich geschrieben.


    Danke

  • In dem du einfach ein "Tag"/Variable setzt und dann die bewässern schleife abbrichst


    Code
    1. while bewaesserung_an ==True:
    2. #bewaessere
    3. #dann setzt du einfach bewaesserung_an auf False.


    Ohne Code schlecht zu erklären, ich hoffe es ist klar was ich meine ;)

    Der Unterschied zwischen Genie und Wahnsinn definiert sich im Erfolg.

    Einmal editiert, zuletzt von dbv ()

  • Mir ist klar was du meinst :)


    Nachdem würde dann quasi nur noch eine IF Abfrage kommen, mit der eingestellten Zeit:
    IF zaehler < eingestellte_zeit:
    script
    zaehler +1


    Dann hätte ich quasi alles enthalten. Die Schleife kann abgebrochen werden und / oder die Zeit läuft ab.
    Oder habe ich gerade einen Denkfehler mit dabei?

  • Zähler und Zeiten passen nicht zusammen ;). Das würde ich mit python datetime objekten machen.


    Code
    1. #bewaesserungszeit setzen
    2. now_plus_10 = datetime.datetime.now() + datetime.timedelta(minutes = 10)
    3. while datetime.datetime.now() < now_plus_10:
    4. while bewaesserung_an ==True:
    5. #bewaessere
    6. #hier notfalls now_plus_10 auf kleiner now setzen

    Der Unterschied zwischen Genie und Wahnsinn definiert sich im Erfolg.

    Einmal editiert, zuletzt von dbv ()

  • Danke für deinen Vorschlag!


    Bei der eingestellten Zeit würde es sich um Sekundenangaben handeln.


    Also bei dem Schieberegler wird nur eine Zahl abgefragt. Die Zahl sollte dann einfach eingefügt werden. Somit fungiert diese als "Zähler".
    Mit einem time.sleep(1) könnte ich dann eigentlich die genauen Sekunden "abzählen".
    Könnte das dennoch so klappen?


    Ich werde auf jeden Fall auch deine Variante berücksichtigen.
    Danke hierfür!

  • Ich habe das ganze jetzt mal angefangen umzusetzten.
    Was mir gleich relativ früh aufgefallen ist, wenn ich eine "while" Schleife verwende die auf True oder auf einen Wert schaut, dann trifft dies immer zu und ich stecke in der Schleife fest.


    Ist mir ziemlich deutlich in der Schieberegler schleife aufgefallen.


    Ich versuche das ganze jetzt in Tkinter mit der after Methode umzubauen.


    Hier ist der alte Code, mit dem ich in der Schleife festgesteckt bin.. Ich teste das ganze momentan mit einer LED damit es hoffentlich schneller geht.



    Hier wird quasi die eingestellte Zeit abgewartet. Erst dann, geht es wieder weiter.


    Ein teste, mit dem ich aber erst heute Abend testen kann, wäre die after Methode.
    Wie gesagt ist ungetestet!
    Es muss auch noch eine Abfrage eingebaut werden, damit die Schleife vorher geprüft wird. Ähnlich wie von dbv mit while running = True und bei Beenden mit False.


    Hier der neue ungetestete Code:


    Hier ist übrigens der ganze Code.
    https://github.com/wusa88/Bewaesserung/blob/master/Code


    Momentan noch auf LED Basis. Soll aber sobald dies funktioniert auf die Bewässserung umgebaut werden.

  • "after" kannst du nur auf das tk.Tk() Objekt anwenden.


    Dein Code ist ein kleinwenig Chaotisch. Setze "global" am Anfang der Funktion, nicht irgendwo mitten drin. Geize mit Leerzeichen nicht an den falschen Stellen :fies:


    Dein Code soll für Python3 sein? Gibts dafür ein Grund? ;)


    Es wär gut das gesamte Konstrukt zu sehen um die Zusammenhänge auch nachvollziehen zu können. Mit den 2 von dir gezeigten Funktionen kann zumindest ich nicht allzu viel anfangen.

  • Das es Python3 ist, hat keinen besonderen Grund. Habe mir mal Python2 und 3 angesehen und wollte dann versuchen alles in Python3 zu machen.
    Ob ich das natürlich immer schaffe, ist eine andere Frage ;)


    Ich habe den Code nochmal kurz bearbeitet und hier ist der gesamte Code zu sehen:
    https://github.com/wusa88/Bewaesserung/blob/master/Code

  • Hm, was mir gleich auffällt:
    Du nutzt zwar die Standard Struktur für Python Scripts, verwendest main() aber nicht seiner Funktionsweise entsprechend: Du initialisierst "root" usw außerhalb
    "btn_exit" in hauptmenue() sollte lieber auf eine extra Funktion zeigen (also als command) in der du dann das Script sauber beenden kannst, also auch die GPIO's zurücksetzen usw.
    bewaesserung = "True" ... Was ist das? Willst du nicht lieber True nutzen anstatt eines Strings "True" ? Das gleiche würde ich mit led_status und scale_vorhanden machen -> True für An/Ja und False für Aus/Nein.
    warten.after(1000,led_automatisch) ... wird wie gesagt IMHO nicht funktionieren, "warten" wurde nirgends definiert und es müsste wenn dann "root" sein, oder "led"...
    Es wird auch eine Fehlermeldung geben sobald du auf den "LED" Button drückst, nämlich weil du led_toggle früher zuweißt als die Funktion definiert wurde.
    Auch hast du noch ein Verständnissproblem mit "after". So wie du es anwendest wird in deiner while ständig ein neuer Thread für "led_automatisch" gestartet und somit drehen sich irgendwann etliche Schleifen im Kreis - ich glaub nicht dass das gewollt ist :-/
    Wieso du 2 verschiedene Fenster öffnest versteh ich zZt auch nicht :s
    Für "Setzen" brauchst du eigentlich auch keine extra Funktion denk ich, da könnte man direkt "led_automatisch" aufrufen.
    Und zu guter letzt nutzt du mehrfach place(x=340, y=20) was auch zu Problemen führen wird :fies:


    Also erst mal würde ich die Grundstruktur des Scripts etwas umbauen. Eine Class wäre denk ich das einfachste, weil man sich dann auch nicht um explizites "global" setzen kümmern muss. Klingt vielleicht kompliziert, ist es aber eigentlich nicht ;)
    Grundgerüst:


    => http://codepad.org/rB3hlVea


    Das entspricht erstmal deinem obigen Code und funktioniert soweit.
    Es fehlt aber noch die von dir erfragte Möglichkeit die Schleife in "led_automatisch" frühzeitig zu unterbrechen. Das Problem damit ist allerdings das die while das Script blockiert, die GUI reagiert also auf keinen Klick. Eine Lösung dafür wäre einen separaten Thread/Process dafür zu starten und über einen Button eine Variable zu ändern die wiederum in der ausgelagerten while Schleife geprüft wird...

  • Erstmal vielen lieben Dank das du dir so viel Zeit nimmst. Und auch Respekt was du sonst so alles machst. Danke !



    Hm, was mir gleich auffällt:
    Du nutzt zwar die Standard Struktur für Python Scripts, verwendest main() aber nicht seiner Funktionsweise entsprechend: Du initialisierst "root" usw außerhalb
    "btn_exit" in hauptmenue() sollte lieber auf eine extra Funktion zeigen (also als command) in der du dann das Script sauber beenden kannst, also auch die GPIO's zurücksetzen usw.


    Das ganze hatte ich Anfangs versucht, leider aber nicht auf Anhieb geschafft. Das mit dem GPIO Cleanup war mir auch noch ein Dorn im Auge. Daran wollte ich mich noch versuchen.



    bewaesserung = "True" ... Was ist das? Willst du nicht lieber True nutzen anstatt eines Strings "True" ? Das gleiche würde ich mit led_status und scale_vorhanden machen -> True für An/Ja und False für Aus/Nein.
    warten.after(1000,led_automatisch) ... wird wie gesagt IMHO nicht funktionieren, "warten" wurde nirgends definiert und es müsste wenn dann "root" sein, oder "led"...
    Es wird auch eine Fehlermeldung geben sobald du auf den "LED" Button drückst, nämlich weil du led_toggle früher zuweißt als die Funktion definiert wurde.


    Das ganze Script habe ich gestern "schnell" auf Lesbarkeit umkopiert ohne das nochmal zu testen.
    Ich weiß nicht ob man in Github die vorhergehenden kompletten Scripte sehen kann und nicht nur die Änderungen.
    Es war nämlich alles in anderer Reihenfolge. Die auch direkt funktioniert hat ;)


    Auch hast du noch ein Verständnissproblem mit "after". So wie du es anwendest wird in deiner while ständig ein neuer Thread für "led_automatisch" gestartet und somit drehen sich irgendwann etliche Schleifen im Kreis - ich glaub nicht dass das gewollt ist :-/


    Die after Methode wollte ich eigentlich dafür verwenden, damit das Script nicht in der Schleife hängen bleibt. Was ich aber jetzt gelernt habe, dass diese so nicht funktionieren kann.



    Wieso du 2 verschiedene Fenster öffnest versteh ich zZt auch nicht :s


    Das hat eigentlich den Sinn, da ich noch mehrere Buttons mit verschiedenen Funktionen einbauen will. So kann ich dann verschiedene Sachen, in verschiedenen Fenstern aufrufen.
    Somit ist jedes Fenster/Button für seine eigene Aufgabe zuständig. Zumindest Optisch.



    Das entspricht erstmal deinem obigen Code und funktioniert soweit.
    Es fehlt aber noch die von dir erfragte Möglichkeit die Schleife in "led_automatisch" frühzeitig zu unterbrechen. Das Problem damit ist allerdings das die while das Script blockiert, die GUI reagiert also auf keinen Klick. Eine Lösung dafür wäre einen separaten Thread/Process dafür zu starten und über einen Button eine Variable zu ändern die wiederum in der ausgelagerten while Schleife geprüft wird...


    Diese Möglichkeit wollte ich mit einer weiteren Schleife in einer Schleife lösen.
    Hierfür war die Variable "bewaesserung". Ich wollte quasi Abfragen

    Code
    1. while bewaesserung == True
    2. while wert < scale_wert


    Auf diese Weise wollt ich dann in die Schleife eingreifen. Wie ich das allerdings anstellen kann, muss ich mir noch überlegen.


    Ich werde mir aber die Möglichkeit auch mal ansehen, in Tkinter, Threads einzubinden.


    Nochmal vielen Dank für deine Hilfe.
    Ich werde heute Abend sofort dein Script testen.


  • Das hat eigentlich den Sinn, da ich noch mehrere Buttons mit verschiedenen Funktionen einbauen will. So kann ich dann verschiedene Sachen, in verschiedenen Fenstern aufrufen.
    Somit ist jedes Fenster/Button für seine eigene Aufgabe zuständig. Zumindest Optisch.


    Wenn alle "Fenster" die selbe Größe bzw Auflösung haben könntest du auch einfach mehrere Frames erzeugen die dann je nach Bedarf in den Vordergrund geschaltet werden. Dadurch muss das jeweilige Fenster nicht destroyed werden. Siehe dazu:
    http://stackoverflow.com/quest…een-two-frames-in-tkinter
    Und die Erklärung zu dem Code: http://stackoverflow.com/quest…ding-how-to-switch-frames



    Diese Möglichkeit wollte ich mit einer weiteren Schleife in einer Schleife lösen.
    Hierfür war die Variable "bewaesserung". Ich wollte quasi Abfragen

    Code
    1. while bewaesserung == True
    2. while wert < scale_wert


    Auf diese Weise wollt ich dann in die Schleife eingreifen. Wie ich das allerdings anstellen kann, muss ich mir noch überlegen.


    Wie gesagt, die erste Schleife (generell eine while Schleife) blockiert bereits das gesamte Script. In einem Thread kann auch nur eines bearbeitet/abgearbeitet werden. Um parallel mehrere Sachen behandeln zu können bedarf es mehrerer Threads oder Prozesse. Du kannst das ja mal aufprobieren indem du in der led_automatisch Funktion da wo wert += 1 steht ein sleep(1) einfügst, startest und dann versuchst während die Schleife aktiv ist auf irgendeinen Button zu drücken...


    Bei Tkinter gibts aber besondere Umstände, was viele gar nicht bedenken: Es läuft bereits eine while Schleife: mainloop
    Durch den mainloop wird after kontinuierlich überprüft, bei jedem Schleifendurchlauf wird also geprüft ob es an der Zeit ist after auszuführen. Mit der Zeitangabe (in millisekunden) legt man fest nach wie viel Zeit die in der after Methode festgelegte Funktion ausgeführt werden soll.
    Und genau das kann man hier ausnutzen.


    Du willst ja bestimmt über den Slider/Schieberegler eine Zeit in Sekunden angeben wie lange die Bewässerung laufen soll, oder? Also nutzen wir einfach die after Methode Mit 1000 Millisekunden und verzichten vollständig auf eine weitere Schleife da wir ja bereits mainloop als solche haben :) Das einzige was wir noch benötigen ist eine "Stop" Funktion um die "after" Geschichte auch frühzeitig unterbrechen zu können, aber das ist ja eigentlich Pillepalle :fies: Denn da wir selber "to" auf 200 definiert haben, brauchen wir beim "Stop" einfach nur einen Wert über 200 angeben also zB 201.


    Würde dann so aussehen:


    Vollständiger Code => http://codepad.org/HAKPPKfO


    :angel:


    Würde aber noch ein paar Optimierungen vorschlagen:
    - Nachdem "Setzen" gedrückt wurde den Button DISABLED setzen damit er nicht erneut gedrückt werden kann solange "led_automatisch" aktiv ist. Und danach natürlich wieder ENABLED ;)
    - Nachdem "Setzen" gedrückt würde den Text des rechten Buttons auf "Bewaesserung\n laeuft" ändern und nachdem das ganze beendet wurde ändern auf "Bewaesserung\n kann\n gestartet\n werden". So hättest du dann auch eine Statusanzeige in der GUI ;)