《python》-10:图形用户界面~tkinter入门

每一个不曾起舞的日子,都是对生命的辜负!

基于tkinter模块的GUI

GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter),从这个名字就可以看出它是基于Tk的,Tk是一个工具包,最初是为Tcl设计的,后来被移植到很多其他的脚本语言中,它提供了跨平台的GUI控件。当然Tk并不是最新和最好的选择,也没有功能特别强大的GUI控件,事实上,开发GUI应用并不是Python最擅长的工作,如果真的需要使用Python开发GUI应用,wxPythonPyQtPyGTK等模块都是不错的选择。
基本上使用tkinter来开发GUI应用需要以下5个步骤:

  1. 导入tkinter模块中我们需要的东西。
  2. 创建一个顶层窗口对象并用它来承载整个GUI应用。
  3. 在顶层窗口对象上添加GUI组件。
  4. 通过代码将这些GUI组件的功能组织起来。
  5. 进入主事件循环(main loop)。

学习资料:

tkinter简单介绍

Tkinter 是使用 python 进行窗口视窗设计的模块. 简单的构造, 多平台, 多系统的兼容性, 能让它成为让你快速入门定制窗口文件的好助手. 它在 python 窗口视窗模块中是一款简单型的. 所以用来入门, 熟悉 窗口视窗的使用, 非常有必要.

Label & Button部件

import tkinter as tk

window = tk.Tk()
window.title('窗口标题')
window.geometry('400x200')

# 定义变量并设置默认值
text = tk.StringVar(value='hello')

# 文本标签
label = tk.Label(
    window,
    textvariable=text,   # 使用 textvariable(变量) 替换 text(定值),
    bg='#ccc',           # 背景色
    font=('Arial', 12),  # 字体及大小
    width=15,            # 标签宽度
    height=2             # 标签高度
)

label.pack()


# 判断点击事件,切换文本
flag = True


# 定义点击事件
def click():
    global flag
    if flag:
        text.set('you click me!')
        flag = False
    else:
        text.set('hello world!')
        flag = True


# 按钮
button = tk.Button(
    window,
    text='click',
    bg='#f60',
    font=('Arial', 12),
    width=15,
    height=2,
    command=click   # 绑定点击事件
)
button.pack()

# 开启窗口
window.mainloop()

Label & Button部件

Entry & Text部件

import tkinter as tk

window = tk.Tk()
window.title('窗口标题')
window.geometry('400x200')

# 创建一个文本框用于输入,内容以“*”显示
e = tk.Entry(window, show='*')
e.pack()

# 定义点击事件
def insert_point():
    text = e.get()
    t.insert('insert', text)  # 插入到光标出


def insert_end():
    text = e.get()
    t.insert('end', text)   # 插入到最后


# 按钮
b1 = tk.Button(
    window,
    text='insert point',
    width=15,
    height=2,
    command=insert_point  # 绑定点击事件
)
b1.pack()

b2 = tk.Button(
    window,
    text='insert end',
    width=15,
    height=2,
    command=insert_end  # 绑定点击事件
)
b2.pack()

# 创建一个文本框用于显示、输入
t = tk.Text(window, height=2)
t.pack()

# 开启窗口
window.mainloop()

Entry & Text部件

Listbox部件

import tkinter as tk

root = tk.Tk()

root.title('list box')
root.geometry('400x200')

# 创建一个label用于显示
labelvar = tk.StringVar(value='人生苦短,我用Python')
l = tk.Label(root, bg='green', textvariable=labelvar)
l.pack()


# 定义点击事件
def print_selection():
    # 获取当前选中的文本
    curselection = lb.get(lb.curselection())
    # 显示当前选中的文本
    labelvar.set(curselection)


# 按钮绑定事件
b = tk.Button(root, text='print selection', command=print_selection)
b.pack()

listvar = tk.StringVar(value=['list box', 'hello world', '人生苦短,我用Python'])
# list box选择框
lb = tk.Listbox(root, listvariable=listvar)
lb.pack()

if __name__ == '__main__':
    root.mainloop()

Listbox部件

Radiobutton部件

import tkinter as tk

root = tk.Tk()

root.title('list box')
root.geometry('400x200')

l = tk.Label(root, bg='yellow', width=20, text='empty')
l.pack()


def print_selection():
    l.config(text='you have selected ' + radiovar.get())


item = [('option A', 'A'), ('option B', 'B'), ('option C', 'C'),
        ('option D', 'D'), ('option E', 'E')]
radiovar = tk.StringVar()
for text, value in item:
    tk.Radiobutton(
        root,
        text=text,
        value=value,
        variable=radiovar,
        command=print_selection
    ).pack()

radiovar.set('B')

if __name__ == '__main__':
    root.mainloop()

Radiobutton部件

Scale部件

s = tk.Scale(window, 
              label='try me', 
              from_=5, 
              to=11, 
              orient=tk.HORIZONTAL,
              length=200, 
              showvalue=0, 
              tickinterval=2, 
              resolution=0.01, 
              command=print_selection
)
s.pack()

这里的参数label是指scale部件的名称,即在这里scale部件名称为try me

  • 参数from_=5,to=11的意思就是从5到11,即这个滚动条最小值为5,最大值为11(这里使用from_是因为在python中有from这个关键词
    参数orient=tk.HORIZONTAL在这里就是设置滚动条的方向,如我们所看到的效果图,这里HORIZONTAL就是横向
  • 参数length这里是指滚动条部件的长度,但注意的是和其他部件width表示不同,width表示的是以字符为单位,比如width=4,就是4个字符的长度,而此处的length=200,是指我们常用的像素为单位,即长度为200个像素
  • 参数resolution=0.01这里表示保留几位小数,此处的0.01就是保留2位小数,如果保留一位就是resolution=0.1,这里的showvalue就是设置在滚动条上方的显示。
  • 参数tickinterval设置的就是坐标的间隔,此处为tickinterval=2,显示的即为效果图中的5.00 7.00 9.00 11.00,如果改为tickinterval=3则为5.00 8.00 11.00
import tkinter as tk

root = tk.Tk()
root.title('Scale')
root.geometry('400x200')

l = tk.Label(root, bg='green', text='hello')
l.pack()

def scale(v):
    l.config(text='you have drag me to ' + v)
s=tk.Scale(
    root,
    label='drag me',
    from_=1,
    to=15,
    orient=tk.HORIZONTAL,
    length=400,
    showvalue=1,
    tickinterval=2,
    resolution=0.01,
    command=scale
).pack()
if __name__ == '__main__':
    root.mainloop()

Scale部件

Checkbutton部件

var1 = tk.IntVar()
c1 = tk.Checkbutton(window, text='Python', variable=var1, onvalue=1, offvalue=0,
                    command=print_selection)
c1.pack()

参数onvalue和前面讲的部件radiobutton中的value相似, 当我们选中了这个checkbutton,onvalue的值1就会放入到var1中, 然后var1将其赋值给参数variableoffvalue用法相似,但是offvalue是在没有选中这个checkbutton时,offvalue的值1放入var1,然后赋值给参数variable 这是创建一个checkbutton部件,以此类推,可以创建多个checkbutton

import tkinter as tk

root = tk.Tk()
root.title('my window')
root.geometry('200x200')

l = tk.Label(root, bg='yellow', width=20, text='empty')
l.pack()


def print_selection():
    if (var1.get() == 1) & (var2.get() == 0):
        l.config(text='I love only Python ')
    elif (var1.get() == 0) & (var2.get() == 1):
        l.config(text='I love only C++')
    elif (var1.get() == 0) & (var2.get() == 0):
        l.config(text='I do not love either')
    else:
        l.config(text='I love both')


var1 = tk.IntVar()
var2 = tk.IntVar()
c1 = tk.Checkbutton(root, text='Python', variable=var1, onvalue=1, offvalue=0,
                    command=print_selection)
c2 = tk.Checkbutton(root, text='C++', variable=var2, onvalue=1, offvalue=0,
                    command=print_selection)
c1.pack()
c2.pack()
if __name__ == '__main__':
    root.mainloop()

Checkbutton部件

Canvas部件

canvas = tk.Canvas(window, bg='blue', height=100, width=200)
canvas.pack()
image_file = tk.PhotoImage(file='ins.gif')
image = canvas.create_image(10, 10, anchor='nw', image=image_file)

这里的代码主要是实现小图片。 image_file = tk.PhotoImage(file='ins.gif')这一句是创造一个变量存放ins.gif这张图片。 image = canvas.create_image(10, 10, anchor='nw', image=image_file)里面的参数10,10就是图片放入画布的坐标, 而这里的anchor=nw则是把图片的左上角作为锚定点,在加上刚刚给的坐标位置,即可将图片位置确定。 最后一个参数的意思大家应该都知道,就是将刚刚存入的图片变量,赋值给image。

x0, y0, x1, y1= 50, 50, 80, 80
line = canvas.create_line(x0, y0, x1, y1)

这段代码主要实现的是画一条直线,后面()中给的参数就是线段两点的坐标,两点确定一条直线。此处给的就是从坐标(50,50)(80,80)画一条直线。

oval = canvas.create_oval(x0, y0, x1, y1, fill='red')  #创建一个圆,填充色为`red`红色
arc = canvas.create_arc(x0+30, y0+30, x1+30, y1+30, start=0, extent=180)  #创建一个扇形
rect = canvas.create_rectangle(100, 30, 100+20, 30+20)   #创建一个矩形

这里面就是创建扇形时多了两个没见过的参数start=0extent=180,其实就是从0度180度,就好像扇子的边打开一样。在我们看来就是个半圆, 如果改为extent=90,我们看到的就是一个1/4圆

import tkinter as tk

window = tk.Tk()
window.title('my window')
window.geometry('200x200')

canvas = tk.Canvas(window, bg='blue', height=100, width=200)
image_file = tk.PhotoImage(file='ins.gif')
image = canvas.create_image(10, 10, anchor='nw', image=image_file)
x0, y0, x1, y1 = 50, 50, 80, 80
line = canvas.create_line(x0, y0, x1, y1)
oval = canvas.create_oval(x0, y0, x1, y1, fill='red')
arc = canvas.create_arc(x0 + 30, y0 + 30, x1 + 30, y1 + 30, start=0, extent=180)
rect = canvas.create_rectangle(100, 30, 100 + 20, 30 + 20)
canvas.pack()


def moveit():
    canvas.move(rect, 0, 2)


b = tk.Button(window, text='move', command=moveit).pack()

if __name__ == '__main__':
    window.mainloop()

Canvas部件

Menu部件

下面是我们制作整个菜单栏的流程, 我们先需要加入一个 Menubar 作为整体框架, 然后再在 Menubar 中加一些部件。

  1. 创建一个菜单栏,这里我们可以把他理解成一个容器,在窗口的上方

    menubar = tk.Menu(window)
    
  2. 定义一个空菜单单元

    filemenu = tk.Menu(menubar, tearoff=0)
    
  3. 将上面定义的空菜单命名为File,放在菜单栏中,就是装入那个容器中

    menubar.add_cascade(label='File', menu=filemenu)
    
  4. File中加入New的小菜单,即我们平时看到的下拉菜单,每一个小菜单对应命令操作。如果点击这些单元, 就会触发do_job的功能

    filemenu.add_command(label='New', command=do_job)
    filemenu.add_command(label='Open', command=do_job)##同样的在`File`中加入`Open`小菜单
    filemenu.add_command(label='Save', command=do_job)##同样的在`File`中加入`Save`小菜单
    
    filemenu.add_separator()##这里就是一条分割线
    
    
  5. 同样的在File中加入Exit小菜单,此处对应命令为window.quit

    filemenu.add_command(label='Exit', command=window.quit)
    
    

同样的我们在定义另一个菜单Edit也是如此和定义的File菜单一样 这里再来看一下效果中比较不一样的菜单就是File中的Import菜单, 在这个菜单选项中, 我们还能分支出更多的选项.

submenu = tk.Menu(filemenu) ##和上面定义菜单一样,不过此处是在`File`上创建一个空的菜单
filemenu.add_cascade(label='Import', menu=submenu, underline=0) ##给放入的菜单`submenu`命名为`Import`
submenu.add_command(label="Submenu1", command=do_job) ##这里和上面也一样,在`Import`中加入一个小菜单命令`Submenu1`
import tkinter as tk

window = tk.Tk()
window.title('my window')
window.geometry('200x200')

l = tk.Label(window, text='', bg='yellow')
l.pack()
counter = 0


def do_job():
    global counter
    l.config(text='do ' + str(counter))
    counter += 1


menubar = tk.Menu(window)
filemenu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='File', menu=filemenu)
filemenu.add_command(label='New', command=do_job)
filemenu.add_command(label='Open', command=do_job)
filemenu.add_command(label='Save', command=do_job)
filemenu.add_separator()
filemenu.add_command(label='Exit', command=window.quit)

editmenu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='Edit', menu=editmenu)
editmenu.add_command(label='Cut', command=do_job)
editmenu.add_command(label='Copy', command=do_job)
editmenu.add_command(label='Paste', command=do_job)

submenu = tk.Menu(filemenu)
filemenu.add_cascade(label='Import', menu=submenu, underline=0)
submenu.add_command(label="Submenu1", command=do_job)

window.config(menu=menubar)

if __name__ == '__main__':
    window.mainloop()

Menu部件

Frame部件

Frame 是一个在 Windows 上分离小区域的部件, 它能将 Windows 分成不同的区,然后存放不同的其他部件. 同时一个 Frame 上也能再分成两个 Frame, Frame 可以认为是一种容器。如果你学过前端,那么自然可以想到这个部件类似于<frame>标签

  1. 定义一个label显示on the window
    tk.Label(window, text='on the window').pack()
    
  2. window上创建一个frame
    frm = tk.Frame(window)
    frm.pack()
    
  3. 在刚刚创建的frame上创建两个frame,我们可以把它理解成一个大容器里套了一个小容器,即frm上有两个framefrm_lfrm_r
    frm_l = tk.Frame(frm)
    frm_r = tk.Frame(frm)
    
  4. 这里是控制小的frm部件在大的frm的相对位置,此处frm_l就是在frm的左边,frm_rfrm的右边
    frm_l.pack(side='left')
    frm_r.pack(side='right')
    
  5. 这里的三个label就是在我们创建的frame上定义的label部件,还是以容器理解,就是容器上贴了标签,来指明这个是什么,解释这个容器。
    tk.Label(frm_l, text='on the frm_l1').pack()##这个`label`长在`frm_l`上,显示为`on the frm_l1`
    tk.Label(frm_l, text='on the frm_l2').pack()##这个`label`长在`frm_l`上,显示为`on the frm_l2`
    tk.Label(frm_r, text='on the frm_r1').pack()##这个`label`长在`frm_r`上,显示为`on the frm_r1`
    
import tkinter as tk

root = tk.Tk()
root.geometry('400x200')

# 定义一个label显示
tk.Label(root, text='人生苦短,我用Python!').pack()

# 创建一个'frame'
frm = tk.Frame(root)
frm.pack()

# 在`frm`中再创建两个'frame',即大容器包小容器
frm_left = tk.Frame(frm)
frm_right = tk.Frame(frm)

# 设置小容器的位置
frm_left.pack(side='left')
frm_right.pack(side='right')

# 显示的内容
tk.Label(frm_left, text='人生苦短').pack()
tk.Label(frm_right, text='hello world').pack()
tk.Label(frm_left, text='我用Python').pack()

if __name__ == '__main__':
    root.mainloop()

Frame部件

Messagebox部件

其实这里的messagebox就是我们平时看到的弹窗。 我们首先需要定义一个触发功能,来触发这个弹窗 这里我们就放上以前学过的button按钮

tk.Button(window, text='hit me', command=hit_me).pack()

通过触发功能,调用messagebox

def hit_me():
   tk.messagebox.showinfo(title='Hi', message='hello world')

对话框类型:

tk.messagebox.showinfo(title='',message='') #提示信息对话窗
tk.messagebox.showwarning() #提出警告对话窗
tk.messagebox.showerror()   #提出错误对话窗
tk.messagebox.askquestion() #询问选择对话窗
import tkinter as tk
from tkinter import messagebox

root = tk.Tk()

root.geometry('400x200')


def click():
    tk.messagebox.showinfo(title='Hello World!', message='人生苦短,我用Python!')


tk.Button(root, text='Say Hi', command=click).pack()

if __name__ == '__main__':
    root.mainloop()
    print(tk.messagebox.askquestion())  # 返回yes和no
    print(tk.messagebox.askokcancel())  # 返回true和false
    print(tk.messagebox.askyesno())  # 返回true和false
    print(tk.messagebox.askretrycancel())  # 返回true和false
Messagebox部件

pack、grid、place定位部件

pack

首先我们先看看我们常用的.pack(), 他会按照上下左右的方式排列.

tk.Label(root, text='1').pack(side='top')     #上
tk.Label(root, text='1').pack(side='bottom')  #下
tk.Label(root, text='1').pack(side='left')    #左
tk.Label(root, text='1').pack(side='right')   #右

grid

接下里我们在看看.grid(), grid 是方格, 所以所有的内容会被放在这些规律的方格中.

for i in range(4):
    for j in range(3):
        tk.Label(root, text=1).grid(row=i, column=j, padx=10, pady=10)

以上的代码就是创建一个四行三列的表格,其实grid就是用表格的形式定位的。这里的参数 row为行colum为列padx就是单元格左右间距pady就是单元格上下间距

place

再接下来就是place(), 这个比较容易理解,就是给精确的坐标来定位,如此处给的(20,10),就是将这个部件放在坐标为(x,y)的这个位置 后面的参数anchor=nw就是前面所讲的锚定点是西北角

tk.Label(window, text=1).place(x=20, y=10, anchor='nw')

exercise python 100 days from https://github.com/jackfrued/Python-100-Days

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

推荐阅读更多精彩内容