LED-Beleuchtungssteuerung mit Raspberry Pi

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
John_0815
User
Beiträge: 5
Registriert: Sonntag 22. Mai 2022, 09:19

Hallo,

ich bin neu hier im Forum und habe mich hier angemeldet um unser Projekt mit euch zu teilen, um andere zu inspierieren und um neues zu lernen.
Es ist ein Gemeinschaftsprojekt bei dem es darum geht die Beleuchtung einer Bar zu steuern. Die Bar wird durch RGB-LED-Stripes beleuchtet welche in 3 Bereiche eingeteilt ist.

Hardwarekomponente:
- Raspberry Pi 3B+
- Touch-Display zur Ein- und Ausgabe
- selbstkonstruierte Platine zur Ansteuerung
- PC-Netzteil zur Versorgung mit 5V und 12V

Software:
Ich bin gerade dabei die Software neu zu schreiben. Bis jetzt arbeite ich an der GUI.
Der aktuelle Stand: https://github.com/John0815/LED-Beleuch ... _220522.py

Für Ideen, Anmerkungen und Kritik bin ich offen und freue mich auf eine gute Zusammenarbeit.


Gruß John
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Variablennamen werden komplett klein geschrieben, Klassen mit großen Anfangsbuchstaben und Konstanten KOMPLETT_GROSS.
Das verwirrt vor allem bei switch_frame, wo Du Klassen übergibst, die aber wie deren Instanzen geschrieben sind.
Globale Variablen darf man nicht verwenden. `history_list` hat in dieser Hinsicht viele Probleme. Es ist nicht nur eine globale Variable, sondern enthält magische Zahlenwerte für die einzelnen Frames statt der konkreten Frames. Die Variable wird in den __init__-Methoden der einzelnen Frames verwaltet, statt zentral im Hauptfenster. Beim Zurückgehen werden neue Frames erzeugt, statt bereits vorhandene wiederzuverwenden. Dadurch verschwinden Eingaben beim Zurückgehen wieder, was für den Anwender überraschend ist.
__init__ ist zum Initialisieren da, nicht dass darin das gesamte Programm läuft. In main_win.__init__ darf es kein mainloop-Aufruf geben.
`place` darf man nicht verwenden, weil es je nach Auflösung und System zu unbenutzbaren Oberflächen führen kann. Benutze grid oder pack. Das macht die komplizierten Rechnungen für die Position überflüssig.
Die vielen Frames haben alle einen ähnlichen Aufbau mit den Buttons. Dort könnte man eine Basisklasse definieren, die generalisiert Buttons erstellt.
`font_list` ist eine Konstante, sollte also GROSS geschrieben sein; benutzt wird aber immer nur ein einzelnes Element, das per fixem Index referenziert wird. Besser wäre es also, vier Font-Konstanten zu haben, die sprechende Namen haben.
John_0815
User
Beiträge: 5
Registriert: Sonntag 22. Mai 2022, 09:19

Hallo Sirius3,

ich danke dir für die schnelle Antwort und bin gerade dabei deine Anmerkungen umzusetzen.
Das mit der Groß- und Kleinschreibung bekomme ich hin. Auch das mit den Schriftarten habe ich geändert.

Ein paar Fragen hab ich da gleich mal.
Wo sollte der mainloop-Aufruf den stehen? Ich hab das so in mehreren Erklärungen gesehen.
Wie kann ich auf vorhandene Frames zurückgehen?

Gruß John
John_0815
User
Beiträge: 5
Registriert: Sonntag 22. Mai 2022, 09:19

Hallo liebe Gemeinschaft,

ich habe noch einmal neu begonnen mit meinem Programm und möchte euch heute meinen Fortschritt zeigen. Ich habe versucht alles zu beachten was mir Sirius3 freundlicherweise aufgezeigt hat und ich hoffe ich hab an alles gedacht. Eine Ausnahme gibt es da allerdings. Mit "Grid" und "Pack" komme ich nicht klar, ich habe wieder "Place" verwendet, aber habe statt fester Zuweisungen jetzt relative verwendet.

Das Programm ist bei weitem noch nicht fertig, aber vlt. hat ja jetzt schon jemand Ideen für Verbesserungen.

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk

# Liste der Grundfarben: [0]=rot, [4]=grün, [8]=blau, [12]=weiß, [13]=schwarz
COLOR_LIST = ['#FF0000', '#FFFF00', '#FF8000', '#FF0080', 
                          '#00FF00', '#00FFFF', '#00FF80', '#80FF00',
                          '#0000FF', '#FF00FF', '#0080FF', '#8000FF',
                          '#FFFFFF', '#000000']


class Main(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Barbeleuchtung")
        self.geometry("480x320")
        self.attributes('-fullscreen',  False)
        self.bind("<F11>",
                         lambda event: self.attributes("-fullscreen",
                            not self.attributes("-fullscreen")))
        self.bind("<Escape>",
                         lambda event: self.attributes("-fullscreen",
                            not self.attributes("-fullscreen")))
        
        self.__styling()
        self.__create_frames()

    def __styling(self):
        s = ttk.Style()
        s.configure('TFrame', 
                            background = "#000000", anchor = "center")
        s.configure('TLabel',
                            background = "#000000", foreground = "#00FF00",
                            font = "Times 13", anchor = "center")
        s.configure("head.TLabel",
                            font = "Times 15 bold")
        s.configure("head2.TLabel", 
                            font = "Times 14 bold")
        s.configure('TButton', 
                            background = "#000000", foreground = "#00FF00", 
                            font = "Times 13", anchor = "center")
        s.configure("back.TButton",
                            background = "#F0F0F0",foreground = "#FF0000",
                            font = "Times 16 bold")
        s.configure("config.TButton",
                            relief = "flat")
        
        
    def __create_frames(self):
        frame_head = Frame_Head(self)
        frame_head.place(x = 0, y = 0, 
                                      relwidth = 1,  relheight = 0.15)
        
        frame_main = Frame_Main(self)
        frame_main.place(relx = 0, rely = 0.15, 
                                      relwidth = 1, relheight = 0.85)
        #frame_main.place_forget()
        
        frame_select_color = Frame_Select_Color(self)
        frame_select_color.place(relx = 0, rely = 0.15, 
                                                relwidth = 1, relheight = 0.85)
        frame_select_color.place_forget()
        
    def __contol_frames(selfself):
        x = 0
        x != 1    # wir sind die Platzhalter :-)
    
class Frame_Head(ttk.Frame):
    def __init__(self, container):
        super().__init__(container)
        self.__create_widgets()
        
    def __create_widgets(self):
        ttk.Label(self, text = "Bar Jugendclub Grünberg", style="head.TLabel"
                      ).place(relx = 0.01, rely = 0.02, 
                                  relwidth = 0.98, relheight = 0.98)
        ttk.Button(self, text = " ", style = "config.TButton"
                        ).place(x = 5, y = 5, 
                                    width = 30, height = 30)
        ttk.Button(self, text = "<-", style = "back.TButton"
                        ).place(relx = 0.84, rely = 0.08, 
                                    relwidth = 0.15, relheight = 0.8)


class Frame_Main(ttk.Frame):
    def __init__(self, container):
        super().__init__(container)
        
        self.__create_widgets()
        
    def __create_widgets(self):
        ttk.Label(self, text = "Beleuchtungssteuerung", style = "head2.TLabel"
                      ).place(relx = 0.02, rely = 0.02, 
                                  relwidth = 0.96, relheight = 0.18)
        ttk.Button(self, text = "Einfarbig"
                        ).place(relx = 0.02, rely = 0.23, 
                                    relwidth = 0.96, relheight = 0.24)
        ttk.Button(self, text = "Mehrfarbig"
                        ).place(relx = 0.02, rely = 0.48, 
                                    relwidth = 0.96, relheight = 0.24)
        ttk.Button(self, text = "Mehrfarbig"
                        ).place(relx = 0.02, rely = 0.73, 
                                    relwidth = 0.96, relheight = 0.24)
    

class Frame_Select_Color(ttk.Frame):
    def __init__(self, container):
        super().__init__(container)
        
        self.__create_widgets()
        
    def __create_widgets(self):
        ttk.Label(self,  text = "Farbe wählen", style = "head2.TLabel"
                      ).place(relx = 0.02, rely = 0.02, 
                                 relwidth = 0.96, relheight = 0.18)
        for i, Color in enumerate(COLOR_LIST):
            column, row = divmod(i, 4)
            tk.Button(self, bg = Color
                            ).place(relx = row * 0.225 + row * 0.02 +0.02,
                                        rely = column * 0.17 + column * 0.03 +0.21,
                                        relwidth = 0.225,
                                        relheight = 0.17)
        ttk.Button(self, text = "individuell"
                        ).place(relx = 0.51, rely = 0.81, 
                                    relwidth = 0.47, relheight = 0.17)


if __name__ == '__main__':
    root = Main()
    root.mainloop()
Um vom "Frame_Main" zum "Frame_Selekt_Color" zu wechseln muss man noch des "#" aus Zeile 56 in Zeile 61 setzen.

Für Ideen und Kritik bin ich immer offen und freue mich auf eure Anregungen.


Gruß John
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@John_0815: Grunddatentypen haben in Namen nichts zu suchen. Das ändert man im Laufe der Programmentwickung öfter als man denkt, und dann hat man entweder falsche, irreführende Namen im Quelltext, oder muss immer überall alle betroffenen Namen mitändern.

Den Kommentar mit den Grundfarben kann man sich sparen wenn man einfach die Namen der Grundfarben bei den Werten verwendet. Also beispielsweise "red" statt "#FF0000". Die subtraktiven Grundfarben "cyan", "magenta", und "yellow" gibt es auch, und für einige andere Farben in der Liste gibt es auch Namen.

Das Hauptprogramm gehört in eine Funktion, auch wenn es nur einen globalen Namen definiert.

Vergiss bitte doppelte führende Unterstriche. Das ist nicht ”private” aus anderen Sprachen, sondern ein Mechanismus um Namenskollisionen bei Mixin-Klassen oder tiefen vererbungshierarchien zu vermeiden. Beides macht man in Python sehr selten, dementsprechend unüblich sind auch diese doppelten Unterstriche. Implementierungsdetails werden mit *einem* führenden Unterstrich gekennzeichnet.

Allerdings hast Du da auch kaum Methoden denn die ganzen `__create_frames()` sollte es gar nicht geben. Es macht keinen Sinn eine nahezu leere `__init__()` zu haben die einfach nur eine andere Methode aufruft in der dann alles steht.

Und dann bleiben viele Klassen die nur aus einer `__init__()` bestehen und die keinerlei Zustand oder Funktionalität über das hinaus besitzen was die Basisklasse bietet. Womit das nicht wirklich sinnvolle Klassen sind, sondern übermässig kompliziert geschriebene Funktionen.

Die Style-Informationen gehören IMHO nicht in das Hauptfenster, denn dann kann man kein anderes Element ausprobieren oder wiederverwenden ohne *das* Hauptfenster erstellt zu haben.

Die Style-Informationen würde ich auch als Konstante in eine Datenstruktur heraus ziehen. Dann kann man die einfacher ändern, oder auch in eine Datei auslagern.

Für Zeichenketten die in Tk eine besondere Bedeutung haben und für die es deshalb eine Konstante in `tkinter` gibt, sollte man diese Konstante verwenden, statt eine literale Zeichenkette mit dem gleichen Wert.

Die beiden `bind()`-Aufrufe wiederholen den gleichen ``lambda``-Ausdruck. Den sollte man entweder an einen Namen binden, oder besser noch eine Methode daraus machen.

Klassennamen enthalten keine Unterstriche und bei Namen sollte man die normale Reihenfolge von Worten beibehalten, denn oft ändert sich die Bedeutung wenn man die Reihenfolge ändert. `frame_head` bedeutet etwas anderes als `head_frame`. Das erste ist eine Tätigkeit, das zweite ist ein ”Ding” und gemeint ist hier das ”Ding” und nicht die Tätigkeit.

Was als eigene Klasse Sinn machen würde ist so etwas wie ein `StackedFrame` in den man Frames einfügen kann und wo es eine Methode gibt aus diesen Frames zu wählen.

Zwischenstand ("<-"-Button führt zum Hauptmenü):

Code: Alles auswählen

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

COLORS = [
    "red",
    "yellow",
    "DarkOrange1",
    "#FF0080",
    "green",
    "cyan",
    "spring green",
    "chartreuse",
    "blue",
    "magenta",
    "#0080FF",
    "#8000FF",
    "white",
    "black",
]

STYLE = {
    "TFrame": {"background": "black", "anchor": tk.CENTER},
    "TLabel": {
        "background": "black",
        "foreground": "green",
        "font": "Times 13",
        "anchor": tk.CENTER,
    },
    "head.TLabel": {"font": "Times 15 bold"},
    "head2.TLabel": {"font": "Times 14 bold"},
    "TButton": {
        "background": "black",
        "foreground": "green",
        "font": "Times 13",
        "anchor": tk.CENTER,
    },
    "back.TButton": {
        "background": "gray94",
        "foreground": "red",
        "font": "Times 15 bold",
    },
    "config.TButton": {"relief": tk.FLAT},
}


def create_main_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Beleuchtungssteuerung", style="head2.TLabel").pack()
    for text in ["Einfarbig", "Mehrfarbig", "Mehrfarbig"]:
        ttk.Button(frame, text=text).pack(expand=True, fill=tk.BOTH)
    return frame


def create_select_color_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Farbe wählen", style="head2.TLabel").pack()

    button_frame = ttk.Frame(frame)
    buttons_per_row = 4
    for column in range(buttons_per_row):
        button_frame.columnconfigure(column, weight=1)

    for i, color in enumerate(COLORS):
        row, column = divmod(i, buttons_per_row)
        button_frame.rowconfigure(row, weight=1)
        tk.Button(button_frame, bg=color).grid(
            row=row, column=column, sticky=tk.NSEW
        )

    assert buttons_per_row - column - 1 == 2, "not exactly 2 columns left"
    ttk.Button(button_frame, text="individuell").grid(
        row=row, column=column + 1, columnspan=2, sticky=tk.NSEW
    )
    button_frame.pack(expand=True, fill=tk.BOTH)

    return frame


def set_styles(master):
    style = ttk.Style(master)
    for name, values in STYLE.items():
        style.configure(name, **values)


class Main(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Barbeleuchtung")
        self.attributes("-fullscreen", False)
        self.bind("<F11>", self.toggle_fullscreen)
        self.bind("<Escape>", self.toggle_fullscreen)

        head_frame = ttk.Frame(self)
        head_frame.columnconfigure(1, weight=1)
        ttk.Button(head_frame, width=2, style="config.TButton").grid(
            row=0, column=0, sticky=tk.NW
        )
        ttk.Label(
            head_frame, text="Bar Jugendclub Grünberg", style="head.TLabel"
        ).grid(row=0, column=1)
        back_button = ttk.Button(
            head_frame, text="<-", width=2, style="back.TButton"
        )
        back_button.grid(row=0, column=2, sticky=tk.NE)
        head_frame.pack(fill=tk.X)

        stacked_frame = ttk.Frame()
        stacked_frame.rowconfigure(0, weight=1)
        stacked_frame.columnconfigure(0, weight=1)
        stacked_frame.pack(expand=True, fill=tk.BOTH)

        main_frame = create_main_frame(stacked_frame)
        main_frame.grid(row=0, column=0, sticky=tk.NSEW)

        back_button["command"] = main_frame.lift

        select_color_frame = create_select_color_frame(stacked_frame)
        select_color_frame.grid(row=0, column=0, sticky=tk.NSEW)

    def toggle_fullscreen(self, _event=None):
        self.attributes("-fullscreen", not self.attributes("-fullscreen"))


def main():
    root = Main()
    set_styles(root)
    root.mainloop()


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
John_0815
User
Beiträge: 5
Registriert: Sonntag 22. Mai 2022, 09:19

@__blackjack__: Ich danke dir für Hilfe, ich werde die ganzen Informationen erstmal verarbeiten müssen.
Eins ist mir aber aufgefallen, die Liste der Farben habe ich so gestaltet weil ich darüber auch die Ports für die LED's gesteuert habe und über den Butten "individuell" habe ich die Farben per tk.Scale noch verändern können, dafür hab ich den Hex-String aufgeteilt und daraus die Informationen für die einzelnen LED's gezogon.
Aber sonst eine sehr hilfreiche Sache auf der ich aufbauen werde.

Dank und Gruß Johannes
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@John_0815: Weder den Farbnamen noch einen Hex-String musst Du selbst umwandeln, dafür hat Tk eine Funktion die eine Farbe, egal wie die angegeben wird, in drei Zahlwerte zwischen 0 und 65535 umwandelt. Je nach dem ob Du ganzzahlige Werte zwischen 0 und 255 oder Gleitkommazahlen zwischen 0 und 1 benötigst, kannst Du die einfach durch 256 oder 65535 teilen.

Code: Alles auswählen

In [301]: root.winfo_rgb("yellow")                                              
Out[301]: (65535, 65535, 0)

In [302]: [value // 256 for value in root.winfo_rgb("yellow")]                  
Out[302]: [255, 255, 0]

In [303]: [value // 256 for value in root.winfo_rgb("spring green")]            
Out[303]: [0, 255, 127]
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
John_0815
User
Beiträge: 5
Registriert: Sonntag 22. Mai 2022, 09:19

@__blackjack__ , ich danke dir, da hab ich wieder was gelernt.
Ich habe mich mit deinem Skript mal beschäftigt und versucht es so umzusetzen bzw. zu erweitern. Ich habe in "Main" eine Funktion geschrieben die heißt "toggle_page" um die unterschiedlichen Frames mit lift nach oben zu holen. Der "print()" Befehl ist nur zum testen für mich gewesen und zeigt mir das "toggle_page" vom Kopf/Main angesprochen wird, aber nicht von den Frames aus. Und dazu kommt dass es nicht das macht was ich mir vorgestellt habe... es macht garnichts (außer "1" und "2" ausgeben beim drücken der entsprechenden Buttons).

könnte mir bitte noch jemand einen kleinen Denkanstoß geben? was mache ich falsch

Code: Alles auswählen

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

COLORS = [
    "red",
    "yellow",
    "DarkOrange1",
    "#FF0080",
    "green",
    "cyan",
    "spring green",
    "chartreuse",
    "blue",
    "magenta",
    "#0080FF",
    "#8000FF",
    "white",
    "black",
]

STYLE = {
    "TFrame": {"background": "black", "anchor": tk.CENTER},
    "TLabel": {
        "background": "black",
        "foreground": "green",
        "font": "Times 13",
        "anchor": tk.CENTER,
    },
    "head.TLabel": {"font": "Times 15 bold"},
    "head2.TLabel": {"font": "Times 14 bold"},
    "TButton": {
        "background": "black",
        "foreground": "green",
        "font": "Times 13",
        "anchor": tk.CENTER,
    },
    "back.TButton": {
        "background": "white",
        "foreground": "red",
        "font": "Times 15 bold",
    },
    "config.TButton": {"relief": tk.FLAT},
}


def create_main_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Beleuchtungssteuerung", style="head2.TLabel").pack(padx=3, pady=3)
    for text in ["Einfarbig", "Mehrfarbig", "Farbwechsel"]:
        ttk.Button(frame, text=text, command=lambda: Main.toggle_page(master, text)).pack(
            expand=True, fill=tk.BOTH, padx=3, pady=3
        )
    return frame
    
    
def create_select_area_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Bereich auswählen", style="head2.TLabel").pack(padx=3, pady=3)


def create_select_color_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Farbe wählen", style="head2.TLabel").pack(padx=3, pady=3)

    button_frame = ttk.Frame(frame)
    buttons_per_row = 4
    for column in range(buttons_per_row):
        button_frame.columnconfigure(column, weight=1)
        
    for i, color in enumerate(COLORS):
        row, column = divmod(i, buttons_per_row)
        button_frame.rowconfigure(row, weight=1)
        tk.Button(button_frame, bg=color).grid(
            row=row, column=column, sticky=tk.NSEW, padx=3, pady=3
        )

    assert buttons_per_row - column - 1 == 2, "not exactly 2 columns left"
    ttk.Button(button_frame, text="individuell").grid(
        row=row, column=column + 1, columnspan=2, sticky=tk.NSEW, padx=3, pady=3
    )
    button_frame.pack(expand=True, fill=tk.BOTH, padx=3, pady=3)

    return frame
    

def create_set_color_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Farbe einstellen", style="head2.TLabel").pack(padx=3, pady=3)
    
    buttons_frame = ttk.Frame(frame)
    for scale in ["red", "green", "blue", "white"]:
        tk.Scale(frame, from_=255, to=0, bg=scale, troughcolor="black", tickinterval=50, sliderlength=50).pack(
            side="left", expand=True, fill=tk.BOTH
        )
    ttk.Button(frame, text="Weiter").pack(side="left", expand=True, fill=tk.X, anchor="s")
    
    buttons_frame.pack(expand=True, fill=tk.BOTH, padx=3, pady=3)
    
    return frame
    

def create_config_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Einstellungen", style="head2.TLabel").pack(padx=3, pady=3)
    
    buttons_frame = ttk.Frame(frame)
    buttons_frame.pack(expand=True, fill=tk.BOTH, padx=3, pady=3)
    
    return frame


def set_styles(master):
    style = ttk.Style(master)
    for name, values in STYLE.items():
        style.configure(name, **values)


class Main(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Barbeleuchtung")
        self.geometry("480x320")
        self.attributes("-fullscreen", False)
        self.bind("<F11>", self.toggle_fullscreen)
        self.bind("<Escape>", self.toggle_fullscreen)

        head_frame = ttk.Frame(self)
        head_frame.columnconfigure(1, weight=1)
        config_button = ttk.Button(
            head_frame, width=2, style="config.TButton"
        )
        config_button.grid(row=0, column=0, sticky=tk.NW)
        ttk.Label(
            head_frame, text="Bar Jugendclub Grünberg", style="head.TLabel"
        ).grid(row=0, column=1, padx=3, pady=3)
        back_button = ttk.Button(
            head_frame, text="<-", width=4, style="back.TButton"
        )
        back_button.grid(row=0, column=2, sticky=tk.NE)
        head_frame.pack(fill=tk.X)
        
        back_button["command"] = lambda: self.toggle_page("back_page")
        config_button["command"] = lambda: self.toggle_page("config_page")
        
        stacked_frame = ttk.Frame()
        stacked_frame.rowconfigure(0, weight=1)
        stacked_frame.columnconfigure(0, weight=1)
        stacked_frame.pack(expand=True, fill=tk.BOTH)

        
        self.select_color_frame = create_select_color_frame(stacked_frame)
        self.select_color_frame.grid(row=0, column=0, sticky=tk.NSEW)
        
        self.set_color_frame = create_set_color_frame(stacked_frame)
        self.set_color_frame.grid(row=0, column=0, sticky=tk.NSEW)
        
        self.config_frame = create_config_frame(stacked_frame)
        self.config_frame.grid(row=0, column=0, sticky=tk.NSEW)
        
        self.main_frame = create_main_frame(stacked_frame)
        self.main_frame.grid(row=0, column=0, sticky=tk.NSEW)

        
    def toggle_fullscreen(self, _event=None):
        self.attributes("-fullscreen", not self.attributes("-fullscreen"))
    
    def toggle_page(self, page):
        if page == "config_page":
            frame=self.config_frame
            print(1)
        elif page == "back_page":
            frame=self.main_frame
            print(2)
        elif page == "Einfarbig":
            frame=self.select_color_frame
            print(3)
        frame.lift


def main():
    root = Main()
    set_styles(root)
    root.mainloop()


if __name__ == "__main__":
    main()
Gruß John
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@John_0815: Dein Code muss die `lift`-Methode auch *aufrufen*. Dann funktionieren die beiden Buttons oben.

Das Menü funktioniert nicht weil `text` in der Schleife am Ende immer den Wert des letzten Menüpunktes hat. Wenn man Werte zum Zeitpunkt der Definition des Rückrufs haben möchte, und nicht zum Zeitpunkt des späteren Aufrufs, kann man ``lambda`` so nicht verwenden, und eigentlich wurde `functools.partial()` genau für diese Fälle erfunden.

Was auch falsch ist, ist ``Main.toggle_page(master, text)``, denn dazu müsste `master` vom Typ `Main` sein, dann könnte und sollte man aber ``master.toggle_page(text)`` schreiben. `master` ist aber gar nicht vom Typ `Main`.

Ich würde da auch nicht diesen unnötigen Umweg über Zeichenketten für die Seiten gehen, sondern direkt die Widgets verwenden.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial
from tkinter import ttk

COLORS = [
    "red",
    "yellow",
    "DarkOrange1",
    "#FF0080",
    "green",
    "cyan",
    "spring green",
    "chartreuse",
    "blue",
    "magenta",
    "#0080FF",
    "#8000FF",
    "white",
    "black",
]

STYLE = {
    "TFrame": {"background": "black", "anchor": tk.CENTER},
    "TLabel": {
        "background": "black",
        "foreground": "green",
        "font": "Times 13",
        "anchor": tk.CENTER,
    },
    "head.TLabel": {"font": "Times 15 bold"},
    "head2.TLabel": {"font": "Times 14 bold"},
    "TButton": {
        "background": "black",
        "foreground": "green",
        "font": "Times 13",
        "anchor": tk.CENTER,
    },
    "back.TButton": {
        "background": "white",
        "foreground": "red",
        "font": "Times 15 bold",
    },
    "config.TButton": {"relief": tk.FLAT},
}


def create_main_frame(master, toggle_page, menu_items):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Beleuchtungssteuerung", style="head2.TLabel").pack(
        padx=3, pady=3
    )
    for text, page in menu_items:
        ttk.Button(frame, text=text, command=partial(toggle_page, page)).pack(
            expand=True, fill=tk.BOTH, padx=3, pady=3
        )
    return frame


def create_select_area_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Bereich auswählen", style="head2.TLabel").pack(
        padx=3, pady=3
    )


def create_select_color_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Farbe wählen", style="head2.TLabel").pack(
        padx=3, pady=3
    )

    button_frame = ttk.Frame(frame)
    buttons_per_row = 4
    for column in range(buttons_per_row):
        button_frame.columnconfigure(column, weight=1)

    for i, color in enumerate(COLORS):
        row, column = divmod(i, buttons_per_row)
        button_frame.rowconfigure(row, weight=1)
        tk.Button(button_frame, bg=color).grid(
            row=row, column=column, sticky=tk.NSEW, padx=3, pady=3
        )

    assert buttons_per_row - column - 1 == 2, "not exactly 2 columns left"
    ttk.Button(button_frame, text="individuell").grid(
        row=row,
        column=column + 1,
        columnspan=2,
        sticky=tk.NSEW,
        padx=3,
        pady=3,
    )
    button_frame.pack(expand=True, fill=tk.BOTH, padx=3, pady=3)

    return frame


def create_set_color_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Farbe einstellen", style="head2.TLabel").pack(
        padx=3, pady=3
    )

    buttons_frame = ttk.Frame(frame)
    for scale in ["red", "green", "blue", "white"]:
        tk.Scale(
            frame,
            from_=255,
            to=0,
            bg=scale,
            troughcolor="black",
            tickinterval=50,
            sliderlength=50,
        ).pack(side="left", expand=True, fill=tk.BOTH)
    ttk.Button(frame, text="Weiter").pack(
        side="left", expand=True, fill=tk.X, anchor="s"
    )

    buttons_frame.pack(expand=True, fill=tk.BOTH, padx=3, pady=3)

    return frame


def create_config_frame(master):
    frame = ttk.Frame(master)
    ttk.Label(frame, text="Einstellungen", style="head2.TLabel").pack(
        padx=3, pady=3
    )

    buttons_frame = ttk.Frame(frame)
    buttons_frame.pack(expand=True, fill=tk.BOTH, padx=3, pady=3)

    return frame


def set_styles(master):
    style = ttk.Style(master)
    for name, values in STYLE.items():
        style.configure(name, **values)


class Main(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Barbeleuchtung")
        self.geometry("480x320")
        self.attributes("-fullscreen", False)
        self.bind("<F11>", self.toggle_fullscreen)
        self.bind("<Escape>", self.toggle_fullscreen)

        head_frame = ttk.Frame(self)
        head_frame.columnconfigure(1, weight=1)
        config_button = ttk.Button(head_frame, width=2, style="config.TButton")
        config_button.grid(row=0, column=0, sticky=tk.NW)
        ttk.Label(
            head_frame, text="Bar Jugendclub Grünberg", style="head.TLabel"
        ).grid(row=0, column=1, padx=3, pady=3)
        back_button = ttk.Button(
            head_frame, text="<-", width=4, style="back.TButton"
        )
        back_button.grid(row=0, column=2, sticky=tk.NE)
        head_frame.pack(fill=tk.X)

        stacked_frame = ttk.Frame()
        stacked_frame.rowconfigure(0, weight=1)
        stacked_frame.columnconfigure(0, weight=1)
        stacked_frame.pack(expand=True, fill=tk.BOTH)

        self.select_color_frame = create_select_color_frame(stacked_frame)
        self.select_color_frame.grid(row=0, column=0, sticky=tk.NSEW)

        self.set_color_frame = create_set_color_frame(stacked_frame)
        self.set_color_frame.grid(row=0, column=0, sticky=tk.NSEW)

        self.config_frame = create_config_frame(stacked_frame)
        self.config_frame.grid(row=0, column=0, sticky=tk.NSEW)

        self.main_frame = create_main_frame(
            stacked_frame,
            self.toggle_page,
            [
                ("Einfarbig", self.select_color_frame),
                ("Mehrfarbig", self.set_color_frame),
                ("Farbwechsel", None),  # TODO Hier fehlt noch ein Widget.
            ],
        )
        self.main_frame.grid(row=0, column=0, sticky=tk.NSEW)

        back_button["command"] = partial(self.toggle_page, self.main_frame)
        config_button["command"] = partial(self.toggle_page, self.config_frame)

    def toggle_fullscreen(self, _event=None):
        self.attributes("-fullscreen", not self.attributes("-fullscreen"))

    @staticmethod
    def toggle_page(page):
        page.lift()


def main():
    root = Main()
    set_styles(root)
    root.mainloop()


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten