Zwei Fenster unabhängig voneinander schließen

Fragen zu Tkinter.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ok, du hast ja Recht, ich war jetzt einfach zu sehr auf Tk fixiert. Obwohl man hier sicher auch nicht-modale-Dialog schreiben kann was aber, wie ich annehme, nicht sehr einfach wird.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
problembär

Die Sache ist eigentlich ganz einfach. Folgendes Beispiel (an das von wuf angelehnt, aber ein bißchen mehr an meinen Geschmack angepaßt ;) ):

Code: Alles auswählen

#!/usr/bin/env python

import Tkinter as tk

class MyDialog(tk.Toplevel):

    def __init__(self, app_win):
        tk.Toplevel.__init__(self)
        self.app_win = app_win
        self.title("Dialog Window")
        self.geometry("+300+250")
        self.label = tk.Label(self, text="Please enter something:")
        self.label.pack()
        self.entry = tk.Entry(self)
        self.entry.pack()
        self.entry.focus()
       
        self.button_ok = tk.Button(self, text="Ok",
                                   command= self.dialog_end)
        self.button_ok.pack()
        self.app_win.wait_window(self)
       
    def dialog_end(self):
        self.entry_data = self.entry.get()
        self.destroy()

class MyTextfield(tk.Toplevel):
    def __init__(self, app_win, message):
        tk.Toplevel.__init__(self)
        self.app_win = app_win
        self.message = message
        self.tfield = tk.Text(self,
                              fg = 'black',
                              bg = 'white')
        self.tfield.insert(tk.END, message)
        self.button_ok = tk.Button(self, text="Ok",
                                   command= self.textfield_end)
        self.button_ok.pack()
        self.tfield.pack()
        self.app_win.wait_window(self)

    def textfield_end(self):
        self.destroy()

   
class MainWin:
    def __init__(self):
        self.app_win = tk.Tk()
        self.app_win.option_add("*font", ("Arial", 15, "normal"))
        self.app_win.geometry("+250+200")
        self.app_win.title("Example of Custom Dialog-Window")
        self.button_dialog = tk.Button(self.app_win,
                                       text="Click button to open dialog-window.",
                                       command = self.create_dialog_and_show_result)
        self.button_dialog.pack(padx=20, pady=20)
   
        self.button_exit = tk.Button(self.app_win,
                                     text = "Exit",
                                     command = self.app_win.destroy)
        self.button_exit.pack(pady=10)
        self.app_win.mainloop()

    def create_dialog_and_show_result(self):
        self.dialog = MyDialog(self.app_win)
        self.textfield = MyTextfield(self.app_win, message = "You entered:\n\n" + self.dialog.entry_data + "\n")

if __name__ == "__main__":
   app = MainWin()
So, jetzt kommentiert mal in der Klasse "MyDialog" die Zeile

Code: Alles auswählen

self.app_win.wait_window(self)
aus, also ändert sie zu

Code: Alles auswählen

# self.app_win.wait_window(self)
Jetzt tritt ein Fehler auf. Aha!

Gruß
BlackJack

@problembär: Gibt es irgendeinen bestimmten Punkt auf den Du damit hinaus willst? Dass man die Funktion zum implementieren von modalen Dialogen benutzt, wurde ja nun schon mehrfach gesagt. Das ist aber auch keine magische, unerklärliche Instabilität, sondern schlicht und einfach ein offensichtlicher Logikfehler wenn man den Programmfluss so gestaltet, dass man auf ein Attribut zugreift, welches es noch nicht geben kann, wenn man nicht dafür sorgt, dass der Code der es setzt, *vorher* ausgeführt wird.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo problembär (The challenger)

Danke für deine Herausforderung. BlackJack hat es schon angedeutet wo das Problem liegt. Habe mir erlaubt etwas von meinem Programmierstil in dein Skript einfliessen zu lassen. Neu wird der Dialog als modales Fenster gezeigt. Das heisst im Hauptfenster sind wärend der Dialog gezeigt wird die Schaltflächen gesperrt und unter Linux sollte der Dialog über dem Hauptfenster zu stehen kommen. Hier das modifizierte Skript:

Code: Alles auswählen

#!/usr/bin/env python

import Tkinter as tk

class MyDialog(tk.Toplevel):

    def __init__(self, app_win, callback=None):
        
        self.app_win = app_win
        self.callback = callback
        
        tk.Toplevel.__init__(self)
        
        self.title("Dialog Window")
        self.geometry("+300+250")
        
        #~~ BEMERKUNG:
        #   Mache das Toplevel-Fenster Modal 
        #   (Habe dies nur unter Linux getestet)
        self.transient(app_win)
        self.grab_set()

        self.label = tk.Label(self, text="Please enter something:")
        self.label.pack()
        
        self.entry = tk.Entry(self)
        self.entry.pack()
        self.entry.focus()
       
        self.button_ok = tk.Button(self, text="Ok",
            command=self.dialog_end)
        self.button_ok.pack()
              
    def dialog_end(self):
        
        entry_data = self.entry.get()
        self.destroy()
        
        if self.callback != None:
            self.callback(entry_data)

class MyTextfield(tk.Toplevel):
    
    def __init__(self, app_win, message):
        
        tk.Toplevel.__init__(self)
        
        self.app_win = app_win
        self.message = message
        self.tfield = tk.Text(self, fg='black', bg='white')
        self.tfield.pack()
        self.tfield.insert(tk.END, message)
        
        self.button_ok = tk.Button(self, text="Ok", command=self.textfield_end)
        self.button_ok.pack()

    def textfield_end(self):
        self.destroy()

class MainWin:
    
    def __init__(self):
        
        self.app_win = tk.Tk()
        self.app_win.option_add("*font", ("Arial", 15, "normal"))
        self.app_win.geometry("+250+200")
        self.app_win.title("Example of Custom Dialog-Window")
        
        self.button_dialog = tk.Button(self.app_win,
            text="Click button to open dialog-window.",
            command = self.create_dialog)
        self.button_dialog.pack(padx=20, pady=20)
   
        self.button_exit = tk.Button(self.app_win, text = "Exit",
            command = self.app_win.destroy)
        self.button_exit.pack(pady=10)
        
        self.app_win.mainloop()

    def create_dialog(self):
        
        self.dialog = MyDialog(self.app_win, self.dialog_callback)
        
    def dialog_callback(self, dialog_data):
        
        #print 'Dialog-Callback', dialog_data
        
        self.textfield = MyTextfield(self.app_win,
            message="You entered:\n\n" + dialog_data + "\n")

if __name__ == "__main__":
   app = MainWin()
Gruß wuf (The opponent) :wink:
Take it easy Mates!
Antworten