GUI hängt sich auf

Fragen zu Tkinter.
Antworten
Denkdirnix
User
Beiträge: 3
Registriert: Donnerstag 5. November 2020, 20:27

Hallo zusammen,

derzeit versuche ich eine grafische Oberfläche zur Bedienung eines automatischenCocktailmixer zu programmieren. Dieser wird über einen Raspberry Pi gesteuert und deshalb nutze ich Python, um Steuerung der Technik und Benutzeroberfläche in einem zu haben. Die GUI soll über ein Touchdisplay später gesteuert werden.

Das Programm bisher:
Grundsätzlich gelangt man von der Startseite, die später als Bildschirmschoner dienen soll, zur Auswahl zwischen "Cocktails" und "Alkoholfrei". Das führt dann jeweils zu mehreren Auswahl-Seiten, durch die man mit "Vor"- und "Zurück"-Buttons blättern kann. Zusätzlich gelangt man über einen Home-Button wieder zurück zur Startseite.

Problem:
Soweit funktioniert das Programm gut, allerdings hängt sich die Grafik nach etwa 14 Klicks, egal ob innerhalb eines Auswahlbereichs oder insgesamt, auf. Die Buttons lassen sich weiter anklicken und print-Ausgaben zeigen an, dass das Programm weiterläuft und man durch die Seiten navigiert, allerdings wird das nicht mehr angezeigt.

Bisherige Lösungsansätze:
Fehlermeldungen werden nicht angegeben und ich habe es auch nicht geschafft mir z.B. stderr ausgeben zu lassen (habe nur eine leere Datei dafür erzeugen können).
Ich habe überlegt, ob der Speicher in irgendeiner Weise vollläuft, zumindest wird bei jedem Aufruf einer Seite ein Objekt mit höherer Nummer erzeugt. Allerdings bin ich über den garbage collector auch nicht weitergekommen, da das Hochsetzen seiner Grenzen nichts am Verhalten des Programms geändert hat.

Leider ist das mein erstes Projekt mit Python und ich habe wenig Erfahrung, deswegen bin ich auch dankbar für jede Hilfe und jeden Tipp!

Hier ist mein Code:

Code: Alles auswählen

import tkinter as tk
from tkinter import Button
from tkinter import StringVar
from tkinter import Label
import tkinter.font
import datetime


    # Variablen
widthF=int() #Fensterbreite
heightF=int() #Fensterhöhe

global button_VorZur_width
global button_VorZur_height
global seiteC
seiteC=1
global seiteA
seiteA=1
  

    # Klassen:   
class RomomixApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)  # __init__ function für Tk Klasse
        self._frame = None
        self.switch_frame(StartSeite(self))
        self.attributes('-fullscreen', True) #Full Screen
       
    def switch_frame(self, frame_class):
        #Zerstört aktuellen Frame und ersetzt ihn mit neuem
        new_frame = frame_class
        if self._frame is not None:
            self._frame.destroy()
        self._frame = new_frame
        self._frame.place(x=0, y=0)

class StartSeite(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.fenstergroesse()
        tk.Frame.config(self, width=widthF, height=heightF)
        breite_Start=int(widthF)
        hoehe_Start=int(heightF)
        startButton=tk.Button(self, bg=hintergrundFarbe, fg=textFarbe,
                              text="Start", font=('Arial', 36, 'bold'),
                              command=lambda: master.switch_frame(Auswahl(app)),
                              activebackground=hintergrundFarbe, activeforeground=textFarbe,
                              highlightthickness=0, relief="flat")
        startButton.place(x=0, y=0, width=int(widthF), height=int(heightF))
        
    def fenstergroesse(self):
        self.winWidth=tk.Frame.winfo_screenwidth(self)
        self.winHeight=tk.Frame.winfo_screenheight(self)
            #Fenster
        widthLokal=self.winWidth
        heightLokal=self.winHeight
        global widthF
        widthF=widthLokal
        global heightF
        heightF=heightLokal
            #Fußzeile
        widthLokalFZ=self.winWidth
        heightLokalFZ= int(self.winHeight/8)
        global widthFZ
        widthFZ=widthLokalFZ
        global heightFZ
        heightFZ=heightLokalFZ
            #Kopfzeile
        widthLokalKZ=self.winWidth
        heightLokalKZ= heightFZ
        global widthKZ
        widthKZ=widthLokalKZ
        global heightKZ
        heightKZ=heightLokalKZ
            #Mittelbereich
        widthLokalC=self.winWidth
        heightLokalC=int(self.winHeight-heightKZ-heightFZ)
        global widthC
        widthC=widthLokalC
        global heightC
        heightC=heightLokalC

         
        

class Auswahl(tk.Frame): # "Cocktail oder Alkoholfrei"
    def __init__(self, master):
        self.frameAuswahl=tk.Frame.__init__(self, master)
        tk.Frame.config(self, width=widthF, height=heightF)
        global seiteC
        seiteC=1
        global seiteA
        seiteA=1
        middleFrame=tk.Frame(self.frameAuswahl, highlightthickness=0, bg=hintergrundFarbe_hell,
                             width=widthC, height=heightC).place(x=0, y=heightKZ) 
        buttonAuswahl_width=int(widthF/3) #250
        buttonAuswahl_height=int(heightF/3) #120
        tk.Button(self.frameAuswahl, text="Cocktail", font=textAuswahl,
                  command=lambda: master.switch_frame(Cocktails(app, 1)), 
                  bd=10, bg=textFarbe, activebackground=hintergrundFarbe,
                  activeforeground=textFarbe).place(x=int(widthC/2-buttonAuswahl_width*1.2),
                                                    y=int(heightF/2-buttonAuswahl_height/2),
                                                    width=buttonAuswahl_width, height=buttonAuswahl_height)  
        tk.Button(self.frameAuswahl, text="Alkoholfrei", font=textAuswahl,
                  command=lambda: master.switch_frame(Alkoholfreie(app, 1)),
                  bd=10, bg=textFarbe, activebackground=hintergrundFarbe,
                  activeforeground=textFarbe).place(x=int(widthC/2+buttonAuswahl_width*0.2),
                                                    y=int(heightF/2-buttonAuswahl_height/2),
                                                    width=buttonAuswahl_width, height=buttonAuswahl_height) 
        foot=Fusszeile(self.frameAuswahl, "Blanko", "mit Uhrzeit", "")
        head=Kopfzeile(self.frameAuswahl, "Auswahl", "")
        
        
        
        

class Cocktails(tk.Frame): #Cocktail-Auswahlseiten
    def __init__(self, master, seite):
        self.frameCocktail=tk.Frame.__init__(self, master)
        tk.Frame.config(self, width=widthF, height=heightF)
        global seiteC
        middleFrame=tk.Frame(self.frameCocktail, highlightthickness=0, bg=hintergrundFarbe_hell,
                             width=widthC, height=heightC).place(x=0, y=heightKZ)
        foot=Fusszeile(self.frameCocktail, "VorZurueck", "mit Uhrzeit","Cocktails")
        head=Kopfzeile(self.frameCocktail, "CocktailAuswahl", "")
        self.seite = seite
        self.masterFrame=self.frameCocktail
        

        if self.seite == 1:
            
            seiteC=1
            nummer=0 # Variable für Getränke-Titel
            buttonC1=Button(self.masterFrame, text=name_Cocktail[nummer], font=textAuswahl,
                            command = lambda: master.switch_frame(Detail(app, 1, name_TitelCocktail[nummer])),
                            bg=textFarbe, fg=hintergrundFarbe, relief=buttonMenu_Art, bd=buttonMenu_Rand,
                            activebackground=hintergrundFarbe, activeforeground=textFarbe) 
            buttonC1.place(relwidth=buttonAuswahl_relwidth, relheight=buttonAuswahl_relheight,
                           relx=buttonAuswahl_randSeite, rely=buttonAuswahl_randOU, anchor="center")
            buttonC2=Button(self.masterFrame, text=name_Cocktail[nummer+1], bg=textFarbe, font=textAuswahl,
                            fg=hintergrundFarbe, relief=buttonMenu_Art,
                            activebackground=hintergrundFarbe, activeforeground=textFarbe, bd=buttonMenu_Rand) 
            buttonC2.place(relwidth=buttonAuswahl_relwidth, relheight=buttonAuswahl_relheight,
                           relx=0.5, rely=buttonAuswahl_randOU, anchor="center")
            buttonC3=Button(self.masterFrame, text=name_Cocktail[nummer+2], bg=textFarbe, font=textAuswahl,
                            fg=hintergrundFarbe, relief=buttonMenu_Art,
                            activebackground=hintergrundFarbe, activeforeground=textFarbe, bd=buttonMenu_Rand) 
            buttonC3.place(relwidth=buttonAuswahl_relwidth, relheight=buttonAuswahl_relheight,
                           relx=1-buttonAuswahl_randSeite, rely=buttonAuswahl_randOU, anchor="center")
            buttonC4=Button(self.masterFrame, text=name_Cocktail[nummer+3], bg=textFarbe, font=textAuswahl,
                            fg=hintergrundFarbe, relief=buttonMenu_Art,
                            activebackground=hintergrundFarbe, activeforeground=textFarbe, bd=buttonMenu_Rand) 
            buttonC4.place(relwidth=buttonAuswahl_relwidth, relheight=buttonAuswahl_relheight,
                           relx=buttonAuswahl_randSeite, rely=1-buttonAuswahl_randOU, anchor="center")
            buttonC5=Button(self.masterFrame, text=name_Cocktail[nummer+4], bg=textFarbe, font=textAuswahl,
                            fg=hintergrundFarbe, relief=buttonMenu_Art,
                            activebackground=hintergrundFarbe, activeforeground=textFarbe, bd=buttonMenu_Rand) 
            buttonC5.place(relwidth=buttonAuswahl_relwidth, relheight=buttonAuswahl_relheight,
                           relx=0.5, rely=1-buttonAuswahl_randOU, anchor="center")
            buttonC6=Button(self.masterFrame, text=name_Cocktail[nummer+5], bg=textFarbe, font=textAuswahl,
                            fg=hintergrundFarbe, relief=buttonMenu_Art,
                            activebackground=hintergrundFarbe, activeforeground=textFarbe, bd=buttonMenu_Rand) 
            buttonC6.place(relwidth=buttonAuswahl_relwidth, relheight=buttonAuswahl_relheight,
                           relx=1-buttonAuswahl_randSeite, rely=1-buttonAuswahl_randOU, anchor="center")
       
        elif self.seite == 2:
            seiteC=2
        elif self.seite == 3:
            seiteC=3
        elif self.seite == 4:
            seiteC=4
        elif self.seite == 5:
            seiteC=5

        
class Alkoholfreie(tk.Frame): # Alkoholfrei-Auswahlseiten
    def __init__(self, master, seite):
        frameAlkoholfrei=tk.Frame.__init__(self, master)
        tk.Frame.config(self, width=widthF, height=heightF)
        middleFrame=tk.Frame(frameAlkoholfrei, highlightthickness=0, bg=hintergrundFarbe_hell,
                             width=widthC, height=heightC).place(x=0, y=heightKZ)
        foot=Fusszeile(frameAlkoholfrei, "VorZurueck", "mit Uhrzeit", "Alkoholfreie")
        head=Kopfzeile(frameAlkoholfrei, "AlkohlfreiAuswahl", "")


class Detail(tk.Frame): # Info-Seite Getränk
    def __init__(self, master, seitenart, titel):
        frameDetail=tk.Frame.__init__(self, master)
        tk.Frame.config(self, width=widthF, height=heightF)
        middleFrame=tk.Frame(frameDetail, highlightthickness=0, bg=hintergrundFarbe_hell,
                             width=widthC, height=heightC).place(x=0, y=heightKZ)
        foot=Fusszeile(frameDetail, "VorZurueck", "mit Uhrzeit", "")
        head=Kopfzeile(frameDetail, "DetailSeite", titel)

        self.seitenart =seitenart

        if self.seitenart == 1: #verschiedene Seiten für Getränke erstellen...
            print("tadaa")


class Fusszeile(tk.Frame):
    def __init__(self, master, art, uhrzeit, menuart):
        footFrame=tk.Frame(master,
                         highlightthickness=0, bg=hintergrundFarbe,
                         width=widthFZ, height=heightFZ)
        footFrame.place(x=0, y=int(heightF-heightKZ))

        self.menuart=menuart

        global seiteC
        
        if art == "VorZurueck":
            self.menu(master)

        if uhrzeit == "mit Uhrzeit":
            uhr._init_(footFrame)
            uhr.aktualisieren()
        

    def menu(self, master): # Navigationsleiste für Auswahlseiten
        
        button_VorZur_width=int(widthFZ/12)
        button_VorZur_height=int(heightFZ*0.8)
        self.buttonVor=Button(master, text="vor", font=textZahl,
                         command=lambda: self.clickVor(master),
                         fg=buttonMenu_Text, bg=buttonMenu_Farbe, relief=buttonMenu_Art, bd=buttonMenu_Rand,
                         highlightthickness=0)
        self.buttonVor.place(x=int(widthFZ/2+button_VorZur_width*0.5+widthFZ/45),
                                                     y=int(heightF-heightFZ/2-button_VorZur_height/2),
                                                     width=button_VorZur_width, height=button_VorZur_height)
        self.buttonZur=Button(master, text="zurück", font=textZahl,
                         command=lambda: self.clickZur(master),
                         fg=buttonMenu_Text, bg=buttonMenu_Farbe, relief=buttonMenu_Art, bd=buttonMenu_Rand,
                         highlightthickness=0)
        self.buttonZur.place(x=int(widthFZ/2-button_VorZur_width*1.5-widthFZ/45),
                                                     y=int(heightF-heightFZ/2-button_VorZur_height/2),
                                                     width=button_VorZur_width, height=button_VorZur_height)
        buttonHome=Button(master, text="Home", font=textZahl, 
                          command=lambda: app.switch_frame(StartSeite(app)),
                          fg=buttonMenu_Text, bg=buttonMenu_Farbe, relief=buttonMenu_Art, bd=buttonMenu_Rand,
                          highlightthickness=0)
        buttonHome.place(x=int(widthFZ/2-button_VorZur_width*0.5),y=int(heightF-heightFZ/2-button_VorZur_height/2),
                         width=button_VorZur_width, height=button_VorZur_height)
        
    def clickVor(self, master): #nächste Seite
        if self.menuart == "Cocktails":
            global seiteC
            
            if seiteC <= 4:
                seiteC=seiteC+1
            else:
                seiteC=1
                
            app.switch_frame(Cocktails(app, seiteC))
            

        elif self.menuart == "Alkoholfreie":
            global seiteA
            
            if seiteA <= 4:
                seiteA=seiteA+1
            else:
                seiteA=1
           
            app.switch_frame(Alkoholfreie(app, seiteA))
            

    def clickZur(self, master): #vorherige Seite
        if self.menuart == "Cocktails":
            global seiteC
            
            if seiteC >=2:    
                seiteC=seiteC-1
            else:
                seiteC=5
            
            app.switch_frame(Cocktails(app, seiteC))
            
        elif self.menuart == "Alkoholfreie":
            global seiteA
            
            if seiteA >=2:    
                seiteA=seiteA-1
            else:
                seiteA=5
            
            app.switch_frame(Cocktails(app, seiteA))
            


class Kopfzeile():
    def __init__(self, master, art, titelDetail):
        headFrame=tk.Frame(master,
                         highlightthickness=0, bg=hintergrundFarbe,
                         width=widthKZ, height=heightKZ).place(x=0, y=0)
        self.titelDetail=titelDetail

        if art == "CocktailAuswahl":
            self.cocktailAuswahl(headFrame)
        elif art == "AlkohlfreiAuswahl":
            self.alkoholfreiAuswahl(headFrame)
        elif art == "DetailSeite":
            self.detailSeite(headFrame)


    def cocktailAuswahl(self, master):
        titel=Label(master, text="Cocktails", font=textTitel,
                    fg=buttonMenu_Text, bg=hintergrundFarbe,
                    relief="flat",
                    highlightthickness=0)
        titel.place(anchor="center", relx=0.5, rely=0.06)

        seiteNummer=str(seiteC)
        zahlSeite=Label(master, text=seiteNummer+"/5", font=textTitel,
                    fg=buttonMenu_Text, bg=hintergrundFarbe,
                    relief="flat",
                    highlightthickness=0)
        zahlSeite.place(anchor="center", relx=0.95, rely=0.06)
        

    def alkoholfreiAuswahl(self, master):
        titel=Label(master, text="Alkoholfrei", font=textTitel,
                    fg=buttonMenu_Text, bg=hintergrundFarbe,
                    relief="flat",
                    highlightthickness=0)
        titel.place(anchor="center", relx=0.5, rely=0.06)

        seiteNummer=str(seiteA)
        zahlSeite=Label(master, text=seiteNummer+"/2", font=textTitel,
                    fg=buttonMenu_Text, bg=hintergrundFarbe,
                    relief="flat",
                    highlightthickness=0)
        zahlSeite.place(anchor="center", relx=0.95, rely=0.06)

    def detailSeite(self, master):
        titel=Label(master, text=self.titelDetail, font=textTitel,
                    fg=buttonMenu_Text, bg=hintergrundFarbe,
                    relief="flat",
                    highlightthickness=0)
        titel.place(anchor="center", relx=0.5, rely=0.06)

class Uhr:
    def _init_(self, master):
        self.zeit = StringVar() # Uhrzeit als Text
        self.anzeige = Label(master, textvariable=self.zeit, font=('Arial', 26), bg=hintergrundFarbe, fg=textFarbe)
        self.anzeige.place(anchor="center", relx=0.95, rely=0.5)

    def aktualisieren(self):
            zeitpunkt=datetime.datetime.today()
            z=zeitpunkt.strftime("%H:%M")
            self.zeit.set(z)
            self.anzeige.after(1, self.aktualisieren) 


    # Layout
textAuswahl=("Arial", 20, 'bold')
textTitel=('Arial', 26, 'bold')
textZutaten=("Arial", 30, 'bold')
textZahl=("Arial", 20, 'bold')
textFarbe='#12dc0d' # grün
hintergrundFarbe='#191717' # dunkel
hintergrundFarbe_hell='#2e2e2e' # aufgehelltes dunkel

    # Menü: Zurück - Home - Vor
buttonMenu_Farbe='#424242' # grau
buttonMenu_Text=textFarbe
buttonMenu_Art="raised"
buttonMenu_Rand=5

    # Auswahl-Buttons
buttonAuswahl_relwidth=0.25
buttonAuswahl_relheight=0.25
buttonAuswahl_randSeite=0.18
buttonAuswahl_randOU=0.32

    # Getränkenamen-Buttons
name_Cocktail=['Sex on\nthe Beach', 'Tequila\nSunrise', 'Touch Down', 'Margarita','Mai Tai','Zombie', 'Blue Lagoon', 'Kamikaze', 'Planters\nPunch', 'Srewdriver', 'Cosmopolitan', 'Hurricane', 'Anita', ' Caribbean\nBlue', 'Mexicano', 'Hector Spezial', 'Pipeline', 'Robinson\nCrusoe', 'Andrea', 'Pikaki', 'Springtime\nCooler', 'Bula Talei', 'Apricot Sour', 'Mexican\nSunset', 'Sweet Red', 'Red Apple', 'Sweet Green']
name_Alkoholfrei=['Safe Sex\non the Beach', '5 Früchte\nCocktail', 'Bahamas', 'Blutorange', 'Virgin Sunrise', 'Rio', 'Exotic Punch', 'Sommer-\nnachtstraum', 'Apfelschorle', 'Maracujaschorle', 'Wasser']

    # Getränkenamen-Titel
name_TitelCocktail=['Sex on the Beach', 'Tequila Sunrise', 'Touch Down', 'Margarita','Mai Tai','Zombie', 'Blue Lagoon', 'Kamikaze', 'Planters Punch', 'Srewdriver', 'Cosmopolitan', 'Hurricane', 'Anita', ' Caribbean\nBlue', 'Mexicano', 'Hector Spezial', 'Pipeline', 'Robinson\nCrusoe', 'Andrea', 'Pikaki', 'Springtime\nCooler', 'Bula Talei', 'Apricot Sour', 'Mexican\nSunset', 'Sweet Red', 'Red Apple', 'Sweet Green']
name_TitelAlkoholfrei=['Safe Sex on the Beach', '5 Früchte Cocktail', 'Bahamas', 'Blutorange', 'Virgin Sunrise', 'Rio', 'Exotic Punch', 'Sommernachtstraum', 'Apfelschorle', 'Maracujaschorle', 'Wasser']



uhr = Uhr()


if __name__ == "__main__":
    app = RomomixApp()
    app.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 14084
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Denkdirnix: Ich habe beim ersten ``global`` aufgehört zu lesen. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Globale Variablen haben in einem sauberen Programm nix zu suchen und wenn man Klassen verwendet, gibt es dafür auch keinerlei Ausrede.

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

Bei GUIs erstellt man üblicherweise die ganze Elemente einmal am Anfang und zerstört die nicht immer wieder und baut sie neu auf.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Denkdirnix
User
Beiträge: 3
Registriert: Donnerstag 5. November 2020, 20:27

Vielen Dank für die Antwort!
Ich werde meinen Code dann nocheinmal grundsätzlich überdenken und anpassen.
Wie schaffe ich es denn, dass ich nur die gewünschte Seite (als Objekte der jeweiligen Klasse) einblende, wenn ich sie nicht zwischendurch zerstöre und durch eine andere ersetze?
Und allein aus Neugierde: eine Idee, woran es beim Aufhängen der Gui scheitert hast Du nicht, oder?
Benutzeravatar
__blackjack__
User
Beiträge: 14084
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Denkdirnix: Das löst man in der Regel so, dass man alle Seiten übereinander legt und immer die jeweils aktuelle nach vorne holt. Andere GUI-Rahmenwerke haben dafür meistens schon etwas fertiges (Qt → QStackedWidget, Gtk → Gtk.Stack), in Tk muss man sich das selbst basteln.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Denkdirnix
User
Beiträge: 3
Registriert: Donnerstag 5. November 2020, 20:27

Alles klar, vielen Dank!
Antworten