Dialog Windows
可用wait_window来完成对话框。wait_window由自己的local event loop, 直到窗口销毁后才返回。
widget.wait_window(window)
#File: dialog1.py
from Tkinter import *
class MyDialog:
def __init__(self, parent):
top = self.top = Toplevel(parent)
Label(top, text = "Value").pack()
self.e = Entry(top)
self.e.pack(padx = 5)
b = Button(top, text = "OK", command = self.ok)
b.pack(pady = 5)
def ok(self):
print "value is", self.e.get()
self.top.destroy()
root = Tk()
Button(root, text = "Hello!").pack()
root.update()
d = MyDialog(root)
root.wait_window(d.top)
分析如下:
函数并没有调用root.mainloop()这个event loop,而是使用wait_windows()控制的local event loop。存在的问题:
- root window仍然存在,并且显示dialog后,root window仍是active的。
- 需要明确的点击entry field,才能拥有输入焦点,只有在按OK后,输入才有效,按Enter并没有效。
- 可以运用WM_DELETE_WINDOW protocol来完成dialog window的撤销。
第一个问题可以用grab_set()来完成;第二个问题可以用focus_set()来设定text entry的输入焦点,并且需要绑定Enter键。
改进后的dialog:
from Tkinter import * import os class Dialog(Toplevel): def __init__(self, parent, title = None): Toplevel.__init__(self, parent) self.transient(parent) if title: self.title(title) self.parent = parent self.result = None body = Frame(self) self.initial_focus = self.body(body) body.pack(padx = 5, pady = 5) self.buttonbox() self.grab_set() if not self.initial_focus: self.initial_focus = self self.protocol("WM_DELETE_WINDOW", self.cancel) self.geometry("+%d+%d" % (parent.winfo_rootx() + 50, parent.weinfo_rooty() + 50)) self.initial_focus.focus_set() self.wait_window(self) def body(self, master): pass def buttonbox(self): box = Frame(self) w = Button(box, text = "OK", width = 10, command = self.ok, default = ACTIVE) w.pack(side = LEFT, padx = 5, pady = 5) w = Button(box, text = "Cancel", width = 10, command = self.cancel) w.pack(side = LEFT, padx = 5, pady = 5) self.bind("<Return>", self.ok) self.bind("<Escape>", self.cancel) box.pack() def ok(self, event = None): if not self.validate(): self.initial_focus.focus_set() return self.withdraw() self.update_idletasks() self.apply() self.cancel() def cancel(self, event = None): self.parent.focus_set() self.destroy() def validate(self): return 1 def apply(self): pass 其中,transient()用于将当前窗口和父窗口关联起来,grab_set()用于激发对话框模式,geometry()用于设定dialog相对于其父窗口的位置,focus_set()设置输入焦点,最后进入wait_window这个event loop.这样一来,便可以用上述类生成新的dialog类了
from Tkinter import * import tkSimpleDialog import string class MyDialog(tkSimpleDialog.Dialog): def body(self, master): Label(master, text = "First:").grid(row = 0) Label(master, text = "Second:").grid(row = 1) self.e1 = Entry(master) self.e2 = Entry(master) self.e1.grid(row = 0, column = 1) self.e2.grid(row = 1, column = 1) return self.e1 def apply(self): first = string.atoi(self.e1.get()) second = string.atoi(self.e2.get()) print first, second
可以用try/except来完成的对输入数据的验证。
def validate(self): try: first= int(self.e1.get()) second = int(self.e2.get()) self.result = first, second return 1 except ValueError: tkMessageBox.showwarning( "Bad input", "Illegal values, please try again" ) return 0 def apply(self): dosomething(self.result)