Tkinter und Multiprocessing,wie infinite loop beenden?

Fragen zu Tkinter.
Antworten
fach1fach
User
Beiträge: 2
Registriert: Freitag 22. Dezember 2017, 18:59

Hallo an alle,

ich bin noch neu im Programmieren und versuche gerade eine GUI in Python, auf dem Raspberry Pi zu schreiben, welche eine Reihe von LED-Leisten ansteuern soll. Dabei soll auch eine Funktion integriert werden die die Leisten, in einer vom Nutzer angegebenen Zeit (ms), blinken lässt. Diese versuche ich über das Multiprocessing Modul in einen eigenen Prozess auszulagern. Dieser soll dann in einem unendlichen Loop laufen, bis der User den "Aplly Blink Change's" Button erneut betätigt, daraufhin soll der laufende Blinkprozess beendet und ein neuer gestartet werden.
Mein Problem liegt nun darin den gestarteten Prozess zu beenden. Ich denke, dadurch das ich versuche den Prozess durch ein erneutes aufrufen der Klasse Blink zu beenden, eine neue Instanz dieser erschaffen wird und diese dann keinen Zugriff mehr auf den gestarteten, ersten Prozess hat.
Ich habe versucht auf den Prozess über den, beim print() Befehl angegebenen Namen und über die ID(pip) zuzugreifen, beides ohne Erfolg. Die Frage ist nun gibt es eine Möglichkeit einen spezifischen Prozess zu terminieren (oder alle, außer dem mainloop) oder ist mein ganzer Ansatz einfach Quark?

Mir ist bewusst das python für zeitkritische Sachen nicht die beste Wahl ist aber da ich mit dieser Sprache nun begonnen habe, würde ich wenn Möglich auch erst einmal bei ihr bleiben, bis ich mich wenigstens einigermaßen eingelebt habe.
Schon mal vielen Dank für alle Antworten.

Gruß Paula

Code: Alles auswählen

import tkinter as tk
import wiringpi as wp
import time
from Blink_Process import Blink



pins ={'pin_R1':11, 'pin_G1':12, 'pin_B1':13}

wp.wiringPiSetupPhys()
for i in pins:
    wp.pinMode(pins[i],1)
    wp.softPwmCreate(pins[i],0,255)
    

#---------------------------------------------------------class's
class LabeledEntry(tk.Entry):
    def __init__(self, master = None, label = 'Platzhalter',**kwargs):
        tk.Entry.__init__(self, master, **kwargs)
        self.label = label
        self.on_exit()
        self.bind('<FocusIn>', self.on_entry)
        self.bind('<FocusOut>', self.on_exit)
    
    def on_entry(self, event = None):
        if self.get() == self.label:
            self.delete(0, tk.END)
                  
    
    def on_exit(self, event = None):
        if not self.get():
            self.insert(0, self.label)
            

#---------------------------------------------------------def's
def apply_RGB_Var():
    duty_cycle = {'r1':100,'r2':100,'r3':100,'r4':100,'r5':100,'r6':100,'r7':100,'r8':100,'r9':100,'r10':100,
                  'g1':100,'g2':100,'g3':100,'g4':100,'g5':100,'g6':100,'g7':100,'g8':100,'g9':100,'g10':100,
                  'b1':100,'b2':100,'b3':100,'b4':100,'b5':100,'b6':100,'b7':100,'b8':100,'b9':100,'b10':100}
    
    duty_cycle ['r1'] = entry_row1_red.get()
    duty_cycle ['g1'] = entry_row1_green.get()
    duty_cycle ['b1'] = entry_row1_blue.get()
    
    
    
    for key, value in duty_cycle.items():
        if type(value) is str:
            if  value.isdigit():
                duty_cycle[key] = int(value)
            else:
                duty_cycle[key] = 100
    
    for key, value in duty_cycle.items():
        if type(value) is int:
            if value > 255:
                duty_cycle[key] = 255
            
        
    
    print(duty_cycle)
    
    wp.softPwmWrite(pins['pin_R1'], duty_cycle ['r1'])
    wp.softPwmWrite(pins['pin_G1'], duty_cycle ['g1'])
    wp.softPwmWrite(pins['pin_B1'], duty_cycle ['b1'])


def run_blink():                                        #die Funktion die den Blinkprozess starten soll
    on_time = entry_row1_on.get()
    off_time = entry_row1_off.get()
    on_time = int(on_time)                     
    off_time = int(off_time)
    global first_run
    blink = Blink(on_time, off_time)
    
    if first_run:
        if on_time | off_time != 0:
            blink.start()
        first_run = False
        
    else:
        blink.stop()
            
            
first_run = True

#---------------------------------------------------------main Window

main_window = tk.Tk()

label_row1 = tk.Label(main_window, text = 'LED-Reihe 1: ')
label_row1.grid(row = 2, sticky = 'W')

label_row1_red = tk.Label(main_window, text = 'Red')
label_row1_red.grid(row = 2, column = 2)
entry_row1_red = LabeledEntry(main_window, width = 5, label = '0-255' )
entry_row1_red.grid(row = 2, column = 3)

label_row1_green = tk.Label(main_window, text = 'Green')
label_row1_green.grid(row = 3, column = 2)
entry_row1_green = LabeledEntry(main_window, width = 5, label = '0-255')
entry_row1_green.grid(row = 3, column = 3)

label_row1_blue = tk.Label(main_window, text = 'Blue')
label_row1_blue.grid(row = 4, column = 2)
entry_row1_blue = LabeledEntry(main_window, width = 5, label = '0-255')
entry_row1_blue.grid(row = 4, column = 3)

label_row1_on = tk.Label(main_window, text = 'On-Time')
label_row1_on.grid(row = 5, column = 2)
entry_row1_on = LabeledEntry(main_window, width = 5, label = '0')# muss noch in 'in ms' geändert werden, kein int, lösung finden
entry_row1_on.grid(row = 5, column = 3)

label_row1_off = tk.Label(main_window, text = 'Off-Time')
label_row1_off.grid(row = 6, column = 2)
entry_row1_off = LabeledEntry(main_window, width = 5, label = '0')
entry_row1_off.grid(row = 6, column = 3)

apply_row1 = tk.Button(main_window, text = "Apply Color Change's", command = apply_RGB_Var)
apply_row1.grid(row = 3, column = 0)

apply_row1_blink = tk.Button(main_window, text = "Apply Blink Change's", command = run_blink)  # der Button der die Blinkfunktion ausführt
apply_row1_blink.grid(row = 4, column = 0)

apply_to_all = tk.Button(main_window, text = 'Apply to all!')
apply_to_all.grid(row = 5, column = 0)



#---------------------------------------------------------advanced option's window's

#---------------------------------------------------------
main_window.title('Beleuchtungssteuerung KHO 1.1')
main_window.mainloop()

for i in pins:
    wp.digitalWrite(pins[i],0)
    wp.pinMode(pins[i],0) 

Code: Alles auswählen

class Blink():
    
    def __init__(self, on_time, off_time):
        self.pool = None
        self.on_time = Value('i',on_time)
        self.off_time = Value('i',off_time)
        self.do_blink = Value('b', True)
        self.proc_blink = Process(target =self.blink)
        
           
    def blink(self):
        print(self.proc_blink.pid)
        self.x = self.proc_blink.pid
        self.x.terminate()
        while self.do_blink:
            print('on: ')
            time.sleep(1)
            print('off: ')
            time.sleep(1)
           
    def start(self):
        self.proc_blink.start()
                
    def stop(self):
        self.do_blink.value = False
        self.proc_blink.terminate()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@fach1fach: das Programm tut aus vielen Gründen nicht. Im Modul »Blink_Process« sind »Value« und »Process« nicht definiert. »self.x« ist eine Zahl und hat daher keine »terminate«-Methode. Im Hauptprogramm sollten die Zeilen 10-13 in eine Funktion »startup« und alles ab Zeile 85 in eine Funktion »main«, die man dann am Ende über ein »if __name__ == '__main__': main()« aufruft.

Zeile 10: wenn Du nur die Werte brauchst, schreibst Du »for pin in pins.values:«. »LabeledEntry« funktioniert so nicht, wie willst Du einen tatsächlichen Wert vom Platzhalter unterscheiden? Da Du schon Labels benutzt, ist so ein Platzhalter auch total überflüssig. Konzentrier Dich auf die wirkliche Funktionalität und mach solche Spielereien nur, wenn alles andere funktioniert und sie wirklich sinnvoll sind.

Zeile 16, 35 und 87: die Kommentare sind überflüssig. Die vielen - machen den Text dahinter unlesbar, und der wiederholt nur offensichtliches. Zeile 87 sollte dann auch besser eine Funktion einleiten, die dann genausoviel aussagt, wie der Kommentar.

`apply_RGB_Var` macht im Moment noch nicht viel. Bedenke, dass Wörterbücher unsortiert sind, wenn Du also druchnummerierte Schlüssel hast, ist es vielleicht sinnvoller, statt dessen eine Liste zu nehmen. Eine Funktion sollte alles, was sie braucht über Parameter bekommen, entry_row1_red, entry_row1_green, entry_row1_blue kommen aus dem Nichts. Für GUIs sind daher eigentlich Klassen unerlässlich. Statt »type(a) is b« nimmt man »isinstance(a, b)« und auch das sollte man vermeiden. Du weißt doch, dass die drei Entry-Werte Strings sind, die Du schon bei der Zuweisung in Zahlen umwandeln kannst. Die zwei for-Schleifen könnte man auch in einer machen.

Zeile 73: vergiß dass es »global« gibt. Wie Du schon festgestellt hast, bringt Dir das hier auch gar nichts, weil Du mehrere Blink-Exemplare erzeugst. Multiprocessing ist hier auch der falsche Weg. Wenn ich das richtig verstanden habe, reicht Dir das »after« von Tkinter.

Zeile 136ff: das Aufräumen sollte auf jeden Fall passieren, daher packt man so etwas in einen finally-Block und die Hauptroutine davor in den dazugehörigen try-Block.
fach1fach
User
Beiträge: 2
Registriert: Freitag 22. Dezember 2017, 18:59

@Sirius3: Danke erstmal für Deine Antwort. So Stück für Stück.
Beim reinkopieren des Codes von Blink_Process war ich unaufmerksam und habe nicht alles erwischt. Process und Value habe ich natürlich importiert. Self.x ist eine weitere Unaufmerksamkeit meinerseits, sorry. Ich habe versucht den Prozess über seine ID zu beenden, was natürlich nicht geht, da es, wie Du richtig sagst nur ein Integer ist, leider auch vergessen zu löschen. Dafür möchte ich mich erst einmal entschuldigen, ich hätte sorgfältiger sein sollen, schließlich geht es mir ja um diesen Teil.

Zeile 10-13 in eine >>startup<< Funktion und alles ab Zeile 85 in >>main<<, zu packen leuchtet mir ein. Allerdings habe ich zu >>if __name__ == '__main__':<< noch die Frage, ob das Ganze noch eine andere Funktion erfüllt, als dafür zu sorgen das der nachfolgende Code nur läuft, wenn die Datei als Hauptprogramm ausgeführt wird. Man diese also auch in andere Programme importieren kann um mit den beinhalteten Klassen zu arbeiten ohne die >>main()<< Funktion auszuführen.

Zu >>for pin in pins.values:<< ,wäre das denn so richtig?

Code: Alles auswählen

wp.wiringPiSetupPhys()
for pin in pins.value:
    wp.pinMode(pin,1)
    wp.softPwmCreate(pin,0,255)
>>LabelEntry<< gefällt mir auch nicht. Die Funktion ist aus dem Netz und ich verstehe sie nur partiell. Der Grund warum ich versuche sie zu integrieren ist der das ich später ein Platzproblem haben werde. Es sind insgesamt 10 RGB-Leisten, die alle individuell angesteuert werden sollen und der Touch-Bildschirm der Steuereinheit hat nur eine Diagonale von 3,5 Inch. Es wird also relativ schnell eng und es wäre daher fein einen Teil der Informationen gleich im Eingabefeld darzustellen. Das Problem die Platzhalter von den Userwerten zu unterscheiden habe ich versucht durch die beiden for-Schleifen zu lösen und sie sollen auch verhindern das das Programm abschmiert wenn der User Quatsch einträgt. Beide in eine for-Schleife zu integrieren ist eine gute Idee und Übung für mich. Mache ich heute Abend nach meiner Spätschicht.

>>apply_RGB_Var<< macht im Moment wirklich nicht viel, ich möchte diese später mit der Blink-Funktion und einer Möglichkeit auf die Übergänge beim Blinken Einfluss zunehmen, verbinden. Soweit ich das verstanden habe, kann ein tKinter Button immer nur eine Funktion aufrufen und es wäre fein wenn die Farb-, Blink- und Fadingeinstellungen über einen "Apply" Button ausgeführt werden. Wieder des Platzes wegen.
Das Wörterbücher unsortiert sind ist mir klar, mein Gedanke war das es für mich leichter ist nachzuvollziehen welche Werte sich, wegen irgendetwas, verändert haben, als das in einer Liste aus Werten für mich der Fall wäre. Auch wollte ich die An- und Auszeiten der Blinkfunktion und die Fading-Werte(hab noch keine Vorstellung wie diese, eventuelle Fadingfunktion später aussieht) in dieses einfügen. Man könnte das Wörterbuch dann einfach in einer Datei speichern und so verschiedene Profile anlegen.

Die GUI werde ich versuchen in eine Klasse zu packen und den Funktionen die Werte als Parameter übergeben. Ich bin noch recht unsicher mit Klassen aber das wird schon.

Die >>global<< habe ich nur benutzt weil es die einzige Möglichkeit war, die ich finden konnte, festzustellen ob eine Funktion zum ersten Mal ausgeführt wird. Beim ersten Durchlauf sollte sie nur einen Prozess starten, bei den darauf folgenden dann den alten Prozess schließen und einen neuen starten. Gibt es da noch eine andere?

Die >>after<< Methode habe ich versucht und sie hat, bis zu einem gewissen Grad, auch funktioniert. Das Problem ist das ich recht "hohe" Frequenzen erreichen mag, so um die 25 Hz. Dabei stellt sich dann, recht schnell, ein nicht periodischer Flimmern ein. Daher würde ich gerne die Blinkfunktion in einen eigenen Prozess auslagern um die höhst mögliche, stabile Frequenz erreichen zu können. Mir würde noch einfallen Python irgendwie komplett zu beenden, die Werte zu speichern und dann mit diesen, neu zu starten. Ein kurzer Schluckauf wäre nicht weiter tragisch. Als letzte Möglichkeit gebe es noch die Hardwareseitige Auslagerung des Blinkens aber dann bräuchte ich 10 zusätzliche Pins und damit einen zweiten Raspberry Pi oder Arduino.

Ich werde erstmal meinen Code bezüglich deiner Anmerkungen umschreiben und wenn jemanden noch was Einfällt, würde mich Freuen! Die nächsten Tage sind leider recht voll daher wünsche ich allen schon mal einen guten Rutsch!

Gruß Paula

Code: Alles auswählen

from multiprocessing import Process, Value
import time

class Blink():
    
    def __init__(self, on_time, off_time):
        self.pool = None
        self.on_time = Value('i',on_time)
        self.off_time = Value('i',off_time)
        self.do_blink = Value('b', True)
        self.proc_blink = Process(target =self.blink)
           
    def blink(self):
        while self.do_blink:
            print('on: ')
            time.sleep(1)
            print('off: ')
            time.sleep(1)
           
    def start(self):
        self.proc_blink.start()
                
    def stop(self):
        self.do_blink.value = False
        self.proc_blink.terminate()
Antworten