Grundsätzlicher Aufbau eines Programms mit mehreren Fenstern

Fragen zu Tkinter.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

:D habe ich grade selbst rausgefunden, um das Fenster zu aktualisieren benutze ich jetzt self.master.update() .
und das in set_main_frame jetzt master.update() ausgeführt wird bevor der Mainloop gestartet wird ist kein Problem?
Und noch was: Wie kann ich mir das vorstellen, wenn der Mainloop läuft, gehts dannach ja erst weiter wenn das Fenster zerstört/geschlossen wird. Es können aber Funktionen aus dem Mainloop gestartet werden, die werden dann so in den mainloop gezogen und werden da ausgeführt, kann man sich das so in etwa vorstellen?
Das script ist also so wie es ist (mit update() und dem einmaligen mainloop()) perfekt?
Gruß
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Gibt es Schwierigkeiten, wenn du master.update() nicht benützt? Arbeitest du unter Windows?

Gruß wuf :wink:
Take it easy Mates!
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Nö, gibts nicht :D
Hatte das für die Lösung eines Problems gehalten, das scheinbar ein ganz anderes war, danke
Habe das Ganze also ausgebessert und noch ein Toplevelfenster hinzugefügt: Ist das so also alles ok?

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: cp1252 -*-

#Module importieren
import tkinter as tk

#Klasse definieren
class App:
    #root/master initialisieren
    def __init__(self):
        self.master = tk.Tk()
        self.master.protocol("WM_DELETE_WINDOW", self.quit_app)
        self.master.title("Frames")
        self.set_main_frame()
        self.master.mainloop()
        
    #Hauptfenster starten
    def set_main_frame(self):
        #Hauptframe definieren
        self.main_frame = tk.Frame(self.master)
        self.main_frame.pack()
        #Widgets initialisieren
        self.hallo_label = tk.Label(self.main_frame, text = "Hallo Tkinter!")
        self.hallo_button = tk.Button(self.main_frame, text = "Weiter", command = self.set_entry_frame)
        self.quit_button = tk.Button(self.main_frame, text = "Beenden", command = self.quit_app)
        #Widgets positionieren
        self.hallo_label.grid(row = 0, column = 0, columnspan = 2)
        self.hallo_button.grid(row = 1, column = 0)
        self.quit_button.grid(row = 1, column = 1)
        #Bestätigung in der Konsole
        print("Hauptfenster geöffnet")
        
    #Einsellungsfenster starten
    def set_entry_frame(self):
        #Einstellungsframe definieren
        self.entry_frame = tk.Frame(self.master)
        #Hauptframe schließen
        self.main_frame.destroy()
        self.entry_frame.pack()
        #Widgets initialisieren
        self.head_label = tk.Label(self.entry_frame, text = "Eingaben")
        self.entry1_label = tk.Label(self.entry_frame, text = "Eingabe 1")
        self.entry1_entry = tk.Entry(self.entry_frame)
        self.entry2_label = tk.Label(self.entry_frame, text = "Eingabe 2")
        self.entry2_entry = tk.Entry(self.entry_frame)
        self.safe_entry_button = tk.Button(self.entry_frame, text = "Ausgeben", command = self.set_request_win)
        #Widgets positionieren
        self.head_label.grid(row = 0, column = 0, columnspan = 2)
        self.entry1_label.grid(row = 1, column = 0)
        self.entry1_entry.grid(row = 1, column = 1)
        self.entry2_label.grid(row = 2, column = 0)
        self.entry2_entry.grid(row = 2, column = 1)
        self.safe_entry_button.grid(row = 3, column = 0, columnspan = 2)
        #Bestätigung in der Konsole
        print("Eingabefenster geöffnet")

    #Bestätigung oder Abbruch
    def set_request_win(self):
        #Eingaben auslesen
        self.entry = [self.entry1_entry.get(), self.entry2_entry.get()]
        #Toplevel Fenster definieren
        self.request_win = tk.Toplevel()
        self.request_win.title("Wirklich?")
        #Widgets initialisieren
        self.question_label = tk.Label(self.request_win, text = "Möchtest du den Text wirklich ausgeben?")
        self.agree_button = tk.Button(self.request_win, text = "Also los, gib ihn aus!", command = self.print_entry)
        self.main_button = tk.Button(self.request_win, text = "Ich überlegs mir...", command = self.to_main)
        self.quit_button = tk.Button(self.request_win, text = "Sofort alles beenden!", command = self.quit_app)
        #Widgets positionieren
        self.question_label.grid(row = 0)
        self.agree_button.grid(row = 1, sticky = "w,e")
        self.main_button.grid(row = 2, sticky = "w,e")
        self.quit_button.grid(row = 3, sticky = "w,e")        

    #Eingaben verwerten und zum Hauptfenster zurückkehren
    def print_entry(self):
        ###Eingaben verwerten#########
        for setting in self.entry:   #
            print(setting)           #
        ##############################
        #Nachfragefenster schließen
        self.request_win.destroy()
        #Ausgabeframe schließen
        self.entry_frame.destroy()
        #Hauptframe starten
        self.set_main_frame()

    def to_main(self):
        self.request_win.destroy()
        self.entry_frame.destroy()
        self.set_main_frame()

    def quit_app(self):
        print("Programm beendet")
        self.master.destroy()

#App ausführen (Instanz der Klasse App erstellen)        
app = App()
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

OK. Es geht doch mit dem Toplevel-Fenster. Ich persönlich brauche eigentlich nur Toplevels für Dialoge. Platzieren und Größe ändern kannst du mit der geometry-Methode wie bei einem Hauptfenster. Für die Position auf dem Bildschirm brauche:

Code: Alles auswählen

toplevel_win.geometry('+{0}+{1}'.format(xpos, ypos))
Für die Abmessungen brauche:

Code: Alles auswählen

toplevel_win.geometry('{0}x{1}'.format(width, height))
Bei Toplevel-Fenstern kann es vorkommen dass sie sich hinter den auf dem Desktop vorhandene Fenstern verstecken (rutschen).

Gruß wuf :wink:
Take it easy Mates!
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Naja, Problem 1 ist ein Fenster was das vorherige ersetzt (also die Frame-Methode)
und Problem 2 ist eben so ein Fenster was noch zusetzlich angezeigt wird (Toplevels).
Steckt ja nun beides in dem Script, und wenn der Code sauber ist, mache ich mich drann eine saubere Version meines Gamelimiters zu Programmieren, dazu habe ich dieses Thema erstellt.
Ist der Code so wie er jetzt ist also sauber?
Gruß
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Für das erstellen von Python Skripts würde ich unbeding den Leitfaden PEP 8 anwenden. Sauber ist für mich, wenn ich nach einem Monat noch interpretieren kann was das geschriebene machen sollte. Code lässt sich meistens noch verbessern und verfeinern. OK ich wünsche dir noch viel Spass bei deinem Vorhaben.

Gruß wuf :wink:
Take it easy Mates!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Also bei der Instanziierung einer Klasse unerwartet eine mainloop zu starten halte ich für alles andere als elegant...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Leonidas hat geschrieben:Also bei der Instanziierung einer Klasse unerwartet eine mainloop zu starten halte ich für alles andere als elegant...
Habe ich nicht verstanden?

Gruß wuf :wink:
Take it easy Mates!
deets

Den mainloop() sollte man nicht in App.__init__ aufrufen. Denn das ist sehr ueberraschend - ein Konstruktor sollte ein Objekt zurueckgeben, und nicht unendlich lange laufen.

Besser ist es, eine explizite App.mainloop()-Methode zu machen, die dann an das root-Objekt delegiert.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

So, der mainloop und das Starten der ersten Frame sind in einer neuen Funktion, ich habe eine main()-Funktion hinzugefügt und alle überflüssigen self's entfernt, das ganze sieht jetzt also so aus:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: cp1252 -*-

#Module importieren
import tkinter as tk

#Klasse definieren
class App:
    #root/master initialisieren
    def __init__(self, master):
        self.master = master
        self.master.protocol("WM_DELETE_WINDOW", self.quit_app)
        self.master.title("Frames")

    #Hauptfenster initialiesieren und starten
    def mainloop(self):
        self.set_main_frame()
        self.master.mainloop()
        
    #Hauptfenster starten
    def set_main_frame(self):
        #Hauptframe definieren
        self.main_frame = tk.Frame(self.master)
        self.main_frame.pack()
        #Widgets initialisieren
        hallo_label = tk.Label(self.main_frame, text = "Hallo Tkinter!")
        hallo_button = tk.Button(self.main_frame, text = "Weiter", command = self.set_entry_frame)
        quit_button = tk.Button(self.main_frame, text = "Beenden", command = self.quit_app)
        #Widgets positionieren
        hallo_label.grid(row = 0, column = 0, columnspan = 2)
        hallo_button.grid(row = 1, column = 0)
        quit_button.grid(row = 1, column = 1)
        #Bestätigung in der Konsole
        print("Hauptfenster geöffnet")
        
    #Einsellungsfenster starten
    def set_entry_frame(self):
        #Einstellungsframe definieren
        self.entry_frame = tk.Frame(self.master)
        #Hauptframe schließen
        self.main_frame.destroy()
        self.entry_frame.pack()
        #Widgets initialisieren
        head_label = tk.Label(self.entry_frame, text = "Eingaben")
        entry1_label = tk.Label(self.entry_frame, text = "Eingabe 1")
        self.entry1_entry = tk.Entry(self.entry_frame)
        entry2_label = tk.Label(self.entry_frame, text = "Eingabe 2")
        self.entry2_entry = tk.Entry(self.entry_frame)
        safe_entry_button = tk.Button(self.entry_frame, text = "Ausgeben", command = self.set_request_win)
        #Widgets positionieren
        head_label.grid(row = 0, column = 0, columnspan = 2)
        entry1_label.grid(row = 1, column = 0)
        self.entry1_entry.grid(row = 1, column = 1)
        entry2_label.grid(row = 2, column = 0)
        self.entry2_entry.grid(row = 2, column = 1)
        safe_entry_button.grid(row = 3, column = 0, columnspan = 2)
        #Bestätigung in der Konsole
        print("Eingabefenster geöffnet")

    #Bestätigung oder Abbruch
    def set_request_win(self):
        #Eingaben auslesen
        self.entry = [self.entry1_entry.get(), self.entry2_entry.get()]
        #Toplevel Fenster definieren
        self.request_win = tk.Toplevel()
        self.request_win.title("Wirklich?")
        #Widgets initialisieren
        question_label = tk.Label(self.request_win, text = "Möchtest du den Text wirklich ausgeben?")
        agree_button = tk.Button(self.request_win, text = "Also los, gib ihn aus!", command = self.print_entry)
        main_button = tk.Button(self.request_win, text = "Ich überlegs mir...", command = self.to_main)
        quit_button = tk.Button(self.request_win, text = "Sofort alles beenden!", command = self.quit_app)
        #Widgets positionieren
        question_label.grid(row = 0)
        agree_button.grid(row = 1, sticky = "w,e")
        main_button.grid(row = 2, sticky = "w,e")
        quit_button.grid(row = 3, sticky = "w,e")        

    #Eingaben verwerten und zum Hauptfenster zurückkehren
    def print_entry(self):
        ###Eingaben verwerten#########
        for setting in self.entry:   #
            print(setting)           #
        ##############################
        #Nachfragefenster schließen
        self.request_win.destroy()
        #Ausgabeframe schließen
        self.entry_frame.destroy()
        #Hauptframe starten
        self.set_main_frame()

    def to_main(self):
        self.request_win.destroy()
        self.entry_frame.destroy()
        self.set_main_frame()

    def quit_app(self):
        print("Programm beendet")
        self.master.destroy()

#Mainfunktion (ausführen der App)        
def main():
    root = tk.Tk()
    app = App(root)
    app.mainloop()

if __name__ == "__main__":
    main()
Noch weitere Verbesserungsmöglichkeiten? :D
Danke
Gruß
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Leonidas & deets

Danke für eure Hinweise. Das wusste ich noch nicht. Mein Frage an euch kann ich das folgende Skript als Startvorlage für eine Tkinter-Anwendung verwenden oder sieht ihr da auch bestimmte heikle Punkte?

Code: Alles auswählen

try:
    #~~ For Python 2.x
    import Tkinter as tk
except ImportError:
    #~~ For Python 3.x
    import tkinter as tk

APP_WIN_XPOS = 50
APP_WIN_YPOS = 50

class App(object):
    
    def __init__(self):
        self.win = tk.Tk()
        self.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))
        self.win.protocol("WM_DELETE_WINDOW", self.close)
    
    def run(self):
        self.win.mainloop()
    
    def close(self):
        self.win.destroy()
            
app = App()
app.win.title("Tk App Templates")

app.run()
Gruß wuf :wink:
Take it easy Mates!
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Also soweit ich wüsste wäre es ok, aber Leonidas meinte, ich sollte keinen Code auf Modulebene schreiben, also main() funktion und if __name__ == "__main__":
main()
Gruß
Zuletzt geändert von Scriptinggamer am Dienstag 17. Juli 2012, 09:39, insgesamt 1-mal geändert.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Scriptinggamer

Hast du den Leitfaden für die Python-Skriptgestaltung näher angeschaut? Er ist unter folgendem Namen bekannt:
-> PEP 8 -- Style Guide for Python Code

Dieser Code-Abschnitt:

Code: Alles auswählen

def set_main_frame(self):
        #Hauptframe definieren
        self.main_frame = tk.Frame(self.master)
        self.main_frame.pack()
        #Widgets initialisieren
        hallo_label = tk.Label(self.main_frame, text = "Hallo Tkinter!")
        hallo_button = tk.Button(self.main_frame, text = "Weiter", command = self.set_entry_frame)
        quit_button = tk.Button(self.main_frame, text = "Beenden", command = self.quit_app)
        #Widgets positionieren
        hallo_label.grid(row = 0, column = 0, columnspan = 2)
        hallo_button.grid(row = 1, column = 0)
        quit_button.grid(row = 1, column = 1)
        #Bestätigung in der Konsole
        print("Hauptfenster geöffnet")
schreibe ich persönlich so:

Code: Alles auswählen

def set_main_frame(self):

    #Hauptframe definieren
    self.main_frame = tk.Frame(self.master)
    self.main_frame.pack()
    
    #Widgets initialisieren
    hallo_label = tk.Label(self.main_frame, text="Hallo Tkinter!")
    hallo_label.grid(row=0, column=0, columnspan=2)
    
    hallo_button = tk.Button(self.main_frame, text="Weiter",
        command=self.set_entry_frame)
    hallo_button.grid(row=1, column=0)
    
    quit_button = tk.Button(self.main_frame, text="Beenden",
        command=self.quit_app)
    quit_button.grid(row=1, column=1)
    
    #Bestätigung in der Konsole
    print("Hauptfenster geöffnet")
Da möchte ich dir aber auf keinen Fall dreinreden wie ich dies bei einem Arzt und seiner Art Rezepte zu schreiben auch nie tun würde. Hi!

Gruß wuf :wink:
Take it easy Mates!
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Also ich finds um einiges übersichtlicher, die Positionierung in einem Block vorzunehmen, da sind änderungen auch einfacher...
Gruß
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Scriptinggamer hat geschrieben:Also soweit ich wüsste wäre es ok, aber Leonidas meinte, ich sollte keinen Code auf Modulebene schreiben, also main() funktion und if __name__ == "__name__":
main()
Richtig, das sollte man in dieses Beispiel-Skelett einfügen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten