Tkinter控件和Pmw大控件的實例


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'.
例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
找到報錯的代碼行,是在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])
    )
解決辦法很簡單,只要把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]))
    )
第二個報錯是
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編程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM