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

posted @ 2020-06-06 08:27  vocus  阅读(2026)  评论(0编辑  收藏  举报