Tkinter快速入门用例

前言

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教程第二版
下载地址百度搜索下即可

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容