Python GUI Cookbook —— 创建 GUI 窗体并添加 Widgets

来源:互联网 发布:长虹电视网络设置在哪 编辑:程序博客网 时间:2024/05/21 00:18

原文链接:Python GUI Cookbook —— 创建 GUI 窗体并添加 Widgets

创建第一个 Python GUI 程序

使用 Python 内置的 tkinter 模块,仅需 4 行代码就能创建一个 GUI。

下面是代码:

#!/usr/bin/env python3import tkinter as tk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Start GUIwindow.mainloop()

Snipaste_2017-12-02_09-41-46.png

防止窗口大小被调整

使用 tkinter 创建的 GUI 默认是能够调整大小的,但有时候我们希望窗体保持特定的大小,因此需要学习如何防止用户调整 GUI 大小。

#!/usr/bin/env python3import tkinter as tk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Disable resizing the GUI by passing in False/Falsewindow.resizable(False, False)# Enable resizing x-dimension, disable y-dimension# window.resizable(True, False)# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-16-10-04.png

在窗体中添加 label

label 是一种简单的 widget,它能够向窗体中添加值(value),能够解释其他 widgets 的目的,提供额外的信息。

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Add a Labelttk.Label(window, text = 'A Label').grid(column=0, row=0)# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-16-17-51.png

这里我们从 tkinter 包导入了一个独立模块 ttk。ttk 模块有一些高级 widgets 能够让我们的 GUI 看起来更好看。ttk 代表 themed tk。

  • 在这个实例中我们使用了 ttk.Label 构造器设置文本属性(text property)
  • 使用了网格布局管理器(grid layout manager)
  • 可以发现窗体突然变小了好多,这是因为我们在窗体中添加了 widget,触发了优化,尽量使用小空间显示 widget(s)。

创建按钮并改变其文本属性

这里我们将添加一个 button widget,并使用该按钮(button)改变其他 widget 的属性。我们将学习 Python GUI 的回调函数(callback function)和事件处理机制。

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Button click Event Functiondef click_me():    action.configure(text='** I have been Clicked! **')    a_label.configure(foreground='red')    a_label.configure(text='A Red Label')# Adding a Buttonaction = ttk.Button(window, text='Click Me!', command=click_me)action.grid(column=1, row=0)# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-16-54-41.png

Screenshot-from-2017-12-02-16-56-57.png

GUIs 是事件驱动的。点击按钮产生一个事件。我们将事件产生时会发生的事与回调函数绑定。通过 ttk.Button widget 的 command 属性调用它,注意调用时不需要圆括号,只需使用名字 click_me

文本框 Text Box widgets

在 tkinter 中,只有一行的 textbox widget 被称为 Entry

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Modified Button click Event Functiondef click_me():    action.configure(text='Hello ' + name.get())# Changing our Labelttk.Label(window, text='Enter a name: ').grid(column=0, row=0)# Adding a Text Box Entry widgetname = tk.StringVar()name_entered = ttk.Entry(window, width=12, textvariable=name)name_entered.grid(column=0, row=1)# Adding a Buttonaction = ttk.Button(window, text="Click Me!", command=click_me)   action.grid(column=1, row=1)# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-17-16-33.png

Screenshot-from-2017-12-02-17-16-56.png

在 tkinter 中,我们需要声明 name 变量为 tk.StringVar()。因为 tkinter 不是 Python。我们只是能够在 Python 中使用它,但它们并不是同种语言。

给 widget 设置焦点,禁用 widgets

只需调用 focus() 方法就能给一个 widget 设置焦点。

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Modified Button click Event Functiondef click_me():    action.configure(text='Hello ' + name.get())# Changing our Labelttk.Label(window, text='Enter a name:').grid(column=0, row=0)# Adding a Text Box Entry widgetname = tk.StringVar()name_entered = ttk.Entry(window, width=12, textvariable=name)name_entered.grid(column=0, row=1)# Adding a Buttonaction = ttk.Button(window, text="Click Me!", command=click_me)   action.grid(column=1, row=1)action.configure(state='disabled') # Disable the Button Widgetname_entered.focus_set() # Place cursor into name Entry# Start GUIwindow.mainloop()

Snipaste_2017-12-02_20-22-09.png

Snipaste_2017-12-02_20-24-24.png

组合框 Combo box widgets

下拉式组合框

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Modified Button click Event Functiondef click_me():    action.configure(text='Hello ' + name.get()+ ' ' + number_chosen.get())# Changing our Labelttk.Label(window, text='Enter a name:').grid(column=0, row=0)# Adding a Textbox Entry widgetname = tk.StringVar()name_entered = ttk.Entry(window, width=12, textvariable=name)name_entered.grid(column=0, row=1) # column 0# Adding a Buttonaction = ttk.Button(window, text="Click Me!", command=click_me)   action.grid(column=2, row=1) # change column to 2ttk.Label(window, text='Choose a number:').grid(column=1, row=0)number = tk.StringVar()number_chosen = ttk.Combobox(window, width=12, textvariable=number)number_chosen['values'] = (1, 2, 4, 42, 100)number_chosen.grid(column=1, row=1) # Combobox in column 1number_chosen.current(0)name_entered.focus() # Place cursor into name Entry# Start GUIwindow.mainloop()

如果要限制用户,让他们只能选择程序中给出的选项,则要向构造器中传 Combobox 的 state 属性

[...]number = tk.StringVar()number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')number_chosen['values'] = (1, 2, 4, 42, 100)number_chosen.grid(column=1, row=1) # Combobox in column 1number_chosen.current(0)[...]

Snipaste_2017-12-02_20-36-49.png

创建具有不同初始状态的复选按钮

Checkbutton widgets

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Modified Button click Event Functiondef click_me():    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())# Changing our Labelttk.Label(window, text='Enter a name:').grid(column=0, row=0)# Adding a Textbox Entry widgetname = tk.StringVar()name_entered = ttk.Entry(window, width=12, textvariable=name)name_entered.grid(column=0, row=1) # column 0# Adding a Buttonaction = ttk.Button(window, text="Click Me!", command=click_me)   action.grid(column=2, row=1) # change column to 2# Creating three checkbuttonsttk.Label(window, text='Choose a number:').grid(column=1, row=0)number = tk.StringVar()number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')number_chosen['values'] = (1, 2, 4, 42, 100)number_chosen.grid(column=1, row=1) # Combobox in column 1number_chosen.current(0)# Creating three checkbuttonschVarDis = tk.IntVar()check1 = tk.Checkbutton(window, text='Disabled', variable=chVarDis, state='disabled')check1.select()check1.grid(column=0, row=4, sticky=tk.W)chVarUn = tk.IntVar()check2 = tk.Checkbutton(window, text='UnChecked', variable=chVarUn)check2.deselect()check2.grid(column=1, row=4, sticky=tk.W)chVarEn = tk.IntVar()check3 = tk.Checkbutton(window, text='Enabled', variable=chVarEn)check3.select()check3.grid(column=2, row=4, sticky=tk.W)name_entered.focus() # Place cursor into name Entry# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-21-27-54.png

将网格的 sticky 属性设置为 tk.W 意味着该 widget 向网格的西(west)面对齐。

使用单选按钮 radio button widgets

Radiobutton widgets

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttk# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Modified Button click Event Functiondef click_me():    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())# Changing our Labelttk.Label(window, text='Enter a name:').grid(column=0, row=0)# Adding a Textbox Entry widgetname = tk.StringVar()name_entered = ttk.Entry(window, width=12, textvariable=name)name_entered.grid(column=0, row=1) # column 0# Adding a Buttonaction = ttk.Button(window, text="Click Me!", command=click_me)   action.grid(column=2, row=1) # change column to 2ttk.Label(window, text='Choose a number:').grid(column=1, row=0)number = tk.StringVar()number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')number_chosen['values'] = (1, 2, 4, 42, 100)number_chosen.grid(column=1, row=1) # Combobox in column 1number_chosen.current(0)# Creating three checkbuttonschVarDis = tk.IntVar()check1 = tk.Checkbutton(window, text='Disabled', variable=chVarDis, state='disabled')check1.select()check1.grid(column=0, row=4, sticky=tk.W)chVarUn = tk.IntVar()check2 = tk.Checkbutton(window, text='UnChecked', variable=chVarUn)check2.deselect()check2.grid(column=1, row=4, sticky=tk.W)chVarEn = tk.IntVar()check3 = tk.Checkbutton(window, text='Enabled', variable=chVarEn)check3.deselect()check3.grid(column=2, row=4, sticky=tk.W)# GUI callback functiondef checkCallback(*ignoredArgs):    if chVarUn.get():        check3.configure(state='disabled')    else:        check3.configure(state='normal')    if chVarEn.get():        check2.configure(state='disabled')    else:        check2.configure(state='normal')# trace the state of the two checkbuttonchVarUn.trace('w', lambda unused0, unused1, unused2 : checkCallback())chVarEn.trace('w', lambda unused0, unused1, unused2 : checkCallback())# Radiobutton GlobalsCOLOR1 = 'Blue'COLOR2 = 'Gold'COLOR3 = 'Red'# Radiobutton Callbackdef radCall():    radSel = radVar.get()    if radSel == 1:        window.configure(background=COLOR1)    elif radSel == 2:        window.configure(background=COLOR2)    elif radSel == 3:        window.configure(background=COLOR3)# Create three Radiobuttons using one variableradVar = tk.IntVar()rad1 = tk.Radiobutton(window, text=COLOR1, variable=radVar,     value=1, command=radCall)rad1.grid(column=0, row=5, sticky=tk.W, columnspan=3)rad2 = tk.Radiobutton(window, text=COLOR2, variable=radVar,     value=2, command=radCall)rad2.grid(column=1, row=5, sticky=tk.W, columnspan=3)rad3 = tk.Radiobutton(window, text=COLOR3, variable=radVar,     value=3, command=radCall)rad3.grid(column=2, row=5, sticky=tk.W, columnspan=3)name_entered.focus() # Place cursor into name Entry# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-21-48-35.png

使用滚动文本 scrolled text widgets

ScrolledText 要比简单的 Entry 大得多并且跨越多行。当文本大于 ScrolledText widget 的高度时,将自动启用垂直滚动条。

import tkinter as tkfrom tkinter import ttkfrom tkinter import scrolledtext[...]# Using a scrlled text controlscrol_w = 40scrol_h = 3scr = scrolledtext.ScrolledText(window, width=scrol_w, height=scrol_h, wrap=tk.WORD)scr.grid(column=0, columnspan=3)name_entered.focus() # Place cursor into name Entry# Start GUIwindow.mainloop()

Screenshot-from-2017-12-02-22-27-00.png

Screenshot-from-2017-12-02-22-27-29.png

  • 通过将 wrap 属性设置为 tk.WORD 来告诉 ScrolledText widget 通过单词来断行。 默认的是 tk.CHAR,以字符来断行。
  • 对于 ScrolledText widget ,将网格的 columnspan 属性设置为 3,能够让该 widget 横跨 3 列。默认情况下只有 1 列。

重构

可以发现在上面的代码中有很多冗余,这里我们将重构它们。

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttkfrom tkinter import scrolledtext# Create instancewindow = tk.Tk()# Add a titlewindow.title("My First Python GUI")# Adding a Label that will get modifieda_label = ttk.Label(window, text = 'A Label')a_label.grid(column=0, row=0)# Modified Button click Event Functiondef click_me():    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())# Changing our Labelttk.Label(window, text='Enter a name:').grid(column=0, row=0)# Adding a Textbox Entry widgetname = tk.StringVar()name_entered = ttk.Entry(window, width=12, textvariable=name)name_entered.grid(column=0, row=1) # column 0# Adding a Buttonaction = ttk.Button(window, text="Click Me!", command=click_me)   action.grid(column=2, row=1) # change column to 2ttk.Label(window, text='Choose a number:').grid(column=1, row=0)number = tk.StringVar()number_chosen = ttk.Combobox(window, width=12, textvariable=number, state='readonly')number_chosen['values'] = (1, 2, 4, 42, 100)number_chosen.grid(column=1, row=1) # Combobox in column 1number_chosen.current(0)# Creating three checkbuttonschVarDis = tk.IntVar()check1 = tk.Checkbutton(window, text='Disabled', variable=chVarDis, state='disabled')check1.select()check1.grid(column=0, row=4, sticky=tk.W)chVarUn = tk.IntVar()check2 = tk.Checkbutton(window, text='UnChecked', variable=chVarUn)check2.deselect()check2.grid(column=1, row=4, sticky=tk.W)chVarEn = tk.IntVar()check3 = tk.Checkbutton(window, text='Enabled', variable=chVarEn)check3.deselect()check3.grid(column=2, row=4, sticky=tk.W)# GUI callback functiondef checkCallback(*ignoredArgs):    if chVarUn.get():        check3.configure(state='disabled')    else:        check3.configure(state='normal')    if chVarEn.get():        check2.configure(state='disabled')    else:        check2.configure(state='normal')# trace the state of the two checkbuttonchVarUn.trace('w', lambda unused0, unused1, unused2 : checkCallback())chVarEn.trace('w', lambda unused0, unused1, unused2 : checkCallback())# First, we change our Radiobutton globals variables into a listcolors = ['Blue', 'Gold', 'Red']# We have also changed the callback function to be zero-based, using list# instead of module-level global variables# Radiobutton Callbackdef radCall():    radSel = radVar.get()    if radSel == 1:        window.configure(background=colors[0]) # now zero-based and using list    elif radSel == 2:        window.configure(background=colors[1])    elif radSel == 3:        window.configure(background=colors[3])# Create three Radiobuttons using one variableradVar = tk.IntVar()# Next we are selecting a non-existing index value for radVarradVar.set(99)# Now we are creating all three Radiobutton widgets within one loopfor col in range(3):    curRad = tk.Radiobutton(window, text=colors[col], variable=radVar,         value=col, command=radCall)    curRad.grid(column=col, row=5, sticky=tk.W)# Using a scrlled text controlscrol_w = 40scrol_h = 3scr = scrolledtext.ScrolledText(window, width=scrol_w, height=scrol_h, wrap=tk.WORD)scr.grid(column=0, columnspan=3)name_entered.focus() # Place cursor into name Entry# Start GUIwindow.mainloop()

运行程序会发现和上面的一样,但代码更简洁清晰。

参考文献

  • Python GUI Programming Cookbook - Second Edition by Burkhard A. Meier
原创粉丝点击