Tabs im Hauptprogramm aufrufen, um sie mit Widgets zu befüllen

Fragen zu Tkinter.
Antworten
Tommilein
User
Beiträge: 10
Registriert: Mittwoch 16. September 2020, 03:56

Hallo,

ich gehöre zu den eher Ahnungslosen und würde mich freuen, wenn mir jemand unter die Arme greifen könnte.
In meinem Programm erzeuge ich das Notebook-Objekt tabControl und befülle es mittels der submit-Funktion mit Tabs. Die Namen der Tabs richten sich nach der Nutzereingabe. Das klappt soweit auch. Nun möchte im Hauptprogramm die angelegten Tabs mit Widgets befüllen, habe aber keinen Schimmer, wie ich die Tabs aufrufen kann. Wäre toll , wenn mir jemand helfen könnte.

Tommilein

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.geometry("500x200")
tabControl = ttk.Notebook(root)
tab1 = ttk.Frame(tabControl)
tabControl.add(tab1, text='Eingabe der Spielernamen')

def submit():
tab = ttk.Frame(tabControl)
tabControl.add(tab, text=name_entry.get())

name_var = tk.StringVar(tab1)
name_label = tk.Label(tab1, text = ('Name:'))
name_entry = tk.Entry(tab1, textvariable = name_var)
name_label.grid(row=3, column=0)
name_entry.grid(row=3,column=1)
sub_btn=tk.Button(tab1,text = 'Submit', command = submit)
sub_btn.grid(row=3,column=3)

tabControl.pack(expand=1, fill="both")

root.mainloop()
Benutzeravatar
peterpy
User
Beiträge: 188
Registriert: Donnerstag 7. März 2013, 11:35

Hallo Tommilein,
bitte benutze den Code Tag </> um den Code zu zeigen, dann bleiben die Einrückungen erhalten.
Ich glaube nicht, dass dein Code funktioniert, Du rufst nicht mal die Funktion submit auf.
Der Aufruf ist in der Funktion submit und die Taste wird nie erstellt.
Dann verwendest Du die Variablen name_entry und name_var bevor diese erzeugt werden.

Du musst erst die Benutzeroberfläche erstellen und dann in weiteren Funktionen die Tab's erstellen.
Oder noch besser Du packst deinen Code in Klassen.

Gruss
Peter
Tommilein
User
Beiträge: 10
Registriert: Mittwoch 16. September 2020, 03:56

Hallo Peter,
vielen Dank für Deine Antwort und sorry wegen der verhundsten Codedarstellung.
Also, die Sache funktioniert soweit schon.
Drücke ich den Submit-Button, wird die Funktion Submit gestartet und ein Tab erzeugt, der den Namen trägt, der vorher in das Entry-Feld eingegeben wurde.
Ich habe danach also mein Notebook mit zwei Tabs. Mein Problem ist: Wie komme ich an den neu erstellten Tab außerhalb der Funktion ran? Ich würde beispielsweise gerne ein Label im Frame des neuen Tabs plazieren. Nun muss ja, soweit ich das verstanden habe, die Tab_id des neuen Tabs beim Erstellen des Labels angegeben werde. Wo kriege ich die her?
Hier nochmal der Code:

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk

root = tk.Tk() 
root.geometry("500x200")
tabControl = ttk.Notebook(root) 
tab1 = ttk.Frame(tabControl) 
tabControl.add(tab1, text='Eingabe der Spielernamen')

def submit():
    tab = ttk.Frame(tabControl)
    tabControl.add(tab, text=name_entry.get())

name_var = tk.StringVar(tab1)
name_label = tk.Label(tab1, text = ('Name:'))
name_entry = tk.Entry(tab1, textvariable = name_var)
name_label.grid(row=3, column=0)
name_entry.grid(row=3,column=1)
sub_btn=tk.Button(tab1,text = 'Submit', command = submit)
sub_btn.grid(row=3,column=3)

tabControl.pack(expand=1, fill="both")

root.mainloop()
Vielen herzlichen Dank im Voraus,
Tommilein
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tommilein: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Also keine globalen Variablen. Was zwangsläufig dazu führt, das man für jede nicht-triviale GUI objektorienterte Programmierung (OOP) in Form von eigenen Klassen benötigt.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also `tab_control` und nicht `tab_control`. Wobei der Name auch inhaltlich ein bisschen komisch für ein `Notebook`-Objekt ist. `notebook` wäre da wesentlich naheliegender. Noch besser wäre es wenn der Name nicht nur aussagen würde was das ist, sondern auch welche konkrete Bedeutung dieses Anzeigeelement im vorliegenden Programm hat.

Namen nummeriert man nicht. Man will dann entweder bessere Namen verwenden, oder gar keine Einzelnamen sondern eine Datenstruktur. Oft eine Liste. Im Falle von `tab1` sollte man dagegen einfach nur die unsinnige 1 weglassen.

Namen sollten keine kryptischen Abkürzungen enthalten, oder nur daraus bestehen Wenn man `submit_button` meint, sollte man nicht `sub_btn` schreiben. Wobei das `sub` tatsächlich irreführend ist, da das eine gebräuchliche Vorsilbe mit der Bedeutung „Unter…“ ist. Das liest keiner im ersten Anlauf als „submit“. Letztlich muss man diesem Objekt aber auch gar nicht zwingend überhaupt einen Namen geben.

Wenn es eine Konstante im `tkinter`-Modul gibt, sollte man die auch verwenden und nicht den Wert selbst als Zeichenkette in den Quelltext schreiben.

Man sollte Elemente nicht an scheinbar willkürlichen Positionen per `grid()` setzen. Wenn man nur eine Zeile verwendet ist das die 0 und nicht die 3. Und Spalten leer lassen ist auch verwirrend. Letztlich hätte man den ersten Reiter auch mit `pack()` befüllen können wenn man tatsächlich nur nebeneinander platziert.

Zwischenstand:

Code: Alles auswählen

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


def submit(notebook, name_entry):
    tab = ttk.Frame(notebook)
    notebook.add(tab, text=name_entry.get())


def main():
    root = tk.Tk()
    root.geometry("500x200")

    name_var = tk.StringVar()

    notebook = ttk.Notebook(root)
    tab = ttk.Frame(notebook)
    tk.Label(tab, text="Name:").pack(side=tk.LEFT)
    name_entry = tk.Entry(tab, textvariable=name_var)
    name_entry.pack(side=tk.LEFT)
    tk.Button(
        tab, text="Submit", command=partial(submit, notebook, name_entry)
    ).pack(side=tk.LEFT)
    notebook.add(tab, text="Eingabe der Spielernamen")
    notebook.pack(expand=True, fill=tk.BOTH)

    root.mainloop()


if __name__ == "__main__":
    main()
Der Stand soweit ging noch mit `functools.partial()` anstelle von einer eigenen Klasse, aber spätestens wenn Callbacks ”Rückgabewerte” haben sollten, geht es nicht mehr anders das sinnvoll umzusetzen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Tommilein
User
Beiträge: 10
Registriert: Mittwoch 16. September 2020, 03:56

Hei __blackjack__,

vielen Dank für die ausführliche Antwort, werde ich mir einverleiben.

Gruß Tommilein
Antworten