Inhalt eines Fensters durch Betätigung des Weiter-Button ersetzen

Fragen zu Tkinter.
Antworten
Halllo12134
User
Beiträge: 2
Registriert: Freitag 29. Dezember 2023, 16:45

Hallo Zusammen


Wir haben eine Frage...

Wir haben folgendes Programm:
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox

def matchmaker():
name = entry_name.get()
name = name.lower()

def erstelle_ansicht_1():
global entry_name
entry_name = Entry(master=window,
width=20)

button_weiter = Button(master=window, text="Weiter", command=zweites_fenster)
button_hilfe = Button(master=window, text="Hilfe", width=10,
command=oeffne_hilfeinformation)
label_person1 = Label(master=window, text="Angabe der 1. Person:", width=25, anchor="center")
label_person1.config(background= "#DD9FB2")
label_name = Label(master=window, text="Vorname:", width=11, anchor="center")
label_haarfarbe = Label(master=window, text="Haarfarbe:", width=11, anchor="center")
label_alter = Label(master=window, text="Alter:", width=11, anchor="center")
label_hobbys = Label(master=window, text="Hobbys:", width=11, anchor="center")

# platziert die Elemente in einem Raster
entry_name.grid(row=3, column=1)
button_hilfe.grid(row=0, column=5)
label_person1.grid(row=0, column=2)
label_name.grid(row=3, column=0)
button_weiter.grid(row=13, column=5)
label_haarfarbe.grid(row=10,column=0)
label_alter.grid(row=11,column=0)
label_hobbys.grid(row=7, column=0)

#hobby_checkbutons
var1 = IntVar()
Checkbutton(master=window, text="Backen/Kochen", variable=var1).grid(row=7, column=1)
var2 = IntVar()
Checkbutton(master=window, text="Sport", variable=var2).grid(row=7, column=2)
var3 = IntVar()
Checkbutton(master=window, text="Musik", variable=var3).grid(row=7, column=3)
var4 = IntVar()
Checkbutton(master=window, text="Lesen", variable=var4).grid(row=7,column=4)
var5 = IntVar()
Checkbutton(master=window, text="Wandern", variable=var5).grid(row=7, column=5)
var6 = IntVar()
Checkbutton(master=window, text="Reisen", variable=var6).grid(row=8, column=4)
var7 = IntVar()
Checkbutton(master=window, text="Essen", variable=var7).grid(row=8, column=3)
var8 = IntVar()
Checkbutton(master=window, text="Tiere", variable=var8).grid(row=8, column=2)
var9 = IntVar()
Checkbutton(master=window, text="Autos/Fahrzeuge", variable=var9).grid(row=8, column=1)
var10 = IntVar()
Checkbutton(master=window, text="Kunst", variable=var10).grid(row=8, column=5)

# Haarfarbe Combobox
selected_colour = StringVar()
choice = Combobox(window,
textvariable=selected_colour)
choice["values"] = ("braun", "blond", "schwarz", "rot", "gefärbt")
choice["state"] = "readonly"
choice.grid( row=10, column=1)
choice.current()

# Radiobutton Alter
var11 = IntVar()
Radiobutton(master=window, text="u11 Jahre ", variable=var11).grid(row=11, column=1)
var12 = IntVar()
Radiobutton(master=window, text="11-14 Jahre", variable=var12).grid(row=12,column=1)
var13 = IntVar()
Radiobutton(master=window, text="15-18 Jahre", variable=var13).grid(row=13,column=1)
var14 = IntVar()
Radiobutton(master=window, text="19-23 Jahre", variable=var14).grid(row=11,column=2)
var15 = IntVar()
Radiobutton(master=window, text="24-30 Jahre", variable=var15).grid(row=12,column=2)
var16 = IntVar()
Radiobutton(master=window, text="ü30 Jahre ", variable=var16).grid(row=13,column=2)

label_name.grid_configure(padx=0)
entry_name.grid_configure(padx=0)


def zweites_fenster():
for widget in window.winfo_children():
widget.destroy()

def oeffne_hilfeinformation():
messagebox.showinfo(title="Hilfe", message="Matchmaker\n\nDieses Programm berechnet, wie gut zwei Personen zusammenpassen. Füllen Sie den Fragebogen aus, klicken Sie auf 'Weiter', woraufhin Sie zum nächsten Formular, mit Fragen über die zweite Person gelangen. Klicken Sie auf 'Auswerten' und sehen Sie die Ergebnisse. "
"\n\n©2023 by Halllo12134 ")

# Fenstereinstellungen
window = Tk()
window.title("Matchmaker")
window.resizable(width=False, height=False)
window.configure(background="#FD9FB2")

erstelle_ansicht_1()

for e in window.winfo_children():
e.grid_configure(padx=10)
e.grid_configure(pady=10)

window.mainloop()


Nun möchten wir, dass durch die Betätigung des Weiter-Buttons anstelle einer leeren Seite der Inhalt der 1. Ansicht erscheint, so, dass man jedoch die Daten für die 2. Person eingeben kann.


Wir bitten um Hilfe und sind euch schon jetzt sehr dankbar :)...
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Halllo12134: Am besten ihr geht noch einmal einen Schritt zurück und fangt ohne GUI an. Also Funktionen lernen, denn die braucht man um Klassen zu verstehen, und dann Klassen, denn die braucht man für jede nicht-triviale GUI, und wenn Klassen als Thema sitzen, dann kann man sich mit einer GUI ereignisbasierte Programmierung als weiteres neues Thema dazu holen. Wenn man mit GUIs anfängt, muss man im Grunde alles auf einmal können, beziehungsweise lernen.

Anmerkungen zum Quelltext:

Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 140 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen. Durch * aus `tkinter` *und* `tkinter.ttk` ist es ja auch tatsächlich schon nicht mehr leicht zu sagen was da jetzt aus welchem der beiden Module stammt.

Auf Modulebene sollte nur Code stehen 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. Überlege mal wieviele von den lokalen Namen man so deklarieren müsste damit das überhaupt so funktionieren kann. Da könnte man sich den lokalen Namensraum in Funktionen im Grunde komplett sparen, dabei ist das eine *der* Eigenschaften von Funktionen.

Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt die sie ausführen, damit der Leser weiss was da passiert, und um sie leicht(er) von eher passiven Werten unterscheiden zu können. `matchmaker()` und `zweites_fenster()` sind keine Tätigkeiten.

Man nummeriert keine Namen. Dann will man entweder bessere Namen vergeben, oder gar keine Einzelnamen- und werte sondern eine Datenstruktur. Oft eine Liste.

Die Reihenfolge von Worten haben in der Regel eine Bedeutung. Ein `entry_name` ist der Name eines Eingabeelements. Ein `name_entry` ist ein Eingabeelement für einen Namen. Bei `label` ist es noch irreführender, weil „to label“ auch eine Tätigkeit ist.

Die Reihenfolge im Code in der die Elemente erstellt und dann platziert werden ist sehr unübersichtlich. Das sollte am besten in der Reihenfolge passieren in der das auch in der GUI angeordnet ist. Und nicht erst einen Schwung an Elementen erstellen und dann erst alle platzieren. Das grid ist auch nicht so wirklich dicht besetzt. Der höchste Zeilenindex ist 13, es gibt aber bei weitem nicht so viele Zeilen in dem Grid.

Bei `Label`\s macht es in aller Regel keinen Sinn eine Breite anzugeben.

Direkt nach dem Erstellen Optionen von Widgets zu setzen macht auch keinen Sinn, denn das kann man ja *beim Erstellen* schon angeben.

Bei den Checkboxen wäre semantisch ein `BooleanVar`-Objekt passender als `IntVar`.

Eine `tk.Variable` pro Radiobutton macht keinen Sinn. Dann sind das alles ”Checkboxen” die man einmal ausgewählt nicht wieder abwählen kann.

Zwischenstand (ungetestet):

Code: Alles auswählen

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


def zeige_hilfeinformation():
    messagebox.showinfo(
        title="Hilfe",
        message=(
            "Matchmaker\n\n"
            "Dieses Programm berechnet, wie gut zwei Personen zusammenpassen."
            " Füllen Sie den Fragebogen aus, klicken Sie auf 'Weiter',"
            " woraufhin Sie zum nächsten Formular, mit Fragen über die zweite"
            " Person gelangen. Klicken Sie auf 'Auswerten' und sehen Sie die"
            " Ergebnisse.\n\n"
            "©2023 by Halllo12134"
        ),
    )


def erstelle_fensterinhalt(master):
    ttk.Button(
        master, text="Hilfe", width=10, command=zeige_hilfeinformation
    ).grid(row=0, column=5)

    ttk.Label(master, text="Angabe der 1. Person:", background="#DD9FB2").grid(
        row=0, column=2
    )

    ttk.Label(master, text="Vorname:").grid(row=1, column=0)
    name_entry = ttk.Entry(master, width=20)
    name_entry.grid(row=1, column=1)

    ttk.Label(master, text="Hobbyies:").grid(row=2, column=0)

    hobby_texte = [
        "Backen/Kochen",
        "Sport",
        "Musik",
        "Lesen",
        "Wandern",
        "Reisen",
        "Essen",
        "Tiere",
        "Autos/Fahrzeuge",
        "Kunst",
    ]
    hobby_text_to_boolean_var = {}
    for i, text in enumerate(hobby_texte):
        variable = tk.BooleanVar()
        hobby_text_to_boolean_var[text] = variable
        row_offset, column_offset = divmod(i, 5)
        ttk.Checkbutton(master, text=text, variable=variable).grid(
            row=2 + row_offset, column=1 + column_offset
        )

    hair_colour_var = tk.StringVar()
    ttk.Label(master, text="Haarfarbe:").grid(row=4, column=0)
    ttk.Combobox(
        master,
        textvariable=hair_colour_var,
        values=["braun", "blond", "schwarz", "rot", "gefärbt"],
        state="readonly",
    ).grid(row=4, column=1)

    age_group_var = tk.IntVar()
    ttk.Label(master, text="Alter:").grid(row=5, column=0)
    for i, text in enumerate(
        ["<11", "11-14", "15-18", "19-23", "24-30", ">30"]
    ):
        row_offset, column_offset = divmod(i, 2)
        ttk.Radiobutton(
            master, text=f"{text} Jahre", value=i, variable=age_group_var
        ).grid(row=5 + row_offset, column=1 + column_offset)

    ttk.Button(
        master,
        text="Weiter",
        command=partial(erstelle_neuen_fensterinhalt, master),
    ).grid(row=8, column=5)

    for widget in master.winfo_children():
        widget.grid_configure(padx=10, pady=10)


def erstelle_neuen_fensterinhalt(master):
    for widget in master.winfo_children():
        widget.destroy()


def main():
    window = tk.Tk()
    window.title("Matchmaker")
    window.resizable(width=False, height=False)
    window.configure(background="#FD9FB2")

    erstelle_fensterinhalt(window)

    window.mainloop()


if __name__ == "__main__":
    main()
Zur eigentlichen Frage: So macht man das nicht. GUIs werden in der Regel von Anfang an komplett erstellt. Man löscht keinen Fensterinhalt um dann neue Elemente dort zu platzieren. Wenn man etwas wie einen ”Wizard” mit mehreren Seiten hat, zwischen denen man Wechseln kann, dann packt man in Tk mehrere Frames mit den Seiteninhalten in *eine* Grid-Zelle übereinander und lässt die jeweils den gesamten Platz ausfüllen, und holt dann per `raise()`-Methode immer den gewünschten `Frame` nach oben.

Andere/modernere GUI-Rahmenwerke haben so etwas meistens schon vorgefertigt, weil das ein Muster ist, was man in GUIs öfter mal hat.

Der Hilfe-Text lässt auch vermuten, dass diese Personendaten-Eingabe-Seite in einer Klasse stecken sollte, denn die wird ja zweimal benötigt, und man schreibt den fast gleichen Code natürlich nicht zweimal.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Halllo12134
User
Beiträge: 2
Registriert: Freitag 29. Dezember 2023, 16:45

Guten Abend, okay vielen Dank...
So detailliert haben wir dies in der Schule bis anhin noch nicht gelernt.

Wie müssten man dies dann machen? Gibt es irgendeine Webseite wo wir dies genauer nachschauen könnten? Weil es interessiert uns sehr, aber unser Lehrer will uns nicht so ins Detail erklären, sondern sagt bloss: "so genau schauen wir dies nicht an, das braucht ihr später eh nicht mehr..." Finde ich voll schade.

Vielen Dank für Ihre Hilfe...
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Was denn genau nachschauen? In der Python-Dokumentation gibt es ein Tutorial zu Python-Grundlagen. Das `tkinter`-Modul ist dokumentiert. Wobei die Dokumentation nicht den gesamten Tk-Umfang beschreibt, sondern mehr die Anbindung zu Tk als Thema hat. Also wie Tk in Python eingebunden ist, so dass man die Tk-Dokumentation auf Python anwenden kann. Das ist eigentlich bei allen GUI-Rahmenwerken so, dass man am besten so viel von der dort verwendeten Programmiersprache lernen sollte, dass man die Originaldokumentation lesen kann. Die Dokumentation von `tkinter` enthält auch eine ganze Reihe von Links auf externe Dokumentation.

Es empfiehlt sich auch mit der Programmlogik anzufangen und die unabhängig von einer GUI zu schreiben. Denn ob man da eine GUI drauf setzt, und wenn ja welche, oder ein Konsolenprogramm draus macht, oder eine Webanwendung, der Kern bleibt ja gleich. Also welche Daten werden erfasst, wie werden die Verarbeitet, was ist dafür die passende Struktur.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Halllo12134 hat geschrieben: Freitag 29. Dezember 2023, 20:49 unser Lehrer will uns nicht so ins Detail erklären, sondern sagt bloss: "so genau schauen wir dies nicht an, das braucht ihr später eh nicht mehr..." Finde ich voll schade.

Vielen Dank für Ihre Hilfe...
Das find ich auch schade. Das entscheidet er doch gar nicht, was ihr später brauchen wollt und werdet. Also gut, dass ihr euch selbst weiter anschaut. Seid ihr schon weiter gekommen?
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@kebap: in der Schule und auch sonst meist ist es so, dass viele Dinge nur oberflächlich behandelt werden, weil für die Tiefe keine Zeit ist, weil Breite als wichtiger angesehen wird.
Dazu sollte es aber auch Aufgaben geben, die ohne das tiefe Wissen sinnvoll bearbeitbar sind. GUI ohne OOP ist aus meiner Sicht nicht sinnvoll.
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Das stimmt, und dazu kommt, dass Lehrer immer 20+ Schüler gleichzeitig unterrichten. Wenn 2-3 davon Überflieger sind, aber andere noch an Grundlagen zu knabbern haben, wo landet das Tempo?
Ich finde daher immer gut, wenn es auch schwierigere Aufgaben gibt, auf die man sich bei Langeweile selbst stürzen kann. Wenn man davon nicht alles sofort alleine hinbekommt, kein Problem, ist nur zum Spaß!
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Antworten