Tkinter快速入门用例

字数 541阅读 2255

前言

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来循环地刷新图形画面
用例1, 按按钮在窗口中添加新的label

用例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', '<Return>', login_check)
e2.grid(row=1, column=1)

b1 = Button(root, text="login")
b1.bind('<Button-1>', login_check)
b1.grid(row=2, column=1)

b2 = Button(root, text="register")
b2.bind('<Button-1>', 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()

登录框的制作, 运用了grid排版

用例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('<Button-2>', pop)
else:
    root.bind('<Button-3>', 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()
image.png

用例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("<Button-1>", on_send)
        b1.bind_class('Entry', '<Return>', 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', '<Return>', login_check)
e2.grid(row=1, column=1)

b1 = Button(root, text="register")
b1.bind('<Button-1>', register)  # bind实现键盘按键与响应函数的绑定
b1.grid(row=2, column=0)

b2 = Button(root, text="login")
b2.bind('<Button-1>', 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

image.png

但是这个方案是有问题的:
首先, 缺少一个滚动条, 因为如果当消息多的时候, 如果画面不可以滚动的话, 那后来的消息我们都看不到了.
其次, 当消息一行特别长的时候, 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()
Text和Scrollbar的运用

用例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("<Button-1>", on_send)
        b1.bind_class('Entry', '<Return>', 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', '<Return>', login_check)
e2.grid(row=1, column=1)

b1 = Button(root, text="register")
b1.bind('<Button-1>', register)  # bind实现键盘按键与响应函数的绑定
b1.grid(row=2, column=0)

b2 = Button(root, text="login")
b2.bind('<Button-1>', 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教程第二版
下载地址百度搜索下即可