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編程
