Python GUI Cookbook —— 定制 widgets

来源:互联网 发布:汉王小龙女手写板软件 编辑:程序博客网 时间:2024/05/22 03:38

原文链接:Python GUI Cookbook —— 定制 widgets

创建消息框——信息、警告和错误

消息框是一种用于给用户反馈的弹出式窗口,它可能是提示性的,也可能是显示潜在问题的,还有可能是指出灾难性错误的。

首先导入模块

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttkfrom tkinter import scrolledtextfrom tkinter import Menufrom tkinter import messagebox as msg[...]

创建消息框的回调函数,此时我们点击 Help|About

[...]# Display a message boxdef _msgBox():    msg.showinfo('Python Message Info Box',         'A Python GUI created using tkinter:\n The year is 2017.')# Add another Menu to the Menu Bar and an itemhelp_menu = Menu(menu_bar, tearoff=0)help_menu.add_command(label='About', command=_msgBox)menu_bar.add_cascade(label='Help', menu=help_menu)name_entered.focus()window.mainloop()

Screenshot-from-2017-12-06-20-39-39.png

将消息换成警告

[...]# Display a message boxdef _msgBox():    # msg.showinfo('Python Message Info Box',     #   'A Python GUI created using tkinter:\n The year is 2017.')    msg.showwarning('Python Message Warning Box',         'A Python GUI Cretaed using tkinter:'        '\nWarning: There might be a bug in this code.')[...]

Screenshot-from-2017-12-06-20-44-10.png

显示一个错误信息

[...]# msg.showinfo('Python Message Info Box',     #   'A Python GUI created using tkinter:\n The year is 2017.')    # msg.showwarning('Python Message Warning Box',     #   'A Python GUI Cretaed using tkinter:'    #   '\nWarning: There might be a bug in this code.')    msg.showerror('Python Message Error Box',         'A Python GUI created using tkinter:'        '\nError: Houston ~ We DO have a serious PROBLEM!')[...]

Screenshot-from-2017-12-06-20-48-02.png

创建多选框

[...]# Display a message boxdef _msgBox():    # msg.showinfo('Python Message Info Box',     #   'A Python GUI created using tkinter:\n The year is 2017.')    # msg.showwarning('Python Message Warning Box',     #   'A Python GUI Cretaed using tkinter:'    #   '\nWarning: There might be a bug in this code.')    # msg.showerror('Python Message Error Box',     #   'A Python GUI created using tkinter:'    #   '\nError: Houston ~ We DO have a serious PROBLEM!')    answer = msg.askyesnocancel('Python Message Mutil Choice Box',         'Are you sure you really wish to do this?')    print(answer)[...]

Screenshot-from-2017-12-06-20-53-21.png

Screenshot-from-2017-12-06-21-21-40.png

然后我们就可以用

If answer == True:    <do something>

来实现一些功能了

创建独立的消息框

这里我们将做一个顶层窗口的消息框

先创建一个简单的窗口看看效果:

from tkinter import messagebox as msgmsg.showinfo('', 'Python GUI created using tkinter:\nThe year is 2017')

这会产生下面这两个窗口

Snipaste_2017-12-09_20-58-10.png

可以看到这并不是我们想要的

通过下面代码去掉额外的窗口

from tkinter import messagebox as msgfrom tkinter import Tkroot = Tk()root.withdraw()msg.showinfo('', 'Python GUI created using tkinter:\n The year is 2017')

Snipaste_2017-12-09_20-53-08.png

创建 tkinter 窗体的标题

import tkinter as tkwindow = tk.Tk()window.title('Python GUI')

更换根窗口的图标

[...]window.iconbitmap('卷纸.ico')name_entered.focus() # Place cursor into name Entry# Start GUIwindow.mainloop()

Snipaste_2017-12-09_21-56-40.png

使用 spin box

[...]from tkinter import Spinbox[...]# Adding a Spinbox widgetspin = Spinbox(mighty, from_=0, to=10)spin.grid(column=0, row=2)# Using a scrolled text controlscrol_w = 30scrol_h = 3scr = scrolledtext.ScrolledText(mighty, width=scrol_w, height=scrol_h, wrap=tk.WORD)scr.grid(column=0, row=3, sticky='WE', columnspan=3)[...]

Screenshot-from-2017-12-10-10-05-24.png

接下来对该 widget 定制一番:减小宽度,增加边框(borderwidth,bd)

spin = Spinbox(mighty, from_=0, to=10, width=5, bd=8)

Screenshot-from-2017-12-10-10-11-03.png

给 widget 增加实际功能

[...]# Spinbox callbackdef _spin():    value = spin.get()    print(value)    scr.insert(tk.INSERT, value+'\n')# Adding a Spinbox widgetspin = Spinbox(mighty, from_=0, to=10, width=5, bd=8, command=_spin)[...]

Screenshot-from-2017-12-10-10-16-09.png

还可以使用

spin = Spinbox(mighty, values=(1, 2, 4, 42, 100), width=5, bd=8, command=_spin)

widget 风格

给 spinbox 增加 bd

[...]# Adding a Spinbox widgetspin = Spinbox(mighty, from_=0, to=10, width=5, bd=8, command=_spin)spin.grid(column=0, row=2)Spinbox(mighty, values=(1, 2, 4, 8, 16, 32, 64),     width=5, bd=20).grid(column=1, row=2)[...]

Screenshot-from-2017-12-10-10-27-54.png

  • 两个 spinbox 都是浮雕(relief)风,第二个具有更大的边框
  • 默认的 relief 属性是 tk.SUNKEN

relief 属性的可选值

|tk.SUNKEN|tk.RAISED|tk.FLAT|tk.GROOVE|tk.EIDGE|

[...]# Adding a Spinbox widgetspin = Spinbox(mighty, from_=0, to=10, width=5, bd=8, command=_spin,     relief=tk.GROOVE)spin.grid(column=0, row=2)Spinbox(mighty, values=(1, 2, 4, 8, 16, 32, 64),     width=5, bd=8, relief='flat').grid(column=1, row=2)[...]

Screenshot-from-2017-12-10-10-38-07.png

使用提示(tooltips)

添加一个 tooltip 应该是一件简单的事,但这里它并不像我们想象的那么简单

首先需要添加一些面向对象(OOP)代码

import tkinter as tkfrom tkinter import ttkfrom tkinter import scrolledtextfrom tkinter import Menufrom tkinter import messagebox as msgfrom tkinter import Spinboxclass ToolTip(object):    def __init__(self, widget):        self.widget = widget        self.tip_window = None    def show_tip(self, tip_text):        if self.tip_window or not tip_text:            return        # get size of widget        x, y, _cx, cy = self.widget.bbox('insert')        # calculate to display tooltip        x = x + self.widget.winfo_rootx() + 25        y = y + cy + self.widget.winfo_rooty() + 25        # create new tooltip window        self.tip_window = tw = tk.Toplevel(self.widget)        # remove all Window Manager (wm) decorations        tw.wm_overrideredirect(True)        tw.wm_geometry('+%d+%d' % (x, y))        label = tk.Label(tw, text=tip_text, justify=tk.LEFT,            background='#ffffe0', relief=tk.SOLID, bd=1)        label.pack(ipadx=1)    def hide_tip(self):        tw = self.tip_window        self.tip_window = None        if tw:            tw.destroy()def create_ToolTip(widget, text):    tooltip = ToolTip(widget)    def enter(event):        tooltip.show_tip(text)    def leave(event):        tooltip.hide_tip()    widget.bind('<Enter>', enter)    widget.bind('<Leave>', leave)[...]

给一些 widget 添加 tooltips

[...]# Adding a Spinbox widgetspin = Spinbox(mighty, from_=0, to=10, width=5, bd=8, command=_spin,     relief=tk.GROOVE)spin.grid(column=0, row=2)create_ToolTip(spin, 'This is a Spinbox widget')Spinbox(mighty, values=(1, 2, 4, 8, 16, 32, 64),     width=5, bd=8, relief='flat').grid(column=1, row=2)# Using a scrolled text controlscrol_w = 30scrol_h = 3scr = scrolledtext.ScrolledText(mighty, width=scrol_w, height=scrol_h, wrap=tk.WORD)scr.grid(column=0, row=3, sticky='WE', columnspan=3)# Add a Tooltip to the ScrolledText widgetcreate_ToolTip(scr, 'This is a ScrolledText widget')[...]

Screenshot-from-2017-12-10-11-20-17.png

添加进度条

[...]from time import sleep[...]# update progressbar in callback loopdef run_progressbar():    progress_bar['maximum'] = 100    for i in range(101):        sleep(0.05)        # increment progressbar        progress_bar['value'] = i        # have to call update() in loop        progress_bar.update()    # reset/clear progressbar    progress_bar['value'] = 0def start_progressbar():    progress_bar.start()def stop_progressbar():    progress_bar.stop()def stop_after_second():    window.after(500, progress_bar.stop)# Create a container to hold labelsbuttons_frame = ttk.LabelFrame(mighty2, text=' ProgressBar ')buttons_frame.grid(column=0, row=3)# Place labels into the container elementttk.Button(buttons_frame, text=' Run Progressbar ',     command=run_progressbar).grid(column=0, row=0, sticky=tk.W)ttk.Button(buttons_frame, text='Start Progressbar',     command=start_progressbar).grid(column=0, row=1, sticky=tk.W)ttk.Button(buttons_frame, text='Stop immediately',     command=stop_progressbar).grid(column=0, row=2, sticky=tk.W)ttk.Button(buttons_frame, text='Stop after second',     command=stop_after_second).grid(column=0, row=3, sticky=tk.W)# Add a Progressbar to Tab 2progress_bar = ttk.Progressbar(tab2, orient='horizontal',     length=286, mode='determinate')progress_bar.grid(column=0, row=4, pady=2)[...]

Screenshot-from-2017-12-10-15-53-39.png

使用 canvas widget

[...]tabControl = ttk.Notebook(window)# Create a tabtab1 = ttk.Frame(tabControl)# Add a tabtabControl.add(tab1, text='Tab 1')tab2 = ttk.Frame(tabControl)tabControl.add(tab2, text='Tab 2')tab3 = ttk.Frame(tabControl)tabControl.add(tab3, text='Tab 3')[...]# Tab 3 controltab3_frame = tk.Frame(tab3, bg='blue')tab3_frame.pack()for orange_color in range(2):    canvas = tk.Canvas(tab3_frame, width=150, height=80,        highlightthickness=0, bg='orange')    canvas.grid(row=orange_color, column=orange_color)[...]

Screenshot-from-2017-12-10-16-05-00.png

Full Version Code

#!/usr/bin/env python3import tkinter as tkfrom tkinter import ttkfrom tkinter import scrolledtextfrom tkinter import Menufrom tkinter import messagebox as msgfrom tkinter import Spinboxfrom time import sleepclass ToolTip(object):    def __init__(self, widget):        self.widget = widget        self.tip_window = None    def show_tip(self, tip_text):        if self.tip_window or not tip_text:            return        # get size of widget        x, y, _cx, cy = self.widget.bbox('insert')        # calculate to display tooltip        x = x + self.widget.winfo_rootx() + 25        y = y + cy + self.widget.winfo_rooty() + 25        # create new tooltip window        self.tip_window = tw = tk.Toplevel(self.widget)        # remove all Window Manager (wm) decorations        tw.wm_overrideredirect(True)        tw.wm_geometry('+%d+%d' % (x, y))        label = tk.Label(tw, text=tip_text, justify=tk.LEFT,            background='#ffffe0', relief=tk.SOLID, bd=1)        label.pack(ipadx=1)    def hide_tip(self):        tw = self.tip_window        self.tip_window = None        if tw:            tw.destroy()def create_ToolTip(widget, text):    tooltip = ToolTip(widget)    def enter(event):        tooltip.show_tip(text)    def leave(event):        tooltip.hide_tip()    widget.bind('<Enter>', enter)    widget.bind('<Leave>', leave)# Create instancewindow = tk.Tk()# Add a titlewindow.title('Python GUI')tabControl = ttk.Notebook(window)# Create a tabtab1 = ttk.Frame(tabControl)# Add a tabtabControl.add(tab1, text='Tab 1')tab2 = ttk.Frame(tabControl)tabControl.add(tab2, text='Tab 2')tab3 = ttk.Frame(tabControl)tabControl.add(tab3, text='Tab 3')# Pack to make visibletabControl.pack(expand=1, fill='both')# Tab 1# LabelFrame using tab 1 as the parentmighty = ttk.LabelFrame(tab1, text=' Mighty Python ')mighty.grid(column=0, row=0, padx=8, pady=4)# Modify adding a Label using mighty as the parent instead of windowttk.Label(mighty, text='Enter a name:').grid(column=0, row=0, sticky='W')# Modify Button Click Functiondef click_me():    action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())# Adding a Textbox Entry widgetname = tk.StringVar()name_entered = ttk.Entry(mighty, width=12, textvariable=name)name_entered.grid(column=0, sticky='W') # align left/West# Adding a Buttonaction = ttk.Button(mighty, text='Click Me!', command=click_me)action.grid(column=2, row=1)ttk.Label(mighty, text='Choose a number:').grid(column=1, row=0)number = tk.StringVar()number_chosen = ttk.Combobox(mighty, width=12, textvariable=number, state='readonly')number_chosen['values'] = (1, 2, 4, 8, 16, 32, 64, 128, 256, 562, 1024)number_chosen.grid(column=1, row=1)number_chosen.current(10)# Spinbox callbackdef _spin():    value = spin.get()    print(value)    scr.insert(tk.INSERT, value+'\n')# Adding a Spinbox widgetspin = Spinbox(mighty, from_=0, to=10, width=5, bd=8, command=_spin,     relief=tk.GROOVE)spin.grid(column=0, row=2)create_ToolTip(spin, 'This is a Spinbox widget')Spinbox(mighty, values=(1, 2, 4, 8, 16, 32, 64),     width=5, bd=8, relief='flat').grid(column=1, row=2)# Using a scrolled text controlscrol_w = 30scrol_h = 3scr = scrolledtext.ScrolledText(mighty, width=scrol_w, height=scrol_h, wrap=tk.WORD)scr.grid(column=0, row=3, sticky='WE', columnspan=3)# Add a Tooltip to the ScrolledText widgetcreate_ToolTip(scr, 'This is a ScrolledText widget')# Tab 2# We are creating a container frame to hold all other widgetsmighty2 = ttk.LabelFrame(tab2, text=' The Snake ')mighty2.grid(column=0, row=0, padx=8, pady=4)# Creating three checkbuttonschVarDis = tk.IntVar()check1 = tk.Checkbutton(mighty2, text='Disabled', variable=chVarDis, state='disabled')check1.select()check1.grid(column=0, row=1, sticky=tk.W)chVarUn = tk.IntVar()check2 = tk.Checkbutton(mighty2, text='UnChecked', variable=chVarUn)check2.deselect()check2.grid(column=1, row=1, sticky=tk.W)chVarEn = tk.IntVar()check3 = tk.Checkbutton(mighty2, text='Enabled', variable=chVarEn)check3.deselect()check3.grid(column=2, row=1, 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 global variables into a listcolors = ['Blue', 'Gold', 'Red']# We have also changed the callback function to be zero-based, using the list# instead of module-level global variables# Radiobutton callbackdef radCall():    radSel = radVar.get()    if radSel == 0:        window.configure(background=colors[0])    elif radSel == 1:        window.configure(background=colors[1])    elif radSel == 2:        window.configure(background=colors[2])# create three Radiobuttons using one variableradVar = tk.IntVar()# Now 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(mighty2, text=colors[col], variable=radVar,         value=col, command=radVar)    currad.grid(column=col, row=2, sticky=tk.W)# update progressbar in callback loopdef run_progressbar():    progress_bar['maximum'] = 100    for i in range(101):        sleep(0.05)        # increment progressbar        progress_bar['value'] = i        # have to call update() in loop        progress_bar.update()    # reset/clear progressbar    progress_bar['value'] = 0def start_progressbar():    progress_bar.start()def stop_progressbar():    progress_bar.stop()def stop_after_second():    window.after(500, progress_bar.stop)# Create a container to hold labelsbuttons_frame = ttk.LabelFrame(mighty2, text=' ProgressBar ')buttons_frame.grid(column=0, row=3)# Place labels into the container elementttk.Button(buttons_frame, text=' Run Progressbar ',     command=run_progressbar).grid(column=0, row=0, sticky=tk.W)ttk.Button(buttons_frame, text='Start Progressbar',     command=start_progressbar).grid(column=0, row=1, sticky=tk.W)ttk.Button(buttons_frame, text='Stop immediately',     command=stop_progressbar).grid(column=0, row=2, sticky=tk.W)ttk.Button(buttons_frame, text='Stop after second',     command=stop_after_second).grid(column=0, row=3, sticky=tk.W)# Add a Progressbar to Tab 2progress_bar = ttk.Progressbar(tab2, orient='horizontal',     length=286, mode='determinate')progress_bar.grid(column=0, row=4, pady=2)# Tab 3 controltab3_frame = tk.Frame(tab3, bg='blue')tab3_frame.pack()for orange_color in range(2):    canvas = tk.Canvas(tab3_frame, width=150, height=80,        highlightthickness=0, bg='orange')    canvas.grid(row=orange_color, column=orange_color)# Exit GUI cleanlydef _quit():    window.quit()    window.destroy()    exit()# Creating a Menu Barmenu_bar = Menu(window)window.configure(menu=menu_bar)# Add menu itemsfile_menu = Menu(menu_bar, tearoff=0)file_menu.add_command(label='New')file_menu.add_separator()file_menu.add_command(label='Exit', command=_quit)menu_bar.add_cascade(label='File', menu=file_menu)# Display a message boxdef _msgBox():    # msg.showinfo('Python Message Info Box',     #   'A Python GUI created using tkinter:\n The year is 2017.')    # msg.showwarning('Python Message Warning Box',     #   'A Python GUI Cretaed using tkinter:'    #   '\nWarning: There might be a bug in this code.')    # msg.showerror('Python Message Error Box',     #   'A Python GUI created using tkinter:'    #   '\nError: Houston ~ We DO have a serious PROBLEM!')    answer = msg.askyesnocancel('Python Message Mutil Choice Box',         'Are you sure you really wish to do this?')    print(answer)# Add another Menu to the Menu Bar and an itemhelp_menu = Menu(menu_bar, tearoff=0)help_menu.add_command(label='About', command=_msgBox)menu_bar.add_cascade(label='Help', menu=help_menu)# Windows "ico" and Unix "xbm"# window.iconbitmap('卷纸.ico')img = tk.PhotoImage(file='卷纸.png')window.tk.call('wm', 'iconphoto', window._w, img)name_entered.focus()window.mainloop()

参考文献

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