前言
Tkinter是一个Python2.7和Python3.x都内置的GUI模块, 使用较为简单, 对于制作一个桌面工具性小程序足够, 而且能够跨平台. 但是, 它的官方文档很差.
我个人建议如果做团队自己用的桌面小程序, 完全可以使用它来写, 特别是那种就几个按钮的东西. 如果是大型的复杂的, 面向用户的程序, 则完全不建议使用Tkinter.
Tkinter最好的学习使用办法是参考别人的用例, 根据用例来体会各个widget的用法. 本文中, 我为了做一个登陆和聊天的程序, 分别了解了Tkinter的一些常用组件, 并且给出了一些用例.
用例1: label, pack, button
# -*- coding:utf-8 -*-
from Tkinter import *
def addHisMsg():
global root
new_label = Label(root, text="Hi, Xiaolong!", background='pink')
new_label.pack(side=TOP)
def addMyMsg():
global root
new_label = Label(root, text="Hi, Ling Liu!", background='green')
new_label.pack(side=TOP)
root = Tk() # 根节点
root.title('我的窗口')
root.geometry('300x200')
label1 = Label(root, text='消息1', background='red') # label显示基本的文字和背景颜色
label2 = Label(root, text='消息1', background='yellow')
label3 = Label(root, text='消息1', background='blue')
label1.pack() # pack是最基本的排列方式, 表示直接**往下居中**地排列
label2.pack()
label3.pack()
btn1 = Button(root, text='send', command=addHisMsg) # button的command可以设置一个函数变量来负责响应单机button的事件
btn2 = Button(root, text='send', command=addMyMsg)
btn1.pack()
btn2.pack()
root.mainloop() # 根节点需要用一个mainloop来循环地刷新图形画面
用例2: grid, 登录框的制作
from Tkinter import *
root = Tk()
root.title('请登录...')
#root.geometry("400x200") # 使用geometry可以控制root窗口的大小
def login_check(event): # 使用第二种方法设置事件, 需要响应函数的第一个参数为event
global root
username = e1.get() # entry.get()获取entry框的输入
password = e2.get()
if username == 'chen' and password == '123':
l1['text'] = 'login successful'
else:
l1['text'] = 'login failed'
l1.config(fg="red") # 设置label中字体颜色使用的是fg属性
e2.delete(0, len(password)) # 删除entry框中用户已经输入的文字, 两个参数分别代表开始和结束(左闭右开区间)
def register(event):
pass
Label(root, text='账户:').grid(row=0, column=0) # grid指的是网格化排版, 是我们将来在tkinter中最主要的排版方式
e1 = Entry(root)
e1.grid(row=0, column=1)
Label(root, text='密码:').grid(row=1, column=0)
e2 = Entry(root, show="*")
e2.bind_class('Entry', '', login_check)
e2.grid(row=1, column=1)
b1 = Button(root, text="login")
b1.bind('', login_check)
b1.grid(row=2, column=1)
b2 = Button(root, text="register")
b2.bind('', register)
b2.grid(row=2, column=0)
l1 = Label(root, text='(Press enter)')
l1.grid(row=3, column=1)
l1 = Label(root, text='')
l1.grid(row=3, column=0)
root.mainloop()
用例3: 顶部菜单条和右键上下文菜单制作
from Tkinter import *
root = Tk()
root.title('Game Lobby Ver0.5')
# 顶部总菜单条
menuBar = Menu(root)
root.configure(menu=menuBar) # 把menuBar加入到root中
def on_quit():
sys.exit()
file_menu = Menu(menuBar, tearoff=False) # 1.创建一个Menu对象
file_menu.add_command(label="Quit", command=on_quit) # 2. 给Menu对象添加
menuBar.add_cascade(label="File", menu=file_menu) # 3.把这个Menu对象加入到menuBar中
about_menu = Menu(menuBar, tearoff=False) # 1.创建一个Menu对象
about_menu.add_command(label="about info") # 2. 给Menu对象添加
menuBar.add_cascade(label="About", menu=about_menu) # 3.把这个Menu对象加入到menuBar中
# 右键上下文菜单
def pop(event):
global rightBar
rightBar.post(event.x_root, event.y_root)
rightBar = Menu(root)
rightBar.add_command(label="C++")
rightBar.add_command(label="Java")
rightBar.add_command(label="Python")
if sys.platform == 'darwin': # macOS右键是对应Button-2
root.bind('', pop)
else:
root.bind('', pop)
root.mainloop()
用例4: 展现tab效果的Notebook
from Tkinter import *
from ttk import *
root = Tk()
root.title('有的tab的窗口V1.0')
notebook = Notebook(root) # notebook属于ttk
frame1 = Frame(notebook)
label1 = Label(frame1, text="I love Beijing!!!\nI love China!!!")
label1.pack()
frame2 = Frame(notebook)
label2 = Label(frame2, text="I love Shanghai!!!\nI love China!!!")
label2.pack()
notebook.add(frame1, text="Beijing")
notebook.add(frame2, text="Shanghai")
notebook.pack()
root.mainloop()
用例5: 简单的登陆和聊天图形界面
# -*- coding:utf-8 -*-
"""
客户端雏形
Test ok
"""
from Tkinter import *
import time
root = Tk()
root.title('GameLobby Prototype')
manager = [] # widget管理器, 除了root以外所有widget应当被加入到这个list中进行管理
def login_check(event):
global root, e1, e2
username = e1.get()
password = e2.get()
if username == 'chen' and password == '123':
root.geometry('505x480') # 主动设置root根节点窗口大小
for item in manager:
item.destroy()
global frame1, b1
# frame的大小应当与root根节点的窗口大小相适应
frame1 = Frame(root, width=500, height=400, bd=2, relief='solid') # frame的width等单位是px
frame2 = Frame(root, width=500, height=400, relief='solid')
e1 = Entry(frame2, width=48, justify=RIGHT) # entry的width单位是一个字符, justify让打字出来的字从左/右边出现
b1 = Button(frame2, text="send")
b1.bind("", on_send)
b1.bind_class('Entry', '', on_send)
frame1.grid(row=0) # frame由于propagation的问题, 必须使用grid排版方式
frame2.grid(row=1)
e1.pack(side=LEFT, fill=X) # fill=X表示允许e1进行水平方向的填充
b1.pack(side=RIGHT)
frame1.grid_propagate(False) # 如果没有False掉propagate的话, frame会根据其内部装的元素size来自动调整它的size
manager.extend([frame1, e1, b1])
else:
l3['text'] = 'login failed'
l3.config(fg="red")
e2.delete(0, len(password))
def register(event): # 注意第一个参数是event
pass
def on_send(event):
global frame1
userinput = e1.get()
if len(userinput) == 0:
return None
prefix = unicode('我:', 'utf-8') + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
new_label1 = Label(frame1, text=prefix)
new_label1.config(fg="Green")
new_label2 = Label(frame1, text=userinput)
new_label1.grid(sticky=W)
new_label2.grid(sticky=W) # sticky=W表示label需要贴着西面(左侧)
e1.delete(0, len(userinput))
l1 = Label(root, text='账户:')
l1.grid(row=0, column=0)
e1 = Entry(root)
e1.grid(row=0, column=1)
l2 = Label(root, text='密码:')
l2.grid(row=1, column=0)
e2 = Entry(root, show="*") # 使得密码的输入是用*符号隐蔽的
e2.bind_class('Entry', '', login_check)
e2.grid(row=1, column=1)
b1 = Button(root, text="register")
b1.bind('', register) # bind实现键盘按键与响应函数的绑定
b1.grid(row=2, column=0)
b2 = Button(root, text="login")
b2.bind('', login_check)
b2.grid(row=2, column=1)
l3 = Label(root, text='')
l3.grid(row=3, column=0)
l4 = Label(root, text='(Press enter)')
l4.grid(row=3, column=1)
manager.extend([l1, l2, l3, l4, e1, e2, b1, b2]) # list_big.extend(list_small), 是一种快速添加多个元素的方便写法
root.mainloop()
这其中, 如果对于pack中的fill不太理解的话, 可以参考这个网页https://stackoverflow.com/questions/28089942/difference-between-fill-and-expand-options-for-tkinter-pack-method
但是这个方案是有问题的:
首先, 缺少一个滚动条, 因为如果当消息多的时候, 如果画面不可以滚动的话, 那后来的消息我们都看不到了.
其次, 当消息一行特别长的时候, label不会帮我们把消息自动换行. 因此用label来显示一条聊天消息的合理性也是存疑的.
为了解决所述的这两个问题, 我们需要引入更高级的Text组件和Scrollbar组件. 我们使用Text组件取代的简单的Label组件, 因为其能够实现根据宽度和高度自动换行, 而Scrollbar为我们提供了滚动的操作.
用例6: Scrollbar和Text
from Tkinter import *
root = Tk()
root.title('试验text和scrollbar')
global textarea
frame = Frame()
frame.pack()
textarea = Text(frame, bg='#CCFFCC', width=30, height=5) # 设置背景色和textarea size, 很关键
scrollbar = Scrollbar(frame, orient=VERTICAL)
scrollbar.config(command=textarea.yview)
scrollbar.pack(side=RIGHT, fill=Y) # 靠右摆放, fill整个纵向
textarea.config(yscrollcommand=scrollbar.set)
textarea.pack(side=LEFT, fill=BOTH, expand=1) # 靠左摆放, 左右的剩余空间都给textarea
textarea.config(state=NORMAL) # 开启允许编辑text
textarea.delete(1.0, END) # 删除所有之前的内容
item1 = item2 = item3 = "testing....\n这行的文字特别特别特别的长长长长长长长长长长长长长长" \
"\naaa\naaaaaaaaaaaaaaaaaaaaaaa\nadfdf\ndf"
textarea.insert(END, item1) # 插入到尾部, 就是append的意思
textarea.insert(END, item2)
textarea.insert(END, item3)
textarea.config(state=DISABLED) # 关闭编辑text
root.mainloop()
用例7: 改进后的登陆和聊天界面
# -*- coding:utf-8 -*-
"""
new prototype
"""
from Tkinter import *
import time
root = Tk()
root.title('GameLobby Prototype V0.5')
manager = [] # widget管理器, 除了root以外所有widget应当被加入到这个list中进行管理
def login_check(event):
global root, e1, e2
username = e1.get()
password = e2.get()
if username == 'chen' and password == '123':
root.geometry('505x370') # 主动设置root根节点窗口大小
for item in manager:
item.destroy()
global frame1, b1, textarea
# frame的大小应当与root根节点的窗口大小相适应
frame1 = Frame(root, bd=2, relief='solid') # frame的width等单位是px
frame2 = Frame(root)
textarea = Text(frame1, bg='#CCFFCC', width=68, height=20)
scrollbar = Scrollbar(frame1, orient=VERTICAL)
scrollbar.config(command = textarea.yview)
textarea.config(yscrollcommand=scrollbar.set)
scrollbar.pack(side=RIGHT, fill=Y)
textarea.pack(side=LEFT, expand=1, fill=BOTH)
textarea.config(state=DISABLED) # 关闭允许编辑text
e1 = Entry(frame2, width=48, justify=RIGHT) # entry的width单位是一个字符, justify让打字出来的字从左/右边出现
b1 = Button(frame2, text="send")
b1.bind("", on_send)
b1.bind_class('Entry', '', on_send)
frame1.grid(row=0) # frame由于propagation的问题, 必须使用grid排版方式
frame2.grid(row=1)
e1.pack(side=LEFT, fill=X) # fill=X表示允许e1进行水平方向的填充
b1.pack(side=RIGHT)
#frame1.grid_propagate(False)
manager.extend([frame1, frame2, e1, b1])
else:
l3['text'] = 'login failed'
l3.config(fg="red")
e2.delete(0, len(password))
def register(event): # 注意第一个参数是event
pass
def on_send(event):
global frame1, textarea
userinput = e1.get()
if len(userinput) == 0:
return None
prefix = unicode('我:', 'utf-8') + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
textarea.config(state=NORMAL)
textarea.insert(END, prefix+'\n')
textarea.insert(END, userinput+'\n')
textarea.config(state=DISABLED)
textarea.yview('moveto', '1.0') # 让滚动条自动往下走到最底下
e1.delete(0, len(userinput))
l1 = Label(root, text='账户:')
l1.grid(row=0, column=0)
e1 = Entry(root)
e1.grid(row=0, column=1)
l2 = Label(root, text='密码:')
l2.grid(row=1, column=0)
e2 = Entry(root, show="*") # 使得密码的输入是用*符号隐蔽的
e2.bind_class('Entry', '', login_check)
e2.grid(row=1, column=1)
b1 = Button(root, text="register")
b1.bind('', register) # bind实现键盘按键与响应函数的绑定
b1.grid(row=2, column=0)
b2 = Button(root, text="login")
b2.bind('', login_check)
b2.grid(row=2, column=1)
l3 = Label(root, text='')
l3.grid(row=3, column=0)
l4 = Label(root, text='(Press enter)')
l4.grid(row=3, column=1)
manager.extend([l1, l2, l3, l4, e1, e2, b1, b2]) # list_big.extend(list_small), 是一种快速添加多个元素的方便写法
root.mainloop()
参考资料
[Tkinter 教程10] Text 控件
http://blog.csdn.net/liuxu0703/article/details/60781513
2014年辛星tkinter教程第二版
下载地址百度搜索下即可