1.概述
tkinter是python的标准GUI库,用于创建可视界面。
一个人最简单的例子:
# 导入库 from tkinter import * # 初始化 root = Tk() # 用到再解释 root.mainloop()
也可以设置窗口的标题和属性
from tkinter import * root = Tk() root.title('hello') # 读取配置文件 root.option_readfile('optionDB') # Label用于显示文字的标签控件,pack显示 Label(root, text="hello world").pack() root.mainloop()
optionDB文件位于当前目录下,内容是
*font: SIMHEI 10 bold
表示 黑体,10号字,加粗显示;注意末尾应该使用换行符
2.Tkinter控件
2.1顶层(Toplevel)
顶层为其他控件提供容器
from tkinter import * root = Tk() root.title('Toplevel') # 读取配置文件 root.option_readfile('optionDB') # 主顶层,作为根被引用 Label(root, text="This is the main(default) toplevel").pack(pady=10) # 子顶层,依赖于根,当跟被破坏,子顶层也被破坏 t1 = Toplevel(root) Label(t1, text="This is a child of root").pack(padx=10, pady=10) # 临时顶层 总是画于父顶层顶部,当父顶层被图标化或最小化之后,其被隐藏 t2 = Toplevel(root) Label(t2, text="This is a transient window of root").pack(padx=10, pady=10) t2.transient(root) # 设置窗口边框为10,背景为蓝色 t3 = Toplevel(root, borderwidth=10, bg="blue") # 设置标签背景蓝色,前景色为白色 Label(t3, text="No wm decorations", bg="blue", fg="white").pack(padx=10, pady=10) # 设置overrideredirect为非零值,窗口不能被缩放或拖动 t3.overrideredirect(1) # 窗口大小是300x100,位置为(200,200) t3.geometry("300x100+200+200") root.mainloop()
2.2框架(Frame)
框架也是其他控件的容器
例1:显示不同样式的框架
from tkinter import * root = Tk() root.title('Frame') # for relief in [RAISED, SUNKEN, FLAT, RIDGE, GROOVE, SOLID]: for relief in ["raised", "sunken", "flat", "ridge", "groove", "solid"]: f = Frame(root, borderwidth=2, relief=relief) Label(f, text=relief, width=10).pack(side=LEFT) f.pack(side=LEFT, padx=5, pady=5) root.mainloop()


例2:显示不同边框的不同样式的框架

from tkinter import * class GUI: def __init__(self): self.root = Tk() self.root.title("Frame Style") # 5种不同的边框 for bdw in range(5): # of0,of1...of4,表示五个框架,每个框架表示一行 setattr(self, "of%d" % bdw, Frame(self.root, borderwidth=0)) # of0,of1...of4每行加入标签控件,文字分别是borderwidth0...borderwidth4,并于显示在左边 Label(getattr(self, "of%d" % bdw), text="borderwidth=%d" % bdw).pack(side=LEFT) ifx = 0 for relief in [RAISED, SUNKEN, FLAT, RIDGE, GROOVE, SOLID]: # f0,f1...f4表示每行地方五列 setattr(self, "f%d" % ifx, Frame(getattr(self, "of%d" % bdw), borderwidth=bdw, relief=relief)) # 画一行 Label(getattr(self, "f%d" % ifx), text=relief, width=10).pack(side=LEFT) getattr(self, "f%d" % ifx).pack(side=LEFT, padx=7-bdw, pady=5+bdw) ifx = ifx+1 getattr(self, "of%d" % bdw).pack() self.root.mainloop() myGUI = GUI()
例3:框架与按钮组合使用
from tkinter import * root = Tk() f = Frame(root, width=250, height=110) xf = Frame(f, relief=GROOVE, borderwidth=2) Label(xf, text="You shot him!").pack(pady=10) Button(xf, text="He's dead!", state=DISABLED).pack(side=LEFT, padx=5, pady=8) Button(xf, text="He's completely dead!", command=root.quit).pack(side=RIGHT, padx=5, pady=8) xf.place(relx=0.01, rely=0.125, anchor=NW) Label(f, text="Self-defence against fruit").place(relx=0.06, rely=0.125, anchor=W) f.pack() root.mainloop()

2.3标签(Label)
标签可以显示文字也可以显示图形,同时支持换行
from tkinter import * root = Tk() Label(root, text="stray birds of summer come to my window to sing and fly away." + "And yellow leaves of autumn,which have no songs,flutter and fall there with a sigh.", wraplength=200, justify=LEFT, relief=GROOVE).pack() f = Frame(root) for bitmap, rlf in [("woman", RAISED), ("mensetmanus", SOLID)]: Label(f, bitmap="@pic/%s" % bitmap, relief=rlf).pack(side=LEFT, padx=5) f.pack() root.mainloop()

通过这种方式读取图片,貌似只能读标准的rc文件
2.4按钮(Button)
严格来说,按钮是对鼠标和键盘事件起反应的标签。当按钮被点击的时候,可以为其绑定一个回调函数
from tkinter import * class GUI: def __init__(self): self.root = Tk() self.root.title("Button Style") for bdw in range(5): setattr(self, "of%d" % bdw, Frame(self.root, borderwidth=0)) Label(getattr(self, "of%d" % bdw), text="borderwidth=%d" % bdw).pack(side=LEFT) fx = 0 for relief in [RAISED, GROOVE, SOLID, FLAT, RIDGE, SUNKEN]: setattr(self, "f%d" % fx, Frame(getattr(self, "of%d" % bdw), borderwidth=bdw, relief=relief)) # Label(getattr(self, "f%d" % fx), text=relief).pack(side=LEFT) Button(getattr(self, "f%d" % fx), text=relief, command=lambda s=self, r=relief, b=bdw: s.prt(r, b)).pack(side=LEFT) getattr(self, "f%d" % fx).pack(side=LEFT, padx=7-bdw, pady=5+bdw) fx = fx + 1 getattr(self, "of%d" % bdw).pack() self.root.mainloop() def prt(self, relief, border): print("%s:%d" % (relief, border)) myGUI = GUI()
这个程序与框架的第二个实例相似。但是按钮在实例化的时候,可以传入command参数,用于接收一个回调函数。
2.5输入(Entry)
用于收集用户的输入
from tkinter import * root = Tk() Label(root, text="Anagram: ").pack(side=LEFT) # 字符串变量的容器 e = StringVar() Entry(root, width=40, textvariable=e).pack(side=LEFT) # 动态设置输入控件的值 e.set("o troupe of little vagrant of the world,leave your footprints in my words.") root.mainloop()
2.6单选按钮(Radiobutton)
实例1:
from tkinter import * root = Tk() var = IntVar() Radiobutton(root, text="apple", value=0, variable=var).pack(anchor=W) Radiobutton(root, text="orange", value=1, variable=var).pack(anchor=W) var.set(0) root.mainloop()


实例2:
from tkinter import * root = Tk() var = IntVar() for value, text in [(0, "apple"), (1, "orange"), (2, "mango"), (3, "banana")]: # 设置indicatoron=0时,单选按钮显示为按钮框,并且选中的按钮表示为凹的浮雕 Radiobutton(root, text=text, value=value, variable=var, indicatoron=0).pack(anchor=W, fill=X, ipadx=15) var.set(2) root.mainloop()

2.7复选按钮(Checkbutton)
例1:
from tkinter import * root = Tk() var1 = IntVar() var2 = IntVar() Checkbutton(root, text="apple", state=NORMAL, variable=var1).grid(row=0, column=0, sticky=W) Checkbutton(root, text="orange", state=DISABLED, variable=var2).grid(row=0, column=1, sticky=W) var1.set(1) root.mainloop()

这里要注意的是Checkbutton构造函数没有value参数,与单选按钮的不一样;不同的复选按钮的variable是不同的IntVar类型值
例2:
from tkinter import * class Dummy: pass var = Dummy() root = Tk() root.option_readfile('optionDB') root.title('Checkbutton') for castmember, row, col, status in [ ('John', 0, 0, NORMAL), ('Eric Idle', 0, 1, NORMAL), ('Graham Chapman', 1, 0, DISABLED), ('Terry Jones', 1, 1, NORMAL), ('Michael Palin', 2, 0, NORMAL), ('Terry Gilliam', 2, 1, NORMAL)]: setattr(var, castmember, IntVar()) Checkbutton(root, text=castmember, state=status, anchor=W, variable=getattr(var, castmember)).grid(row=row, column=col, sticky=W) var.John.set(1) getattr(var, "Eric Idle").set(1) root.mainloop()
2.8主菜单(Menubutton)
按钮较为复杂。所以这里从简单到复杂多写几个实例
2.8.1命令菜单(Command Menu)
创建命令菜单的步骤为:
①使用Menubutton创建一个菜单按钮
②使用Menu创建一菜单
③使用add_command添加命令菜单项
④将创建的菜单与菜单按钮关联
例1:
from tkinter import * root = Tk() def new_project(): print("new project") mBar = Frame(root, relief=RAISED, borderwidth=0) mBar.pack(fill=X) fileBtn = Menubutton(mBar, text="button", underline=0) fileBtn.pack(side=LEFT, padx="2m") # 建立菜单项 fileBtn.menu = Menu(fileBtn, tearoff=0) # 添加命令菜单 fileBtn.menu.add_command(label="New Project...", command=new_project) fileBtn.menu.add_command(label="New...", underline=0) # 图片菜单 fileBtn.menu.add_command(bitmap="@pic/RotateLeft") # 添加分割线 fileBtn.menu.add("separator") # 退出command=fileBtn.quit fileBtn.menu.add_command(label="quit", command=root.quit) # 将菜单赋给menubutton fileBtn['menu'] = fileBtn.menu root.mainloop()

2.8.2级联菜单(Cascade Menu)
例2:
from tkinter import * root = Tk() cascadeBtn = Menubutton(root, text="cascade menubutton") cascadeBtn.pack() # 一级菜单,以menubutton为根。变量名几乎是随便命名的。python类,为一个不存在的变量赋值,自动添加变量 cascadeBtn.menu = Menu(cascadeBtn) # 二级菜单,以一级菜单为根 cascadeBtn.menu.choices = Menu(cascadeBtn.menu) # 三级菜单,以二级菜单为根 cascadeBtn.menu.choices.vierdones=Menu(cascadeBtn.menu.choices) # 为三级菜单添加命令 cascadeBtn.menu.choices.vierdones.add_command(label="CRLF-Windows") cascadeBtn.menu.choices.vierdones.add_command(label="LF-Unix and macOS") cascadeBtn.menu.choices.vierdones.add_command(label="CR-Classic macOS") # 为二级菜单添加命令 cascadeBtn.menu.choices.add_command(label="File Encoding") cascadeBtn.menu.choices.add_command(label="Remove BOM") cascadeBtn.menu.choices.add_command(label="Associate with File Type...") cascadeBtn.menu.choices.add_command(label="Make File Read-Only") # 为二级菜单的一项命令关联三级菜单 cascadeBtn.menu.choices.add_cascade(label="Line Separators", menu=cascadeBtn.menu.choices.vierdones) # 一级菜单设置 cascadeBtn.menu.add_cascade(label="File Properties", menu=cascadeBtn.menu.choices) # 将一级菜单关联到menubutton上 cascadeBtn["menu"] = cascadeBtn.menu root.mainloop()


2.8.3其他菜单
复选菜单(Checkbutton Menu),单选菜单(Radiobutton Menu)和禁用菜单(Disable Menu)
from tkinter import * root = Tk() menuBtn = Menubutton(root, text="menubutton") menuBtn.pack() m1 = Menu(menuBtn) m1.add_command(label="apple") # 多选菜单,可以同时选中多个 m1.add_checkbutton(label="pear") m1.add_checkbutton(label="watermelon") # 将pear设置为选中 m1.invoke(m1.index("pear")) # 单选菜单,只能同时选中一个 m1.add_radiobutton(label="lemon") m1.add_radiobutton(label="orange") m1.invoke(m1.index("orange")) # 禁用菜单 m1.add_command(label="durian", state=DISABLED) # 关联进menubutton menuBtn["menu"] = m1 root.mainloop()
2.9消息(Message)
用于显示多行文本
from tkinter import * root = Tk() Message(root, text="He wishes for the cloths of heaven" "Had I the heaven's embroidered cloths," "Enwrought with golden and silver light," "The blue and the dim and the dark cloths," "I would spread the cloths under your feat:" "But I,being poor,have only my dreams;" "I have spread my dreams under your feet;" "Tread softly because you tread on my dreams.", bg="royalblue", fg="ivory", relief=GROOVE).pack(padx=10, pady=10) root.mainloop()

可以加\n换行符给文本换行
2.10文本(Text)
文本控件提供格式化的文本显示。
例1:
from tkinter import * root = Tk() # 这里的宽和高并不是像素,而是用当前字体的字符个数来测量 text = Text(root, height=20, width=70) # 插入文字 # 在文尾插入文字 text.insert(END, "Something up with my banter,chaps?\n") # 在第一行,第一列插入文字 text.insert(1.0, "Four hours to bury a cat?\n") # text.insert(2.1, "Can i call you 'Frank'?\n") # 当前位置插入 text.insert(CURRENT, "Can i call you 'Frank'?\n") # 设置标签 text.tag_config("bold_italics", font=("verdana", 12, "bold", "italic")) text.insert("3.0", "What's happening Thursday then?\n", "bold_italics") # 插入按钮 # 定义一个按钮 button = Button(text, text="I do live at 46 Horton terrace") # 创建窗口放置按钮 text.window_create(END, window=button) # 插入图片 # 创建图片对象。这个完全不行,一般的图片都加载不了。所以用到第三方库PIL # photo = PhotoImage(file="pic/lumber.gif") # text.image_create(END, image=photo) # 使用PIL库加载图片 from PIL import Image, ImageTk img = Image.open("pic/tree.gif") # 缩放一下 img = img.resize((100, 100)) photo = ImageTk.PhotoImage(img) text.image_create(CURRENT, image=photo) # 绑定事件 # 创建事件标签 text.tag_bind("bite", "<1>", lambda e, t=text: t.insert(END, "I'll bite your legs off!")) text.insert(END, "I dare you to click on this\n", "bite") text.pack() root.mainloop()
例2:
import tkinter.filedialog import tkinter.messagebox from tkinter import * root = Tk() # 打开文件 def open_file(): filename = tkinter.filedialog.askopenfilename(defaultextension=".txt", initialdir="f:") print(filename) root.title(filename) with open(filename, "r") as f: try: # 先清空,再写入 text.delete(1.0, END) text.insert(1.0, f.read()) set_style(text) except: tkinter.messagebox.showwarning("warning", "fail to open a file") def set_style(t): # 返回指定位置字符的左上角坐标和宽高,单位为像素 x, y, w, h = t.bbox(3.2) print(x, y, w, h) # 菜单设置 frame = Frame(root, relief="solid") frame.pack(fill=X) fileBtn = Menubutton(frame, text="File") fileBtn.pack(side=LEFT) fileBtn.menu = Menu(fileBtn, tearoff=0) fileBtn.menu.add_command(label="open...", command=open_file) fileBtn.menu.add("separator") fileBtn.menu.add_command(label="quit", command=root.quit) fileBtn["menu"] = fileBtn.menu # 文本处理 text = Text(root, height=20, width=70) text.pack() root.mainloop()
2.11画布(Canvas)
http://www.cnblogs.com/vocus/p/11470707.html
2.12列表框(ListBox)
from tkinter import * root = Tk() ls = Listbox(root, width=15) ls.pack() for item in range(10): ls.insert(END, item) root.mainloop()
补充:
# 选中第二个值,可以多选一个区间 ls.selection_set(1,) # 点击事件 def print_c(event): print(ls.curselection()) print(ls.get(ls.curselection())) ls.bind("<1>", print_c)
2.13滚动条(Scrollbar)
滚动条能被加到任何支持滚动,如文本、画布和列表框控件上
from tkinter import * root = Tk() ls = Listbox(root, height=6, width=15) # 创建滚动条对象,并且设置滚动与列表框y轴关联 scroll = Scrollbar(root, command=ls.yview) # 配置列表框y轴滚动属性,指定其回调函数为scroll.set ls.configure(yscrollcommand=scroll.set) ls.pack(side=LEFT) scroll.pack(side=RIGHT, fill=Y) for item in range(30): ls.insert(END, item) root.mainloop()

2.14标尺(Scale)
from tkinter import * # 通过修改矩形和三角形(合成一个向下的箭头)来实现动画效果 def set_height(c, hstr): h = int(hstr) print(h) if h > 210: h = 210 # 修改三角形的坐标 c.coords("poly", 0, 30+h, 40, 30+h, 20, 40+h) # 修改矩形的坐标 c.coords("rectangle", 10, 30+h, 10, 0, 30, 0, 30, 30+h, 10, 30+h) root = Tk() canvas = Canvas(root, height=250, width=40, highlightthickness=0) canvas.grid(row=0, column=1, sticky="WN") # 不分成两部分更好 # poly_point = [(10, 0), (30, 0), (30, 30), (40, 30), (20, 40), (0, 30), (10, 30)] # canvas.create_polygon(poly_point, fill="cadetblue") # 动态效果,通过canvas.coords修改坐标项实现动态效果,初始化是一个三角形和一个矩形。 canvas.create_polygon(0, 30, 40, 30, 20, 40, fill="cadetblue", tags="poly") canvas.create_polygon(10, 30, 10, 0, 30, 0, 30, 30, 10, 30, fill="cadetblue", tags="rectangle") scale = Scale(root, orient=VERTICAL, length=284, from_=0, to=250, tickinterval=50, # scale内部似乎维护了一个变量h,即为标尺滑动的高度 command=lambda h, c=canvas: set_height(c, h)) scale.grid(row=0, column=0, sticky="NE") root.mainloop()

3.Pmw大控件
Pmw大控件是以Tkinter为基类合成的控件
3.1关于对话框(Pmw.AboutDialog)
# 导入大控件库 import Pmw from tkinter import * root = Tk() # 关于版本 Pmw.aboutversion("1.5") # 关于版权 Pmw.aboutcopyright("Copyright Company Name 1999\nAll rights reserved") # 关于联系方式 Pmw.aboutcontact( "For information about this application contact:\n" + "Sales at Company Name\n" + "Phone...\n" + "email..." ) # 创建关于对话框 about = Pmw.AboutDialog(root, applicationname="this application") root.mainloop()


可以看出创建一个关于对话框很简单,但是显示效果见仁见智。
3.2输入域(Pmw.EntryField)
输入域包含一个label和一个输入框控件。同时可以检查输入框控件里的内容格式
例1:
import Pmw from tkinter import * root = Tk() # labelpos指label的现实位置北,南,东,西分别为N,S,E,W,label_text标签文字 Pmw.EntryField(root, labelpos=W, label_text="username").pack(padx=10, pady=5) Pmw.EntryField(root, labelpos=W, label_text="password").pack(padx=10, pady=5) Button(root, text="Login").pack() root.mainloop()


做一个登录框界面很方便
例2:
import Pmw from tkinter import * root = Tk() # 建议初始化一下,也可以不用 Pmw.initialise() # 只能输入a~z和A~Z,使用标识alphabetic,min表示最小长度,max表示最大长度;minstrict设置为false,表示不检查最小输入,maxstrict表示不检查最大输入 user = Pmw.EntryField(root, labelpos=W, label_text="username", value="user", validate={"validator": "alphabetic", "min": 5, "max": 10, "minstrict": 0}) # 只能输入数字+字母组合,使用alphanumeric标识 pwd = Pmw.EntryField(root, labelpos=W, label_text="password", value="pwd", validate={"validator": "alphanumeric", "min": 5, "max": 10, "minstrict": 0}) # 只能输入数字,可以使用numeric标识 lucky = Pmw.EntryField(root, labelpos=W, label_text="lucky", value="21", validate={"validator": "numeric", "min": 0, "max": 100}) # 只能输入指定格式日期,时间格式貌似只能是2010/1/1 birthday = Pmw.EntryField(root, labelpos=W, label_text="birthday", value="1900/1/1", validate={"validator": "date", "min": "1900/1/1", "max": "2020/1/1"}) # 不检查格式 sign = Pmw.EntryField(root, labelpos=W, label_text="signature", value="no limitation", validate=None) widgets = (user, pwd, lucky, birthday, sign) for widget in widgets: widget.pack(fill=X, expand=1, padx=10, pady=5) # 使控件对齐 Pmw.alignlabels(widgets) # 使密码输入框获得输入焦点 # sign.component("entry").focus_set() Button(root, text="Login").pack() root.mainloop()

备注,使用validate设置输入内容格式,接收的参数为一个字典,固定格式如下:
{“validator": "real", "max":0, "min":0, "minstrict": "0"}
其中,validator键的值可设置为:
'numeric'
An integer greater than or equal to 0. Digits only. No sign.
'integer'
Any integer (negative, 0 or positive) as accepted by string.atol().
'hexadecimal'
Hex number (with optional leading '0x'), as accepted by string.atol(text, 16).
'real'
A number, with or without a decimal point and optional exponent (e or E), as accepted by string.atof(). This validator accepts a 'separator' argument, which specifies the character used to represent the decimal point. The default 'separator' is '.'.
'alphabetic'
Consisting of the letters 'a-z' and 'A-Z'. In this case, 'min' and 'max' specify limits on the length of the text.
'alphanumeric'
Consisting of the letters 'a-z', 'A-Z' and '0-9'. In this case, 'min' and 'max' specify limits on the length of the text.
'time'
Hours, minutes and seconds, in the format 'HH:MM:SS', as accepted by Pmw.timestringtoseconds(). This validator accepts a 'separator' argument, which specifies the character used to separate the three fields. The default separator is ':'. The time may be negative.
'date'
Day, month and year, as accepted by Pmw.datestringtojdn(). This validator accepts a 'separator' argument, which specifies the character used to separate the three fields. The default is ':'. This validator also accepts a 'format' argument, which is passed to Pmw.datestringtojdn() to specify the desired ordering of the fields. The default is 'ymd'.
An integer greater than or equal to 0. Digits only. No sign.
'integer'
Any integer (negative, 0 or positive) as accepted by string.atol().
'hexadecimal'
Hex number (with optional leading '0x'), as accepted by string.atol(text, 16).
'real'
A number, with or without a decimal point and optional exponent (e or E), as accepted by string.atof(). This validator accepts a 'separator' argument, which specifies the character used to represent the decimal point. The default 'separator' is '.'.
'alphabetic'
Consisting of the letters 'a-z' and 'A-Z'. In this case, 'min' and 'max' specify limits on the length of the text.
'alphanumeric'
Consisting of the letters 'a-z', 'A-Z' and '0-9'. In this case, 'min' and 'max' specify limits on the length of the text.
'time'
Hours, minutes and seconds, in the format 'HH:MM:SS', as accepted by Pmw.timestringtoseconds(). This validator accepts a 'separator' argument, which specifies the character used to separate the three fields. The default separator is ':'. The time may be negative.
'date'
Day, month and year, as accepted by Pmw.datestringtojdn(). This validator accepts a 'separator' argument, which specifies the character used to separate the three fields. The default is ':'. This validator also accepts a 'format' argument, which is passed to Pmw.datestringtojdn() to specify the desired ordering of the fields. The default is 'ymd'.
例3:
也可以自定义输入条件
import Pmw from tkinter import * root = Tk() class Demo: def __init__(self, parent): self._real = Pmw.EntryField(parent, labelpos=W, label_text="score", value=10, validate={"validator": "real", "min": 0, "max": 99, "minstrict": 0}) self._color = Pmw.EntryField(parent, labelpos=W, label_text="color", value="#bbbbbb", validate=self.custom_validate) def show(self): self._real.pack() self._color.pack() # 检查输入是#六位颜色值,算法应该可以优化 def custom_validate(self, text): print(text) if len(text) is not 7 or text[0] != "#": return -1 temp = text[1:7] for i in temp: if (i < '0' or i > '9') and (i < 'a' or i > 'f') and (i < 'A' or i > 'F'): return -1 return 1 myDemo = Demo(root) myDemo.show() root.mainloop()

在这里自定义了一个函数,用于检查输入的文本是#加上六位颜色值
3.2浮动图(Pmw.Balloon)
import Pmw from tkinter import * root = Tk() balloon = Pmw.Balloon(root) field = Pmw.EntryField(root, labelpos="w", label_text="Input") field.setentry("your name") field.pack() balloon.bind(field, "please input your name", "msg") root.mainloop()

3.3按钮框(Pmw.ButtonBox)
import Pmw from tkinter import * root = Tk() def button_press(text): print(text) def default_key(event): buttonBox.invoke() buttonBox = Pmw.ButtonBox(root, labelpos=NW, label_text="button box") buttonBox.pack() buttonBox.add("Ok", command=lambda text="ok": button_press(text)) buttonBox.add("Cancel", command=lambda text="cancel": button_press(text)) # 设置获得焦点按钮 buttonBox.setdefault("Cancel") # 事件绑定,回车键 root.bind("<Return>", default_key) # root.focus_set() # 到目前为止,我理解它是用于排版 buttonBox.alignbuttons() root.mainloop()
3.4组合框(Pmw.ComboBox)
import Pmw from tkinter import * root = Tk() toplevel = Toplevel(root) toplevel.title("play") toplevel.geometry("200x50+200+200") label = Label(toplevel, bg="#fff", fg="blue") label.pack(fill=X, pady=15) # 隐藏顶层窗口 toplevel.withdraw() toplevel.update() def chose_entry(entry): print("you chose %s" % entry) # 更新显示顶层窗口 toplevel.deiconify() toplevel.update() label.configure(text=entry) music_list = ["sound of silence", "hero", "break free", "halo"] # label_text去掉就是简单的列表框,selectioncommand,listbox_width列表框宽度,selectioncommand列表点击事件,scrolllist_items列表数据,dropdown=0列表不隐藏 comboBox = Pmw.ComboBox(root, labelpos=NW, label_text="Music List", listbox_width=24, selectioncommand=chose_entry, scrolledlist_items=music_list, dropdown=0) comboBox.pack() root.mainloop()


这个关闭topleve窗口之后,就无法调用topleve.deiconify来显示了,程序会报错。
3.5组合对话框(Pmw.ComboBoxDialog)
import Pmw from tkinter import * root = Tk() musicList = ["just one last dance", "from sarah with love", "stronger"] # selectioncommand属性被去掉了貌似,可以通过comboBoxDialog.get()获得列表选择 comboBoxDialog = Pmw.ComboBoxDialog(root, combobox_labelpos=W, label_text="play", scrolledlist_items=musicList, listbox_width=25, buttons=("OK", "Cancel"), defaultbutton="Ok") # 可有可无 # comboBoxDialog.pack() # 获得按钮点击选择 result = comboBoxDialog.activate() choice = comboBoxDialog.get() print("%s,%s" % (result, choice)) root.mainloop()

3.6对话框(Pmw.Dialog)
import Pmw from tkinter import * root = Tk() def default_key(result): print(result) # 对话框退不出也是一个问题了,只能关闭主窗口? if result == "Cancel": # root.quit() root.destroy() # 点击关闭按钮的时候result是None if result is None: root.destroy() dialog = Pmw.Dialog(root, title="dialog", buttons=("Ok", "Cancel"), defaultbutton="Cancel", command=lambda result: default_key(result)) # interior()方法返回对话框子域 label = Label(dialog.interior(), text="Pmw Dialog\n Bring out your dead!", bg="black", foreground="white", pady=20) label.pack() root.bind("<Return>", default_key) dialog.activate() root.mainloop()
关不掉对话框可以试着加上deactivate()
3.7计数器(Pmw.Counter)
https://www.cnblogs.com/vocus/p/11563263.html
import Pmw from tkinter import * root = Tk() counter = Pmw.Counter(root) counter.grid(row=0, column=1) # 设置初始值 counter.setentry(2) # +1 counter.increment() # +1 counter.increment() # -1 counter.decrement() # datatype数据类型,time表示设置成时间格式00:00:00,buttonaspect用于设置箭头大小,缺省是1.0,orient表示箭头所在位置 counter1 = Pmw.Counter(datatype="time", increment=60, buttonaspect=2.0, orient=VERTICAL) counter1.grid(row=1, column=1) counter1.setentry("01:00:00") # 获得值 print("entry:%s" % counter1.get()) print("increment:%s" % counter1.cget("increment")) print("increment:%s" % counter1.cget("buttonaspect")) print(counter1.configure("increment")) counter3 = Pmw.Counter(root, pady=20, padx=20) counter3.grid(row=2, column=1) # 配置部件属性,使用configure # 改变箭头北京颜色,以及箭头颜色。entry_foreground非官方文档的作法,这样改变之后,输入框文本也变色了 counter3.configure(downarrow_background="#3c3f41", uparrow_background="#3c3f41", entry_foreground="#c75450") # 改变输入域背景色,前景色改变之后,箭头的颜色也改变了 counter3.configure(entryfield_entry_background="#3c3f41", entryfield_entry_foreground="#a73e28") counter3.setentry(99) root.mainloop()
3.8计数对话框(Pmw.CounterDialog)
这个是继承自Pmw.dialog,所以具有一些dialog和counter的属性,可以试试
import Pmw from tkinter import * root = Tk() # CounterDialog cd = Pmw.CounterDialog(root, buttons=("Ok", "Cancel"), defaultbutton="Cancel") cd.setentry("3") num = cd.get() result = cd.activate() print(num, result) root.mainloop()

3.9组(Pmw.Group)
import Pmw from tkinter import * root = Tk() group = Pmw.Group(root, tag_text="group", tag_foreground="blue", tag_pyclass=Checkbutton) group.pack(fill=X) label = Label(group.interior(), text="apple") label.pack() root.mainloop()
3.10标签控件(Pwm.Labeledwidget)
import Pmw from tkinter import * root = Tk() labelWidget = Pmw.LabeledWidget(root, labelpos=N, label_text="Image Show") # hull指整个大控件本体 labelWidget.component("hull").configure(borderwidth=3, relief=SUNKEN) labelWidget.pack(padx=10, pady=10) # 加载图片 from PIL import Image, ImageTk img = Image.open("pic/rose.jpg") photo = ImageTk.PhotoImage(img) btn = Button(labelWidget.interior(), bg="yellow", image=photo) btn.pack(padx=10, ipady=10, fill="both", expand=1) root.mainloop()

3.11消息对话(Pmw.MessageDialog)
import Pmw from tkinter import * root = Tk() md = Pmw.MessageDialog(root, title="Message Dialog", buttons=("apple", "banana", "pear", "lemon"), message_text="choose your favourite fruit") # md.iconname("Simple message dialog") result = md.activate() print(result) root.mainloop()

这个跟ButtonBox很像,不过区别也许是Button和Dialog的区别吧
3.12消息栏(Pmw.MessageBar)
???????????ScrolledListBox
3.13菜单条(Pmw.MenuBar)
比tkinter的原生Menu组件更容易创建菜单,同时还可以直接添加浮动帮助功能
import Pmw from tkinter import * root = Tk() balloon = Pmw.Balloon(root) # hull_relief,hull_borderwidth menuBar = Pmw.MenuBar(root, hull_relief=RAISED, hull_borderwidth=1, balloon=balloon) menuBar.pack() # 添加菜单,第一个参数菜单名,第二个参数是浮动内容 menuBar.addmenu("Buttons", "Simple Commands") # 第一个参数菜单名,第二个参数文档写的是浮动帮助信息,但是这里看来是item类型,第三个参数状态栏帮助信息,font用于设置字体,label设置文本 menuBar.addmenuitem("Buttons", "command", "Close this window", font=("StringerLight", 14), label="Close") from PIL import Image, ImageTk img = Image.open("pic/apple.jpg") img = img.resize((30, 30)) photo = ImageTk.PhotoImage(img) # 古老的程序喜欢用bitmap menuBar.addmenuitem("Buttons", "command", "Close this window", image=photo) # 添加分割线 menuBar.addmenuitem('Buttons', 'separator') # 普通的命令菜单项 menuBar.addmenuitem('Buttons', 'command', 'Exit the application', label='Exit') # 添加一个菜单 menuBar.addmenu("Cascade", "Cascading Menus") # 添加一项级联菜单,注意第二个参数为自定义的该级联菜单的名称标识 menuBar.addcascademenu("Cascade", "Submenu", "unknown", label="cascade") # 在级联菜单下加入一个菜单项项 menuBar.addmenuitem("Submenu", "command", "unknown", label="normal") # 添加一个radiobutton风格菜单项 menuBar.addmenuitem("Cascade", "radiobutton", "unknown", label="simple") root.mainloop()


3.14选项菜单(Pmw.OptionMenu)
简单实现弹出式菜单
import Pmw from tkinter import * root = Tk() var = StringVar() optMenu = Pmw.OptionMenu(root, labelpos=W, label_text="Chose one fruit", menubutton_textvariable=var, menubutton_width=20, items=("mango", "peach", "cherry")) optMenu.pack() root.mainloop()

这个效果可以说是很奇葩了
3.15记事本(Pmw.NoteBook)
老的版本可以使用NoteBookR和NoteBookS创建记事本,但是在教新(?)版本里,已经被废弃了。取而代之的是NoteBook
另外,这里还有点问题,可能是Pmw版本的问题,运行《Python与Tkinter编程》随书源码的时候会出错,源码如下(我加了一些注释):
from tkinter import * import Pmw root = Tk() # root.option_readfile('optionDB') root.title('Notebook') # Pmw.initialise() # 创建三个页面,并设置标题 nb = Pmw.NoteBook(root) p1 = nb.add('Page 1') p2 = nb.add('Page 2') p3 = nb.add('Page 3') nb.pack(padx=5, pady=5, fill=BOTH, expand=1) # 第一个显示一个button Button(p1, text='This is text on page 1', fg='blue').pack(pady=40) # 第二页画个图 c = Canvas(p2, bg='gray30') # 窗口宽高 w = c.winfo_reqwidth() h = c.winfo_reqheight() # 画一个椭圆 c.create_oval(10, 10, w-10, h-10, fill='DeepSkyBlue1') # 文字 c.create_text(w/2, h/2, text='This is text on a canvas', fill='white', font=('Verdana', 14, 'bold')) c.pack(fill=BOTH, expand=1) # setnaturalpagesize 变为setnaturalsize # nb.setnaturalpagesize() nb.setnaturalsize() root.mainloop()
第一个报错:
在实例化的时候,就报错了,提示
...
'#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
TypeError: %x format: an integer is required, not float
TypeError: %x format: an integer is required, not float
找到报错的代码行,是在PmwColor.py文件的这段代码:
def bordercolors(root, colorName):
....
....
return (
'#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
'#%04x%04x%04x' % (darkRGB[0], darkRGB[1], darkRGB[2])
)
'#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
'#%04x%04x%04x' % (darkRGB[0], darkRGB[1], darkRGB[2])
)
解决办法很简单,只要把float转换成int输出就好了:
return (
'#%04x%04x%04x' % (int(lightRGB[0]), int(lightRGB[1]), int(lightRGB[2])),
'#%04x%04x%04x' % (int(darkRGB[0]), int(darkRGB[1]), int(darkRGB[2]))
)
'#%04x%04x%04x' % (int(lightRGB[0]), int(lightRGB[1]), int(lightRGB[2])),
'#%04x%04x%04x' % (int(darkRGB[0]), int(darkRGB[1]), int(darkRGB[2]))
)
第二个报错是
AttributeError: 'NoteBook' object has no attribute 'setnaturalpagesize'
把setnaturalpagesize 修改为setnaturalsize
这个函数可以自动调节Notebook的大小,而且应该放在代码的末尾处


3.16窗格控件(Pmw.PanedWidget)
这个效果也是有点奇葩
from tkinter import * import Pmw root = Tk() paned = Pmw.PanedWidget(root, hull_width=300, hull_height=200) paned.add("top", min=100) paned.add("bottom", min=100) paned.pack() root.mainloop()

中间有一条可调整上下两部分pane的分割线
from tkinter import * import Pmw root = Tk() # 创建一个pane pane = Pmw.PanedWidget(root, hull_width=300, hull_height=200) pane.pack() # 添加top和bottom两个部分,最下值为100 pane.add("top", min=100) pane.add("bottom", min=100) # pane.pane("top")取的上半部分pane的索引,HORIZONTAL,在横向上划分 topPane = Pmw.PanedWidget(pane.pane("top"), orient=HORIZONTAL) topPane.pack() # 0~1之间的浮点数表示的应该是占半分比 topPane.add("apple", min=.2) topPane.add("pear", min=.2) btn1 = Button(topPane.pane("apple"), text="apple") btn2 = Button(topPane.pane("pear"), text="pear") btn1.pack() btn2.pack() root.mainloop()

3.17提示对话框(Pmw.PromptDialog)
一个对话框加一个EntryField
from tkinter import * import Pmw root = Tk() def print_c(choice): print(choice) promptDlg = Pmw.PromptDialog(root, title="PASSWORD", entryfield_labelpos=N, label_text="password", # command=print_c entry_show="*", buttons=("Ok", "Cancel")) promptDlg.pack() result = promptDlg.activate() print(result) root.mainloop() 3.18单选选项(Pmw.RadioSelect) from tkinter import * import Pmw root = Tk() def print_c(*args): print(args) # selectmode=MULTIPLE表示可以多选 rs = Pmw.RadioSelect(root, labelpos=W, label_text="fruit", frame_borderwidth=3, frame_relief=RIDGE, command=print_c, selectmode=MULTIPLE) rs.pack() for text in ("apple", "pear", "banana", "lemon", "melon", "peach"): rs.add(text) # 默认选择 rs.invoke("apple") root.mainloop()

3.18单选选项(Pmw.RadioSelect)
def print_c(*args): print(args) # selectmode=MULTIPLE表示可以多选 rs = Pmw.RadioSelect(root, labelpos=W, label_text="fruit", frame_borderwidth=3, frame_relief=RIDGE, command=print_c, selectmode=MULTIPLE) rs.pack() for text in ("apple", "pear", "banana", "lemon", "melon", "peach"): rs.add(text) # 默认选择 rs.invoke("apple")
3.19文本对话框(Pmw.TextDialog)
from tkinter import * import Pmw root = Tk() discuss = """Jack:Nice to meet you! Bruce:Nice to meet you,too! """ textDialog = Pmw.TextDialog(root, scrolledtext_labelpos=N, label_text="discuss", title="TextDialog", defaultbutton=0) textDialog.pack() textDialog.insert(END, discuss) textDialog.configure(text_state=DISABLED) # textDialog.activate() textDialog.tkraise() root.mainloop()
3.20时间计数(Pmw.TimeCounter)
from tkinter import * import Pmw root = Tk() tc = Pmw.TimeCounter(root, labelpos=W, label_text="Time Counter", min="00:00:00", max="23:59:59") tc.pack() root.mainloop()

运行一下,它直接获得系统的时间填入
3.21滚动画布(Pmw.ScrolledCanvas)
这里先弄清一个东西,用canvas画图的时候,默认是以像素作为单位的,除此之外,还可以使用c作为单位。因为找不到参考文档,所以我猜测c是厘米吧,因为厘米是cm;例外我试了一下也可以使用m做单位,1m刚好是1c的十分之一,所以我猜测是毫米?比如:
from tkinter import * root=Tk() cv1=Canvas(root,width="3c",height="3c",bg="DodgerBlue") cv1.pack(side=LEFT) cv2=Canvas(root,width="30m",height="30m",bg="Green3") cv2.pack(side=RIGHT) root.mainloop()

from tkinter import * import Pmw root = Tk() # borderframe框架是否有边界,设置为True或False;usehullsize,如果设为True则大控件的大小只由主体组件觉得,否则由其他组件一起决定; sc = Pmw.ScrolledCanvas(root, borderframe=1, labelpos=N, label_text="ScrolledCanvas", usehullsize=1, hull_width="400", hull_height="300") sc.pack() # 画30x10个,矩形宽高为2c*2c,矩形之间间隔为1c for i in range(30): x = -10 + 3*i y = -10 for j in range(10): # 画矩形 sc.create_rectangle("%dc" % x, "%dc" % y, "%dc" % (x+2), "%dc" % (y+2), fill="gold1") # 画文字 sc.create_text("%dc" % (x+1), "%dc" % (y+1), text="(%d, %d)" % (i, j), fill="indianred1") y = y+3 # 加了这句才会添加滚动 sc.resizescrollregion() root.mainloop()

3.22滚动区域(Pmw.ScrolledField)
from tkinter import * import Pmw root = Tk() sf = Pmw.ScrolledField(root, label_text="Scrolled Field", labelpos=N, entry_width=20) # 设置输入域文本 sf.configure(text="Tom eats an apple") sf.pack() text = ["Jerry broke a glass", "Hannah is swimming", "Pony is a pony"] index = 0 def scroll(): global index # sf.configure(text=text[index]) # 取余 sf.configure(text=text[index % len(text)]) index = index+1 ''' if index == len(text): index = 0 ''' btn = Button(root, text="scroll it", command=scroll) btn.pack() root.mainloop()

这也不是自动滚动什么的?还是因为我是鼠标坏掉了?
3.23滚动框架(Pmw.ScrolledFrame)
写个九九乘法表
from tkinter import * import Pmw root = Tk() from tkinter.dialog import * import tkinter.messagebox as msg def calc(a, b): # dlg = Pmw.Dialog(root, buttons=("Ok",), defaultbutton="Ok") # Label(dlg.interior(), text="%d" % (a*b)).pack() Dialog(None, title="answer", text="%d" % (a*b), strings=("Ok",), default=0, bitmap=DIALOG_ICON) # msg.showinfo("answer", "%d" % (a*b)) sf = Pmw.ScrolledFrame(root, labelpos=N, label_text="Scrolled Frame", hull_width=300, hull_height=200, usehullsize=1, borderframe=1) f = sf.interior() sf.pack() for i in range(9): fm = Frame(f) fm.pack(anchor=NW) y = i+1 for j in range(y): btn = Button(fm, text="%d x %d" % (i+1, j+1), bg="moccasin", borderwidth=1, command=lambda a=i+1, b=j+1: calc(a, b)) btn.pack(side=LEFT) sf.component("frame").configure(bg="linen") root.mainloop()

3.24滚动列表框(Pmw.ScrolledListBox)
from tkinter import * import Pmw root = Tk() def double_click(): items = slb.getcurselection() if len(items) == 0: print("have no select item") else: print(items[0]) print() # selectioncommand点击事件,dblclickcommand双击事件,usehullsize=True大小由主体组件决定, # vscrollmode="static"显示垂直滚动条,并且总是显示,listbox_selectmode设置单选或多选 slb = Pmw.ScrolledListBox(root, labelpos=N, label_text="Scrolled Listbox", items=("rose", "jasmine", "lily", "chrysanthemums", "sakura", "orchid"), dblclickcommand=double_click, hull_width=200, hull_height=150, usehullsize=1, vscrollmode="static", listbox_selectmode=MULTIPLE) slb.pack() root.mainloop()

3.25滚动文本(Pmw.ScrolledText)
from tkinter import * import Pmw root = Tk() # text_wrap="none"不自动换行 st = Pmw.ScrolledText(root, borderframe=1, hull_width=400, hull_height=300, usehullsize=1, labelpos=N, label_text="Scroll text", text_pady=5, text_padx=10, text_wrap="none", vscrollmode="static") st.pack() st.importfile("he Falling Of the Leaves.txt") root.mainloop()

3.26选项对话(Pwm.SelectionDialog)
from tkinter import * import Pmw root = Tk() def get_item(result): c = sdlg.getcurselection() # c是个元组,判断一下它的长度,这里略了 print(c[0]) print(result) # 注意deactivate和activate sdlg.deactivate(result) sdlg = Pmw.SelectionDialog(root, title="SelectionDialog", scrolledlist_items=("tree", "grass", "flower", "bird", "cloud", "wind"), scrolledlist_labelpos=N, label_text="nature", buttons=("ok", "cancel"), defaultbutton="ok", command=get_item) sdlg.activate() root.mainloop()
John E Grayson Python与Tkinter编程
