tkinter之旅(一)
- 组件的外观配置:主要调用
config
方法或者初始化的时候添加配置选项。
def main():
root = Tk()
label_font = ('Droid Sans', 20, 'bold')
label = Label(root, text='你好,欢迎来到GUI的世界!')
label.config(bg='blue', fg='white')
label.config(font=label_font)
print(len(label['text']) + 5)
label.config(height=5, width=len(label['text']) * 2)
label.pack(expand=YES, fill=BOTH)
root.mainloop()
main()
一些常用的标签或者类似组件的通用配置介绍:
- 颜色:
bg
顾名思义,就是设置背景色;fg
则用于设置前景色。颜色支持简单的颜色名称如blue
或者十六进制字符串如#ff0000
。 - 大小:设置组件的大小(
height
和width
。 - 字体:字体设置参数由一个三元组提供
(字体名称, 字体大小, 字体类型)
。常用的字体类型如下: normal
:正常romam
:罗马italic
:斜体underline
:下划线overstrike
:加粗- 组合:
bold italic
- 颜色:
其他自定义外观的配置选项:
- 边框和浮凸:
bd=N
可以设置组件的边框宽度;relief=S
可以指定一种边框类型:FLAT
:扁平SUNKEN
:凹陷RAISED
:凸起GROOVE
:凹槽SOLID
:加粗RIDGE
:脊状- 光标:
cursor="gumby"
可以将鼠标指针设置为Gumby图形。其他常用的有watch
(手表)、pencil
(铅笔)、crdss
(十字)和hand2
(手形2)。 - 状态:某些组件支持状态属性设置,从而影响到外观。
state
常用的选项:DISABLED
(禁用)、NORMAL
(正常)、READONLY
等。 - 留白:通过
padx=N
和pady=N
选项来设置,可以为组件周围添加额外的空间。此外,可以在pack
调用中也可以对这两个选项设置。
综合的示例:
# 普通实现
def main():
# 这里的padx, pady设置是给标签周围留白
button = Button(text='FooBar', padx=10, pady=10)
button.config(cursor='gumby')
button.config(bd=8, relief=RAISED)
button.config(bg='dark blue', fg='white')
button.config(font=('Source Code Pro', 20, 'underline italic'))
button.pack()
# 这个是给Button组件留白,相应地会改变默认的窗口大小
button.pack(padx=30, pady=30)
mainloop()
# 使用类继承实现
class ThemeButton(Button):
def __init__(self, master=None, cnf={}, **kw):
# super(ThemeButton, self)调用会返回一个类似`Button`的`self`,然后
# 调用父类的`__init__`函数即可。
# 直接使用`Button.__init__(...)`会出错!
super(ThemeButton, self).__init__(master, cnf, **kw)
self.pack()
self.config(padx=10,
pady=10,
cursor='gumby',
bd=8,
relief=RAISED,
bg='blue',
fg='white',
font=('Source Code Pro', 20, 'underline italic')
)
ThemeButton(text='FooBar').pack(padx=30, pady=30)
- 所有的tkinter的GUI都有一个应用程序的根窗口,无论该窗口是默认创建的还是显示调用
Tk()
创建的。一般会在根窗口中封装最重要和使用时间最长的组件。 Toplevel
组件对象可以帮助创建任意数量的独立窗口,这些窗口可以根据需要生成或弹出。所创建的每个Toplevel
对象都会创建新的窗口,并且自动将新窗口添加到程序的GUI事件循环处理流中(无需调用new_window.mainloop()
激活)。Toplevel
多用于实现多窗口显示、弹出模态和非模态对话框等。
def toplevel_demo():
win1 = Toplevel()
win1.title('win1')
win2 = Toplevel()
win2.title('win2')
Button(text='Quit', command=sys.exit).pack()
Button(win1, text='Win1: Close', command=win1.destroy).pack()
Button(win2, text='Win1: Close', command=win2.destroy).pack()
mainloop()
顶层窗口协议介绍:
- 拦截关闭:
protocol
,可以在点击关闭按钮后拦截关闭事件,比如win.protocol('WM_DELETE_WINDOW', lambda: None)
表示忽略关闭操作。 - 关闭一个窗口(及其子窗口):
destroy
。 - 关闭所有的窗口:
quit
,事实上是终止有效的mainloop
调用。 - 窗口标题设置:
win.title(string)
。 - 窗口图标:
win.iconbitmap(path_to_your_icon)
。 - 几何管理:顶层窗口是其他组件的容器,顶层窗口组件自身永远不会封装(或者网格化、放置)。可以调用
maxsize()
获取最大化窗口大小(屏幕尺寸,[宽度,高度]二元组);geometry(宽* 高+x+y)
设置窗口的初始大小。 - 状态:
iconify
和withdraw
方法可以隐藏并消除一个正在使用的窗口;deiconify
会重绘被隐藏或删除的窗口。state
可以查询或改变状态:iconic
,withdrawn
,normal
。 - 菜单:每个顶层窗口都可以有自己的菜单。
- 拦截关闭:
可以通过组件的
master
属性得到组件的所属者。- 对话框:所有的标准对话框调用都是模态的,并且标准对话框在显示时会阻塞程序的主窗口。
- 简单的对话框示例:
def callback():
if askyesno('Verify', 'Are sure about it?'):
showwarning('Yes', 'Quit now...')
else:
showinfo('No', 'Quit has been canceled')
def main():
errmsg = 'Sorry, no Spam allowed!'
Button(text='Quit', command=callback).pack(fill=X)
Button(text='Spam', command=(lambda: showerror('Spam', errmsg))).pack(fill=X)
mainloop()
- 标准对话框模块主要有:
filedialog
,colorchooser
,messagebox
,simpledialog
,以下给出综合的示例代码:
from tkinter import *
import tkinter.messagebox as messagebox
import tkinter.filedialog as filedialog
import tkinter.colorchooser as colorchooser
import tkinter.simpledialog as simpledialog
FONT_NORMAL = ('Droid Sans', 10, 'normal')
FONT_BOLD = ('Droid Sans', 12, 'bold')
class MButton(Button):
def __init__(self, master=None, cnf={}, **kw):
super(MButton, self).__init__(master, cnf, **kw)
self.config(font=FONT_NORMAL)
self.pack(side=LEFT, expand=YES, fill=Y)
class MLabel(Label):
def __init__(self, master=None, cnf={}, **kw):
super(MLabel, self).__init__(master, cnf, **kw)
self.pack(side=TOP, expand=YES, fill=X)
self.config(font=FONT_BOLD)
class MFrame(Frame):
def __init__(self, master=None, cnf={}, **kw):
super(MFrame, self).__init__(master, cnf, **kw)
self.pack(side=TOP, expand=YES, fill=X, pady=10)
self.label = None
self.make_widgets()
def make_widgets(self):
pass
def print_result(self, text):
if self.label:
self.label.config(text='测试结果:{}'.format(text))
class MessageBoxFrame(MFrame):
def __init__(self, master=None, cnf={}, **kw):
super(MessageBoxFrame, self).__init__(master, cnf, **kw)
def make_widgets(self):
MLabel(self, text='消息对话框演示')
self.label = MLabel(self, text='等待测试...')
MButton(self, text='Ask Yes No', command=self.yesno_demo)
MButton(self, text='Ask Ok Cancel', command=self.okcancel_demo)
MButton(self, text='Question', command=self.question_demo)
MButton(self, text='Retry', command=self.retrycancel_demo)
MButton(self, text='Yes No Cancel', command=self.yesnocancel_demo)
MButton(self, text='Show Error', command=self.showerror_demo)
MButton(self, text='Show Info', command=self.showwarning_demo)
MButton(self, text='Show Warning', command=self.showinfo_demo)
def yesno_demo(self):
self.print_result(messagebox.askyesno('Yes No', 'Yes or No? Give me your answer.'))
def okcancel_demo(self):
self.print_result(messagebox.askokcancel('Ok Cancel', 'Do you want to quit?'))
def question_demo(self):
self.print_result(messagebox.askquestion('Question', 'No question here.'))
def retrycancel_demo(self):
self.print_result(messagebox.askretrycancel('Retry', 'Do you want to retry or not?'))
def yesnocancel_demo(self):
self.print_result(messagebox.askyesnocancel('Yes No Cancel', 'You know what?'))
def showerror_demo(self):
self.print_result(messagebox.showerror('Error', 'Nothing happened here.'))
def showwarning_demo(self):
self.print_result(messagebox.showwarning('Warn', 'Nothing happened here.'))
def showinfo_demo(self):
self.print_result(messagebox.showinfo('Info', 'Nothing happened here.'))
class ColorChooserFrame(MFrame):
def __init__(self, master=None, cnf={}, **kw):
super(ColorChooserFrame, self).__init__(master, cnf, **kw)
def make_widgets(self):
MLabel(self, text='颜色选择演示')
self.label = MLabel(self, text='等待测试...')
MButton(self, text='选择颜色', command=self.askcolor_demo)
def askcolor_demo(self):
r = colorchooser.askcolor('blue')
self.print_result(r)
self.label.config(fg=r[-1])
class SimpleDialogFrame(MFrame):
def __init__(self, master=None, cnf={}, **kw):
super(SimpleDialogFrame, self).__init__(master, cnf, **kw)
def make_widgets(self):
MLabel(self, text='简单对话框演示')
self.label = MLabel(self, text='等待测试...')
MButton(self, text="输入字符串",
command=lambda: self.print_result(simpledialog.askstring('Input', 'Your name: ')))
MButton(self, text="输入整数",
command=lambda: self.print_result(simpledialog.askstring('Input', 'Your age: ')))
MButton(self, text="输入浮点数",
command=lambda: self.print_result(simpledialog.askstring('Input', 'Your height: ')))
class FileDialogFrame(MFrame):
def __init__(self, master=None, cnf={}, **kw):
super(FileDialogFrame, self).__init__(master, cnf, **kw)
def make_widgets(self):
MLabel(self, text='文件对话框演示')
self.label = MLabel(self, text='等待测试...')
MButton(self, text='打开目录', command=lambda: self.print_result(filedialog.askdirectory()))
MButton(self, text='打开文件', command=lambda: self.print_result(filedialog.askopenfile()))
MButton(self, text='选择文件名', command=lambda: self.print_result(filedialog.askopenfilename()))
MButton(self, text='选择多个文件名', command=lambda: self.print_result(filedialog.askopenfilenames()))
MButton(self, text='打开多个文件', command=lambda: self.print_result(filedialog.askopenfiles()))
MButton(self, text='保存为...', command=lambda: self.print_result(filedialog.asksaveasfile()))
MButton(self, text='保存为文件名...', command=lambda: self.print_result(filedialog.asksaveasfilename()))
def main():
MessageBoxFrame()
ColorChooserFrame()
SimpleDialogFrame()
FileDialogFrame()
mainloop()
if __name__ == '__main__':
main()
- 自定义对话框支持任意接口,会创建一个带有附加组件的Toplevel的弹出式窗口,并安排一个回调处理程序来获取用户的对话框输入并负责销毁窗口。如果是模态窗口,则需要通过给出窗口输入焦点来等待答复,并且同时使其他窗口无效且等待事件。
- 模态对话框中锁定其他窗口并等待答复的普遍构建模式:
win.focus_set()
win.grab_set()
:设置后用户不能和其他窗口交互win.wait_window()
- 一个简单的模态对话框示例:
def dialog():
win = Toplevel()
Label(win, text='You see see.').pack()
Button(win, text='Ok', command=win.destroy).pack()
# 这里很关键,否则就不是模态对话框了
win.focus_set()
win.grab_set()
win.wait_window()
print('Dialog exit.')
def main():
Button(text='Open dialog', command=dialog).pack()
mainloop()