Notebook(tabs) füllen

Fragen zu Tkinter.
Antworten
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

Hallo Python-Forum,

ich bin noch recht neu was Python betrifft und aus Büchern werde ich langsam nicht mehr schlauer bzw. Sie helfen mir nicht mehr so weiter wie ichs gerne hätte.
Mein Problem besteht darin das ich ein Programm mit Python "nach bauen" soll, Struktur ist an sich kein Problem aber jetzt ist es eher so das ich eine Menüleiste
einbauen muss quasi Tabs, welche ich aber schon erfolgreich mit dem Modul "Notebook" erstellen/anlegenkonnte.

Diese hab ich in einer Klasse angelegt und nun möchte ich aber eine Klasse, z.B. Kundenanlegen, in einem Tab aufrufen. Quasi Ausgabe der Eingabefelder mit den Button Speichern und Beenden von mir aus.

Ich hoffe ihr versteht was ich meine und könnt mir helfen :)

Gruß Kalli
BlackJack

@Kalli87: Wo ist denn das Modul `Notebook` her? Oder meinst Du die Klasse `Notebook` aus dem `ttk`-Modul?

Klassen nach Tätigkeiten benennen und aufrufen zu wollen klingt falsch. Klassen repräsentieren Vorlagen für ”Dinge” im weitesten Sinne. Sie fassen einen Zustand, also Daten, und Methoden, also Operationen/Tätigkeiten auf diesen Daten zu einer Einheit zusammen. Und wenn man eine Klasse aufruft, bekommt man ein konkretes Exemplar von so einem ”Ding”.

Da ich die Frage nicht wirklich verstehe, rate ich mal: Um Inhalt in ein Tab zu bekommen erstellt man den, zum Beispiel ein `Frame`-Exemplar, und fügt ihn mit der `add()`-Methode zum `Notebook`-Objekt hinzu.
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

Hallo BlackJack,
danke für die schnelle Antwort.

Das Modul "Notebook" kommt aus ttk-Modul, vllt hab ich auch wieder einen riesen großen Denkfehler was auch nicht das erste mal wäre ;)

Ok das mit der "add()-Methode" ist mir nicht unbekannt.
Wenn ich dich richtig verstanden habe müsste ich quasi nur, als Beispiel, in der Klasse "Tabs" die Funktion/Formular "Kunde(neu)" einbinden?

Hier mal der Code, damit es zum besseren Verständnis beiträgt

Code: Alles auswählen

import tkinter
import tkinter.ttk
import tkinter.messagebox
from tkinter import *
from tkinter.ttk import *
import sys
import psycopg2

def beenden():
    if tkinter.messagebox.askyesno('Beenden', 'Wollen Sie wirklich das Programm beenden?'):
        main.destroy()


main = tkinter.Tk()
main.title("WBS")

def Kunde_neu():
    entry_titel = tkinter.Entry(main)
    entry_name = tkinter.Entry(main)
    entry_vorname = tkinter.Entry(main)
    entry_anrede = tkinter.Entry(main)
    entry_geburtstag = tkinter.Entry(main)
    entry_staat = tkinter.Entry(main)
    entry_steuer = tkinter.Entry(main)
    entry_finanz = tkinter.Entry(main)
    entry_strasse = tkinter.Entry(main)
    entry_hausnr = tkinter.Entry(main)
    entry_plz = tkinter.Entry(main)
    entry_ort = tkinter.Entry(main)
    entry_land = tkinter.Entry(main)
    entry_email = tkinter.Entry(main)
    entry_telefon = tkinter.Entry(main)
    entry_telefax = tkinter.Entry(main)

    entry_titel.grid(row=1, ipady=2, ipadx=50)
    tkinter.Label(main, text="Titel", font="Helvetica 11 bold").grid(row=2, sticky="W")

    entry_name.grid(row=1, column=1, ipady=2, ipadx=50)
    tkinter.Label(main, text="Name", font="Helvetica 11 bold").grid(row=2, column=1, sticky="W")

    entry_vorname.grid(row=1, column=2, ipady=2, ipadx=50)
    tkinter.Label(main, text="Vorname", font="Helvetica 11 bold").grid(row=2, column=2, sticky="W")


    entry_anrede.grid(row=3, columnspan=2, ipadx=182, ipady=2)
    tkinter.Label(main, text="Briefanrede", font="Helvetica 11 bold").grid(row=4, sticky="W")

    entry_geburtstag.grid(row=3, column=2, ipady=2, ipadx=50)
    tkinter.Label(main, text="Geb. (TT.MM.JJJJ)", font="Helvetica 11 bold").grid(row=4, column=2, sticky="W")


    entry_staat.grid(row=5, column=0, ipady=2, ipadx=50)
    tkinter.Label(main, text="Staatsangehörigkeit", font="Helvetica 11 bold").grid(row=6, column=0, sticky="W")

    entry_steuer.grid(row=5, column=1, ipady=2, ipadx=50)
    tkinter.Label(main, text="Steueridentifikationsnr.", font="Helvetica 11 bold").grid(row=6, column=1, sticky="W")

    entry_finanz.grid(row=5, column=2, ipady=2, ipadx=50)
    tkinter.Label(main, text="Finanzamt", font="Helvetica 11 bold").grid(row=6, column=2, sticky="W")

    entry_strasse.grid(row=7, column=0, ipady=2, ipadx=50)
    tkinter.Label(main, text="Strasse", font="Helvetica 11 bold").grid(row=8, column=0, sticky="W")

    entry_hausnr.grid(row=7, columnspan=3, ipady=2, ipadx=50)
    tkinter.Label(main, text="Hausnummer", font="Helvetica 11 bold").grid(row=8, column=1, sticky="W")

    entry_plz.grid(row=9, column=1, ipady=2, ipadx=50)
    tkinter.Label(main, text="Postleitzahl", font="Helvetica 11 bold").grid(row=10, column=1, sticky="W")

    entry_ort.grid(row=9, column=0, ipady=2, ipadx=50)
    tkinter.Label(main, text="Ort", font="Helvetica 11 bold").grid(row=10, column=0, sticky="W")


    entry_telefon.grid(row=11, column=0, ipady=2, ipadx=50)
    tkinter.Label(main, text="Telefonnummer", font="Helvetica 11 bold").grid(row=12, column=0, sticky="W")

    entry_telefax.grid(row=11, column=1, ipady=2, ipadx=50)
    tkinter.Label(main, text="Telefax", font="Helvetica 11 bold").grid(row=12, column=1, sticky="W")



    b_save = tkinter.Button(main, text="Speichern", font="Helvetica 11 bold")
    b_save.grid(row=9, column = 2)

    b_end = tkinter.Button(main, text="Beenden", font="Helvetica 11 bold", command=beenden)
    b_end.grid(row=10, column = 2)
    main.mainloop()


class tabs:
  def __init__(self):
    root = Tk()
    notebook = Notebook(root)

    notebook.add(Kunde_neu(width=400, height=300), text="Kunde (neu)")
    notebook.add(Frame(width=400, height=300), text="Kunde")
    notebook.bind_all("<<NotebookTabChanged>>")
    notebook.pack()

    self.label = Label(root, text="")
    self.label.pack()
    root.mainloop()

def main():
    window = tabs()

main()

Liege ich denn so richtig? Oder hab ich doch was falsch verstanden?

Gruß Kalli
Zuletzt geändert von Anonymous am Montag 10. November 2014, 12:29, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@Kalli87: Der Quelltext macht keinen Sinn. In der `add()`-Methode muss man etwas übergeben was Tk darstellen kann, also zum Beispiel ein `Frame`-Objekt, direkt oder davon abgeleitet, und keine Funktion. Und man sollte daneben vielleicht noch mindestens den Titeltext des Tabs angeben (`text`-Argument bei `add()`).
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

Ok, jetzt bin komplett "raus"...
Hast du ein konkretes Beispiel was ich mir mal anschauen könnte?
Bzw. Ich vermute nun das du verstanden hast was ich gerne möchte, oder?

Ich hab den Code bei mir selbst wieder zum Ausgangspunkt gebracht, soll heißen "class Kunde_neu" was im Prinzip das Formular darstellen soll um Kunden anzulegen
und "class tabs" welches mir, wie der Name schon sagt, Tabs anzeigt/ausgibt oder wie auch immer es man nennen möchte.

Starte ich ich das ganze nun kommt als erstes das Formular, schließe ich das Formular kommt dann das Fenster mit den Tabs.
Ich möchte aber das "Formular" in einem "Tab" haben und wenn ich auf ein anderen "Tab" klicke soll was anderes erscheinen.

Sorry das ich mich vllt dumm anstelle aber ich denke einfach zu komplex um simple Sachen schnell zu begreifen bzw umzusetzen.

Gruß Kalli
BlackJack

@Kalli87: Das ist alles sehr verwirrend was Du da machst. Das macht ingesamt keinen Sinn. Auf Modulebene sollte man ausser Code der Konstanten, Funktionen, und Klassen definiert nichts vom Programm haben. Dann kann man auch nicht einfach so auf Werte zugreifen die nicht als Argumente an Funktionen oder Methoden übergeben wurden, was ein Programm deutlich einfacher nachzuvollziehen macht, weil man die Abhängigkeiten deutlicher sieht.

In einerm normalen Tk-Programm gibt es genau *einen* Aufruf von `mainloop()`, und zwar nach dem man die GUI-Objekte soweit erstellt und konfiguriert hat, dass man die GUI dem Benutzer das erste mal präsentieren möchte. Wenn man diese Funktion/Methode nämlich aufruft, dann geht der Programmfluss nach dieser Funktion erst weiter wenn das Hauptfenster geschlossen wurde. Das heisst in einer Funktion die nur ein Widget zur Anzeige in einerm Tab erzeugen soll, will man nicht die *Hauptschleife* des GUI-Rahmenwerks starten.

So wie die Funktion aufgerufen wird, sollte sie auch ein Widget *zurückgeben*, denn der Rückgabwert ist bei Dir ja der Wert der an `add()` zum Anzeigen in einem Tab übergeben wird.

Für die ganzen Widgets die in der Funktion erzeugt werden ist `main` ganz sicher das falsche Elternwidget. Da muss das Widget übergeben werden in dem diese Widgets direkt als Kinder dargestellt werden. Da in dem Hauptfenster das Notebook dargestellt wird, können Elemente die in dem Notebook dargestellt werden, unmöglich Kinder vom Hauptfenster sein. Wie schon gesagt, macht es Sinn ein `Frame`-Objekt für den Inhalt eines Tabs zu erstellen und alle Widgets die im Tab angezeigt werden sollen, in diesem `Frame` anzuordnen.

In der Funktion sind reichlich viele Wiederholungen von Quelltextzeilen die sich nur geringfügig durch Daten unterscheiden. Teilweise Daten die generiert werden könnten, wie die Zeilennummer im Grid. Überleg mal was das jetzt für einen Aufwand machen würde zum Beispiel in der zweiten Zeile einen Wert einzufügen. Da das sehr nach CRUD aussieht sollte man diese Eingabemaske(n) vielleicht generell deutlich allgemeiner programmieren und die Daten für eine Eingabemaske als Datenstruktur anlegen die so einer Funktion übergeben wird. Letztlich hat man ja für jedes Feld die gleiche Art von Informationen wie Beschriftung, Name des Datenbankfelds, und Typ des Datums.

Eine Beenden-Schaltfläche gehört IMHO nicht in den Tab. Das ist ja etwas was für das gesamte Programm gilt. Und entweder hast Du dann Tabs über die man das Programm beenden kann, und solche wo das nicht geht, was inkonsistent wäre, oder man müsste in jedem Tab so eine Schaltfläche haben. Das wären dann wieder unnötige Code-Wiederholungen, weil man so eine Schaltfläche dann auch *einmal* ausserhalb der Tabs anzeigen könnte. Und falls man aus irgendwelchen Gründen unbedingt so eine Schaltfläche pro Tab braucht, sollte man das in eine Funktion oder eine Klasse herausziehen um zumindest die Quelltextwiederholungen zu vermeiden.

In einem Programm darf es immer nur *ein* `Tk`-Exemplar zur gleichen Zeit geben. Das ist das *Haupt*-Fenster.
Das das eine Funktion ist statt einer Klasse wird spätestens zum Problem wenn man auf die Daten in den Eingabefeldern zugreifen möchte. Oder man muss diese ganzen Werte zum Beispiel mit `functools.partial()` binden wenn man die `command`-Funktion für die Speichern-Schaltfläche angibt.

Beim Aufruf von der Funktion übergibst Du Argumente, die Funktion erwartet aber überhaupt gar keine. Der Code ist so also gar nicht lauffähig. Selbst wenn das nicht wäre, dann definierst Du auf Modulebene ein `Tk`-Exemplar und eine Funktion beide unter dem Namen `main`.

Die Namensschreibweisen von Funktionen und Klassen halten sich nicht an den Style Guide for Python Code.
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

In vielen Punkten gebe ich dir recht.
Die Struktur ist wirklich nicht gerade die beste, liegt aber auch daran das ich selbst erst am probieren bin und am ende alles anpasse, Hauptsache es funktioniert erstmal wie es soll.

Der Punkt das viele Sachen sich vom Code wiederholen ist auch richtig, liegt aber auch wieder daran das es erstmal funktioniert wie es soll später kommt der Feinschliff.

Ich bin halt ein totaler Anfänger was Python betrifft und muss noch vieles lernen, Ich wurde ins kalte Wasser geschmissen, wie man so schön sagt und muss mich nun alleine durcharbeiten.
Ich hab mich deswegen hier angemeldet um Hilfe zu bekommen bzw. zu finden.

So wie es aussieht kann ich wieder bei "Null" anfangen, Oberfläche zu programmieren scheint doch nicht ganz einfach zu sein wie ich dachte.

Gruß Kalli
BlackJack

@Kalli87: Grafische Oberflächen erfordern die ganzen Grundlagen bis einschliesslich objektorientierter Programmierung und durch die GUI kommen dann noch ereignisbasierte Programmierung und die Strukturen und Entwurfsmuster des verwendeten GUI-Toolkits als neue Themen hinzu. Programmieren würde ich nicht mit allgemeiner GUI-Programmierung anfangen, weil man da fast alles an Vorwissen braucht was Python als objektorientierte Programmiersprache ausmacht. Und das alles *auf einmal* zu lernen ist IMHO ein bisschen viel.
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

Ich hab ja nicht mit GUI-Programmierung angefangen, ich hab ganz normal mit der Konsole angefangen.
Ein simple Konsolenprogramm was Matheaufgaben dir stellt und schaut ob deine eingegebenen Ergebnisse richtig sind.

Datenbankverwaltung im kleinem Format, sprich Datensätze hinzufügen, ändern, löschen, suchen

Da alles wunderbar funktionierte ging ich nun weiter und fing mit der GUI an.

Des Weiteren hab ich schon einige Vorkenntnisse was Objektorientierung angeht, da ich gelernter Technischer Assistent für Informatik bin und der Schwerpunkt auf PHP lag is mir vieles bekannt.
Jetzt mach ich eine Weiterbildung zum Fachinformatiker und musste durch C und C++ durch. Jetzt im Praktikum is Python meine Hauptsprache.
Ich bin in der Hinsicht ehrgeizig und will weiter kommen deswegen hab ich mich hier im Forum registriert. Um nach Hilfe zu suchen.

Gruß Kalli

ps. Ich hab mich sogar durch Codeadamy durchgeschlagen, falls das jemand kennt.
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

Vielleicht kann dir dieses Beispiel helfen:

Code: Alles auswählen

try:
    # Python2
    import Tkinter as tk
    import ttk
except ImportError:
    # Python3
    import tkinter as tk
    import tkinter.ttk as ttk

root = tk.Tk()
# use width x height + x_offset + y_offset (no spaces!)
root.geometry("%dx%d+%d+%d" % (300, 200, 100, 50))
root.title('testing the ttk.Notebook')

nb = ttk.Notebook(root)
nb.pack(fill='both', expand='yes')

# create a child frame for each page
f1 = tk.Frame(bg='red')
f2 = tk.Frame(bg='blue')
f3 = tk.Frame(bg='green')

# create the pages, text goes on the tabs
nb.add(f1, text='page1')
nb.add(f2, text='page2')
nb.add(f3, text='page3')

# put a button widget on child frame f1 on page1
button1 = tk.Button(f1, text='button1')
button1.pack(side='left', anchor='nw', padx=3, pady=5)

# put a combo box widget on child frame f2 on page2
combo = ttk.Combobox(f2)
shop_list = ['grapes', 'pears', 'onions']
combo['values'] = shop_list
combo.set(shop_list[0])
combo.pack(side='left', anchor='nw', padx=3, pady=5)

# put something different into frame f3 on page3
listbox = tk.Listbox(f3, bg='yellow')
for item in shop_list:
    listbox.insert('end', item)
listbox.pack(side='left', anchor='nw', padx=3, pady=5)

root.mainloop()
Atomkraftwerkaktienbesitzer
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

@Ene Uran

Ja hat es, danke :)
Antworten