Programm stoppt; warum?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Master_T
User
Beiträge: 1
Registriert: Samstag 4. Januar 2020, 14:54

Hallo zusammen,

ich möchte mithilfe eines Raspberry ein Buzzerspiel mit grafischer Benutzeroberfläche entwickeln.
Ich habe es schon ohne GUI probiert, dort klappt es ohne Progleme. Einige IF bedingungen und Fertig.
Nun möchte ich einen Schritt weiter gehen, und habe ein Modul gebaut, mit eingebautem Touchscreen und der möglichkeit über XLR Anschlüsse einige Buzzer anzuschließen.
Es soll über den Touchscreen die Spielernamen abfragen, und den Buzzern zuordnen und wer zuerst Buzzert soll im Display angezeigt werden.

Nun zu meinem Problem:
Wenn ich den Spielernamen eingegeben habe und den start Button der GUI drücke läuft das Programm welches die Buzzer abfragt auch ohne Probleme. Sobald ein Buzzer gedrückt wird. bleibt das Programm in der Abfrageschleife in Zeile 71 hngen, trotzdem die variable programm welches die Schleife starten und beenden soll auf false steht.

Ich habe absolut keine Ahnung woran das liegen könnte und hoffe ihr könnt mir weiterhelfen.
Hier der Code:

Code: Alles auswählen

from tkinter import * #TKinter Modul zur erstellung der GUI Importieren
import RPi.GPIO as gpio #GPIO Pins des Raspi laden
import time #Time Modul sollte klar sein ;-)

gpio.setmode(gpio.BOARD) #GPIO Modus BOARD = GPIO Pin Nummerierung wie auf dem Raspi
gpio.setwarnings(False) #Nervige Fehlermeldungen ausblenden

#Ein und Ausgänge definieren
LED1 = 29
LED2 = 31
LED3 = 33
LED4 = 37
p1 = 7
p2 = 11
p3 = 13
p4 = 15
reset = 12
global programm #Hilfsvariable um die Programmabfrage zu starten
programm = bool()
global player1_pressed
player1_pressed = bool()
global player2_pressed
player2_pressed = bool()
global player3_pressed
player3_pressed = bool()
global player4_pressed
player4_pressed = bool()

gpio.setup(reset,gpio.IN, pull_up_down=gpio.PUD_DOWN)
gpio.setup(p1,gpio.IN, pull_up_down=gpio.PUD_DOWN)
gpio.setup(p2,gpio.IN, pull_up_down=gpio.PUD_DOWN)
gpio.setup(p3,gpio.IN, pull_up_down=gpio.PUD_DOWN)
gpio.setup(p4,gpio.IN, pull_up_down=gpio.PUD_DOWN)
gpio.setup(LED1,gpio.OUT)
gpio.setup(LED2,gpio.OUT)
gpio.setup(LED3,gpio.OUT)
gpio.setup(LED4,gpio.OUT)

#GUI anlegen
fenster = Tk()
fenster.title("Buzzman")

Textbox1 = Label(text="Spielernamen eingeben:")
Textbox1.pack()

#Spielernamen abfragen
Player1 = Entry(fenster)
Player1.pack()
Player2 = Entry(fenster)
Player2.pack()
Player3 = Entry(fenster)
Player3.pack()
Player4 = Entry(fenster)
Player4.pack()





def start():
    
    
    global programm
    programm = True
    
    global player1_pressed
    global player2_pressed
    global player3_pressed
    global player4_pressed    
    
    while(programm == True):
        
        #Solbald ein Schalter gedrückt wurde und die entsprechende LED leuchtet sollen die anderen Schalter keine Auswirkung mehr auf die LEDs haben)
    #Abfrage Welche Schalter gefdckt wurde  
        if (gpio.input(p1) ==1) and (gpio.input(p2) ==0)and(gpio.input(p3) ==0) and (gpio.input(p4) ==0):
            player1_pressed = True         
  
    #Abfrage Welche Schalter gedückt wurde    
        if (gpio.input(p1) ==0) and (gpio.input(p2) ==1)and(gpio.input(p3) ==0) and (gpio.input(p4) ==0):
            player2_pressed = True
            
    #Abfrage Welche Schalter gedückt wurde   
        if (gpio.input(p1) ==0) and (gpio.input(p2) ==0)and(gpio.input(p3) ==1) and (gpio.input(p4) ==0):
            player3_pressed = True
            
    #Abfrage Welche Schalter gedückt wurde   
        if (gpio.input(p1) ==0) and (gpio.input(p2) ==0)and(gpio.input(p3) ==0) and (gpio.input(p4) ==1):
            player3_pressed = True
    #Beenden der Abfrageschleife, sobald ein Button gedrückt wurde
        if (player1_pressed == True) or (player3_pressed == True) or (player3_pressed ==True) or (player4_pressed ==True):
            programm = False
 
#LEDs und Variablen zurücksetzen 

if (gpio.input(reset) ==1):
    gpio.output(LED1, 0)
    gpio.output(LED2, 0)
    gpio.output(LED3, 0)
    gpio.output(LED4, 0)
    player1_pressed = False
    player2_pressed = False
    player3_pressed = False
    player4_pressed = False    
        

            
Start = Button(fenster, text="Start", command=start)
Start.pack()

#Abfrage zum testen
while(player1_pressed == True):
    print(player1.get())



mainloop()
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und wieder die Murmeltiere ueberall. Das wird hier tatsaechlich permanent diskutiert. Eine GUI braucht den Mainloop, um auf Benutzereignisse zu reagieren. Und eine while-Schleife blockiert das nunmal.

Wir haben hier schon $GOTT-weiss-wie-oft diskutiert wie man das richtig macht, such mal im Tkinter Unterforum oder in diesem hier nach GPIO und tkinter und Queue und after. Du wirst fuendig, ich verspreche es.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Master_T: Wenn etwas klar sein sollte braucht es auch keinen Kommentar das es klar sein sollte. Zumal mir wirklich nicht klar ist warum das `time`-Modul importiert wird, denn es wird nirgends verwendet.

Kommentare sollten dem Leser einen Mehrwert über den Code liefern. Faustregel: Nicht kommentieren *was* der Code macht, denn das steht da ja bereits als Code, sondern nur *warum* der das *so* macht, sofern das nicht klar ist. Wofür das `tkinter`-Modul da ist, sollte beispielsweise klar sein. Falls nicht, dann kann man das in der Dokumentation nachlesen. Was man durch nachlesen in der Dokumentation lernen kann, muss man nicht kommentieren.

Sternchen-Importe sind Böse™. Bei `tkinter` holt man sich damit hunderte Namen ins Modul von denen nur ein kleiner Bruchteil tatsächlich verwendet wird. Und nicht nur die Namen die in `tkinter` selbst definiert werden, sondern auch alles was im `tkinter` selbst von woanders importiert wird.

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

``global`` hat in einem sauberen Programm nichts zu suchen. Auf Modulebene hat das nicht einmal einen Effekt. Da sollten wie gesagt auch gar keine Variablen existieren. Alles was eine Funktion oder Methode ausser Konstanten benötigt, sollte als Argument(e) übergeben werden und Ergebnisse als Rückgabewert an den Aufrufer zurückgegeben werden oder bei Methoden an das Objekt gebunden werden.

Man nummeriert keine Namen durch. Dann will man sich entweder bessere Namen ausdenken oder gar keine Einzelnamen sondern eine Datenstruktur für alle Werte verwenden. Oft eine Liste. Zum Beispiel für die Tasten-Pins und die LED-Pins.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Statt „Nervige Fehlermeldungen ausblenden“ sollte man besser die Ursache beheben. Also zum Beispiel sicherstellen das egal wie das Programm endet die `gpio.cleanup()`-Funktion aufgerufen wird.

``bool()`` ist nicht die verständlichste Art `False` zu schreiben.

Die erste Zuweisung an `programm` im Hauptprogramm wird nirgends verwendet, warum ist das nicht einfach lokal in `start()` definiert?

Das `Label` braucht gar keinen Namen, weil es später nicht mehr verwendet wird.

Um Bedingungen bei ``if`` und ``while`` gehören keine unnötigen Klammern.

Die Abfrage des Reset-Pins macht an der Stelle wo es steht keinen Sinn.

`programm` in `start()` kann man sich sparen. Da schreibt man einfach eine ”Endlosschleife” die an der entsprechenden Stelle mit ``break`` verlassen wird.

Der Kommentar ``#Abfrage Welche Schalter gefdckt wurde`` ist nur ein Zeichen von was unanständigem entfernt. 😜 Die folgenden Kommentare lassen stark vermuten das da viel Code durch kopieren und einfügen entstanden ist — das macht man nicht. Es ist in der Regel ein Zeichen dafür das man eigentlich eine Schleife und oder Funktionen schreiben möchte um die Codewiederholungen zu vermeiden.

Kommentare sollten entsprechend dem Code eingerückt werden damit der Leser die Codestruktur leicht erfassen kann.

Der Code mal überarbeitet, aber natürlich immer noch mit dem/den Problemen die GUI-Programmierung und Nebenläufigkeit so mit sich bringen:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk

from RPi import GPIO as gpio


LED_PINS = [29, 31, 33, 37]
BUTTON_PINS = [7, 11, 13, 15]
assert len(LED_PINS) == len(BUTTON_PINS)

RESET_PIN = 12


def start():
    while True:
        button_states = [gpio.input(pin) for pin in BUTTON_PINS]
        pressed_buttons_count = sum(button_states)
        if pressed_buttons_count == 1:
            gpio.output(LED_PINS[button_states.index(True)], True)

        if pressed_buttons_count > 0:
            break


def main():
    gpio.setmode(gpio.BOARD)
    try:
        gpio.setup(
            [RESET_PIN, *BUTTON_PINS], gpio.IN, pull_up_down=gpio.PUD_DOWN
        )
        gpio.setup(LED_PINS, gpio.OUT)

        fenster = tk.Tk()
        fenster.title("Buzzman")

        tk.Label(text="Spielernamen eingeben:").pack()

        player_name_entries = list()
        for _ in range(len(BUTTON_PINS)):
            entry = tk.Entry(fenster)
            entry.pack()
            player_name_entries.append(entry)

        tk.Button(fenster, text="Start", command=start).pack()

        fenster.mainloop()
    finally:
        gpio.cleanup()


if __name__ == "__main__":
    main()
Du wirst spätestens bei GUI-Programmierung nicht wirklich um objektorientierte Programmierung herum kommen. Aber auch das Spiel ohne GUI könnte schon mindestens mal eine Spieler-Klasse vertragen die damit man da keine ”parallelen” Datenstrukturen für Taster- und LED-Pins führen muss.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten