tkinter 基础教程

介绍

Tkinter 模块(Tk 接口)是 Python 的标准 Tk GUI 工具包的接口 .Tk 和 Tkinter 可以在大多数的 Unix 平台下使用,同样可以应用在 Windows 和 Macintosh 系统里。Tk8.0 的后续版本可以实现本地窗口风格,并良好地运行在绝大多数平台中。

Tkinter 是 Python 的标准 GUI 库。Python 使用 Tkinter 可以快速的创建 GUI 应用程序。

由于 Tkinter 是内置到 python 的安装包中、只要安装好 Python 之后就能 import Tkinter 库、而且 IDLE 也是用 Tkinter 编写而成、对于简单的图形界面 Tkinter 还是能应付自如。

注意:Python3.x 版本使用的库名为 tkinter,即首写字母 T小写

模块

导入方式

import tkinter

验证模块是否已安装

终端中运行命令:python -m tkinter ,运行后界面会弹出一个 tk 小窗口,表示安装成功,而且告诉你 Tcl/Tk 的版本号,通过这个版本号,你就可以参考对应的 Tcl/Tk 文档了。

API 使用

主窗口

创建 GUI 界面,作为根窗口:

# 创建主窗口
root = tk.Tk()
root.title("tk Root")  # 设置窗口名称
root.geometry('200x200')  # 设置窗口大小

运行窗口

# 运行
root.mainloop()

组件列表介绍

名称 简介
Toplevel 顶层 容器类,为其他组件提供单独容器
Button 按钮 按钮组件
Canvas 画布 提供绘图功能,包括:直线,矩形,椭圆,多边形,位图等
Checkbutton 复选框 可选的复选框
Entry 单行输入框 可输入内容
Frame 容器 装载其它GUI组件
Label 标签 显示不可编辑的文本或图标
LabelFrame 容器 容器组件,类似Frame支持添加标题
Listbox 列表框 列出多个选项,供选择
Menu 菜单 菜单组件
Menubutton 菜单按钮 包含菜单的按钮(下拉式,层叠式)
OptionMeny 菜单按钮 Menubutton的子类,可通过按钮打开一个菜单
Message 消息框 类似标签,但可以显示多行文本
PanedWindow 分区窗口 该容器会被划分成多个区域,每添加一个组件占一个区域,用户可以拖动分割线改变各区域大小
Radiobutton 单选钮 供用户点边的单选妞
Scale 滑动条 拖动滑动块可设定起始值和结束值,可显示当前位置的精确值
Spinbox 微调选择器 可通过该组件的向上、向下箭头选择不同的值
Scrollbar 滚动条 为组件提供滚动功能
Text 多行文本框 显示多行文本

Label 标签

属性说明:

width, height=宽高 font(name,size)=字体 image=图像(只支持GIF格式)

fg(foreground)=前景色 bg(background)=背景色 justify(left|center|right)=对齐方式

# 设置 label 属性
L = tk.Label(root,               # 窗口
             text='展示标签!',   # 描述
             bg='green',         # 背景
             borderwidth=5,   # 边界宽度
             relief="solid",  # 指定组件 3D 效果
             justify="right",  # 字体对齐方式
             font=('微软雅黑', 12),  # 字体
             width=15, height=2)  # 宽高
# 放置 label 控件在窗口中的位置
L.pack(side='right')    # 上top下bottom 左left右right
# 配置GIF图片方式
gifpath = r"D:\xxx\www.gif"
photo = tk.PhotoImage(file=gifpath)  # 实例化图片对象
lab = tk.Label(image=photo)          # 设置图片标签
lab.pack()

Button 按钮

# 定义空字符串变量,用于标签属性中接收数据
var = tk.StringVar()
# 设置 label 标签属性
L = tk.Label(root,               # 窗口
             # text='展示标签!',   # 描述
             textvariable=var,   # 接收外部传参
             bg='green',         # 背景
             font=('微软雅黑', 12),  # 字体
             width=15, height=2)  # 宽高
# 放置 label 位置
L.pack()    # 上下 左left右right

on_func = False  # 定义一个开关
# 按钮事件
def func():
    global on_func
    if on_func:
        on_func = False
        var.set('')
    else:
        on_func = True
        var.set('要显示内容')
# 设置 button 按钮属性
B = tk.Button(root,  text='按钮',  width=15, height=2,
              command=func)        # 设置按钮事件,函数
B.pack()

Options 属性选项

组件中的属性,可以通过 Options 设置组件对应的属性,从而控制组件的各种状态。比如:宽度、高度、背景色等等。

有三种方式设置 Options 选项,在各种 GUI 组件中用法都一致。

  1. 创建对象时,使用命名参数(也叫关键字参数):
fred = Button(width=15, height=2, bg="blue")
  1. 创建对象后,使用字典索引方式:
fred['fg'] = "red"
fred['bg'] = "blue"
  1. 创建对象后,使用 config() 方式:
fred.config(width=15, height=2, bg="blue")
  • 属性选项列表:
属性名 简介
activebackground 指定组件处于激活状态时的背景色
activeforeground 指定组件处于激活状态时的前景色
disabledforeground 指定组件处于禁用状态时的前景色
anchor 指定组件内的字符在组件中显示位置,选项值(N,NE,E,SE,S,SW,W,NW,CENTER)
background 组件正常的背景色(bg)
foreground 组件正常的前景色(fg)
bitmap 指定在组件上显示该选项指定的位图,位图的显示方式受anchor、justify影响。如果同时指定bitmap和text,那么bitmap覆盖文本;如果同时指定bitmap和image,那么image覆盖bitmap。
relief 组件的3D效果,支持: raised(凸起),sunken(凹陷),flat(平坦),ridge(隆起),solid(实心),groove(凹槽)
borderwidth 指定组件正常显示的3D边框宽度
cursor 指定光标在组件上的样式
command 指定组件关联的命令方法。该方法在组件离开组件时触发
font 组件上显示的文字字体
highlightbackground 指定组件在高亮状态下的背景色
highlightcolor 指定组件在高亮状态下的前景色
highlightthickness 指定组件在高亮状态下的周围方形区域的宽度
state 组件的当前状态,支持:NORMAL(正常),DISABLE(禁用)
height 组件的高度
width 组件的宽度
wraplength 对于能支持字符换行的组件,该选项指定每行显示的最大字符数,超过设定自动换行
image 组件中显示的图片
justify 组件内部内容对齐方式,支持(小写):LEFT(左对齐),CENTER(居中),RIGHT(右对齐)
padx 组件内部在水平方向上两边的空白
pady 组件内部在垂直方向上两边的空白
takefocus 指定组件在键盘遍历(Tab或Shift+Tab)时是否接收焦点,将选项设为1:接收焦点;设为0:不接受焦点
text 组件显示的文本
textvariable 指定一个变量名,组件负责显示该变量值转换得到的字符串
underline 指定组件文本的第几个字符添加下划线,该选项相当于为组件绑定了快捷键
xscrollcommand 通常用于将组件的水平滚动改变与水平滚动条的 set 方法关联,从而让组件的水平滚动改变传递到水平滚动条
yscrollcommand 通常用于将组件的垂直滚动改变与垂直滚动条的 set 方法关联,从而让组件的垂直滚动改变传递到垂直滚动条

文本框

Entry 单行文本框

Entry 用来接收一行字符串的控件,如果用户输入的内容长度超过 Entry 控件的宽度时,文字会自动换行。

简单示例:

def login():
    print('名称:'+entry1.get())  # 接收文本框的内容
labe = tk.Label(root, text="名称:")
labe.pack()
 
v1 = tk.StringVar() # 设置空字符串变量,属性value='可设置默认值'
# 单行文本框
# show属性可将输入的内容转为指定字符方式显示,比如输入密码
entry1 = tk.Entry(root, textvariable=v1, show='*')
entry1.pack()
v1.set('设置初始值...')  # 方式二:单独设置默认值
tk.Button(root, text='发送文本中内容', command=login).pack()  

Text 多行文本框

主要用来显示多行文本,还可以显示网页链接、图片、HTML、CSS样式表、添加组件等等。

wrap=换行方式,word:单词换行

undo,True:开启撤销功能, False:不开启

示例:

''' Text 多行文本框  '''
tk.Label(root, text='文本框:', width=30).pack(anchor='w')
# 创建多行文本
tex = tk.Text(root, width=40, height=12)
tex.pack()
  • insert(index, 'str') 插入属性

index:位置,可数字表示(行从1起,列从0起),或INSERT(开始)、END(结尾)

str:需要插入的内容

规则:不包头,包尾

tex.insert('insert', 'ABCD\nEFG') # 在第一行第0列位置插入内容
tex.insert(2.2, '12345')  # 在第二行第3列位置插入内容
'''结果:
ABCD
EF12345G
'''
  • get(insert, end) 获取内容属性

insert:起始位置,int类型,用1.0表示(行从1起,列从0起);

end:结尾,str类型或 int类型,用1.0表示(行从1起,列从0起);

tex.get(3.2, 3.7)  # 指定范围内
tex.get(3.2, 'end')  # 指定起始位置至结尾
  • 在文本框中放入图片:
gifpath1 = r"D:\xxxx\www.gif"
photo1 = tk.PhotoImage(file=gifpath1)
tex.image_create('end', image=photo1)  # 在结尾放入图片

文本框属性:

background, bd, bg, borderwidth, cursor,exportselection, fg, font, foreground, ighlightbackground,

highlightcolor, highlightthickness, insertbackground,insertborderwidth, insertofftime, insertontime,

insertwidth,invalidcommand, invcmd, justify, relief, selectbackground,selectborderwidth,

electforeground, show, state, takefocus,textvariable, validate, validatecommand, vcmd, width,xscrollcommand

选择框

Radiobutton 单选框

value: 定义单选按钮返回的内容

''' 单选按钮 '''
def confirm():
    messagebox.showinfo('测试', '选择的内容:' + var.get())
var = tk.StringVar()
var.set("J")
tk.Label(root, text='单选: ', width=10).pack(anchor='w')
radio1 = tk.Radiobutton(root, text='Python', value='P', variable=var)
radio2 = tk.Radiobutton(root, text='Java', value='J', variable=var)
radio1.pack(anchor='w');  radio2.pack(anchor='w')
tk.Button(root, text='确认', command=confirm).pack()

Checkbutton 多选框

onvalue=1  # 1 表示默认选状态,0 表示不选
offvalue=0 # 未选择的返回值
def check():
    if code.get() == 1 and vide.get() == 1:
        messagebox.showinfo('多选', '选择:python, java')
    elif vide.get() == 1:
        messagebox.showinfo('多选', '选择:java')
    elif code.get() == 1:
        messagebox.showinfo('多选', '选择:python')
    else:
        messagebox.showinfo('多选', '都没选择!')
tk.Label(root, text='多选:', width=10).pack(anchor='w')
code = tk.IntVar();  vide = tk.IntVar()   # 接收int类型参数
check1 = tk.Checkbutton(root, text='python', onvalue=1, offvalue=0, variable=code)
check2 = tk.Checkbutton(root, text='Java', onvalue=1, offvalue=0, variable=vide)
check1.pack(anchor='w'); check2.pack(anchor='w')
tk.Button(root, text='多选确认', command=check).pack()

选择框属性:

activebackground, activeforeground, anchor,background, bd, bg, bitmap, borderwidth,

command, cursor,disabledforeground, fg, font, foreground, height,highlightbackground,

highlightcolor, highlightthickness, image,indicatoron, justify, offvalue, onvalue, padx, pady,

relief,selectcolor, selectimage, state, takefocus, text, textvariable,underline, variable, width, wraplength

通用消息弹窗

messagebox 消息弹框

Messagebox 用于显示弹出消息。Messagebox主要提供了7种消息提示,如:showinfo()、showerror()、showwarning()、askquestion()、askokcancel()、askyesno()、askretyrcancel()。

导入方式:

from tkinter import messagebox

消息对话框函数列表:

返回值:选择项

函数 说明
showinfo(title, message, **options) 消息提示框
showerror(title, message, **options) 错误提示框
showwarning(title, message, **options) 警告提示框
askquestion(title, message, **options) 疑问提示框
askokcancel(title, message, **options) 确定/取消
askyesno(title, message, **options) yes/No
askretrycancel(title, message, **options) 重试/取消

简单示例:

import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
root.title("TK title")
root.geometry("500x1000+100+200")
def msg1():
    messagebox.showinfo('information', 'Hi! You got a prompt.')
    messagebox.showerror('error', 'Something went wrong!')
    messagebox.showwarning('warning', 'accept T&C')
    messagebox.askquestion('Ask Question', 'Do you want to continue?')
    messagebox.askokcancel('Ok Cancel', 'Are You sure?')
    messagebox.askyesno('Yes|No', 'Do you want to proceed?')
    messagebox.askretrycancel('retry', 'Failed! want to try again?')
tk.Button(root, text='Click Me', command=msg1).pack(pady=50)

canvas 画布

canvas 画布是一个矩形区域,可以放置图形、图像、组件等等。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        # 代表父类的定义,不是父类对象
        super().__init__(master)
        self.master = master
        self.pack()
        self.createWidget()
    def createWidget(self):
        self.canvas = tk.Canvas(self, width=300, height=200, bg='green')
        self.canvas.pack()
        # 画一条直线
        self.canvas.create_line(10,10,30,20,40,50)
        # 画一个矩形
        self.canvas.create_rectangle(50,50,100,100)
        # 画一个圆
        self.canvas.create_oval(50,50,100,100)
        # 加图片
        path = "D:\wangxin_m\selenium_py36\gui_tkinter\www.gif"
        global photo
        photo = tk.PhotoImage(file=path)
        self.canvas.create_image(110,300, image=photo)
if __name__ == '__main__':
    root = tk.Tk()
    root.title('画布练习')
    root.geometry('500x200+200+200')
    app = Application(master=root)
    root.mainloop()

布局管理

一个 GUI 应用程序必然有大量的组件,tkinter提供了布局管理器帮助我们组织、管理在父组件中子组件的布局方式。提供了三种管理器:pack、grid、place

grid 表格布局

采用表格结构组织组件;子组件的位置由行和列的单元格来确定,并且可以跨行和跨列,从而实现复杂的布局。

grid()方法提供的选项:

选项 说明
column 单元格的列号(从0开始)
columnspan 跨列
row 单元格的行号(从0开始)
rowspan 跨行
ipadx,ipady 设置子组件之间的间隔
padx,pady 并列的组件之间的间隔
sticky 组件紧贴所在单元格的某一角,对应对应东南西北及4个角
import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        # 代表父类的定义,不是父类对象
        super().__init__(master)
        self.master = master
        self.pack()
        self.gridWidget()    
    def gridWidget(self):
        self.labY = tk.Label(self, text='用户名')
        self.labP = tk.Label(self, text='iPhone')
        self.enrY = tk.Entry(self)
        self.enrP = tk.Entry(self)
        # 设置控件位置
        self.labY.grid(row=0, column=0) # row=0, column=0 表示:第1行第1列位置 
        self.enrY.grid(row=0, column=1)
        self.labP.grid(row=1, column=0)
        self.enrP.grid(row=1, column=1)
        tk.Button(self, text='登录').grid(row=2, column=0, sticky='w')
        tk.Button(self, text='取消').grid(row=2, column=1, sticky='e')
        # 跨行,多行文本框
        self.text = tk.Text(self, width=28, height=3)
        self.text.grid(row=3, column=0, columnspan=2)
if __name__ == '__main__':
    root = tk.Tk()
    root.title('画布练习')
    app = Application(master=root)
    root.mainloop()

计算器布局示例:

class Application(tk.Frame):
    def __init__(self, master=None):
        # 代表父类的定义,不是父类对象
        super().__init__(master)
        self.master = master
        self.pack()
        self.jsqWidget()    
    def jsqWidget(self):
        btnText = (("MC", "M+", "M-", "MR"),  ("C", "±", "/", "*"),
            (7,8,9,"-"),(4,5,6,"+"),(1,2,3,"="), (0,  ".")     )
        # 显示框,第一行
        tk.Entry(self).grid(row=0, column=0, columnspan=4, pady=10)
        # 按钮控件布局
        for rindx, r in enumerate(btnText):
            for cindx, c in enumerate(r):
                if c == "=":
                    but_d = tk.Button(self, text=c, width=3)
                    but_d.grid(row=rindx+1, column=cindx, rowspan=2, sticky='nsWE')
                elif c == 0:
                    but_0 = tk.Button(self, text=c, width=3)
                    but_0.grid(row=rindx+1, column=cindx, columnspan=2, sticky='nsWE')
                elif c == '.':
                    but_d = tk.Button(self, text=c, width=3)
                    but_d.grid(row=rindx+1, column=cindx+1, sticky='nsWE')
                else:
                    tk.Button(self, text=c, width=3).grid(row=rindx+1, column=cindx, sticky='nsWE')

pack 布局

按照组件的创建顺序将子组件添加到父组件中,按照“垂直”或者“水平”的方向自然排序。默认在父组件中自顶向下垂直添加组件。

pack()方法提供选项:

选项 说明
expand 当值为"YES"时,side选项无效,组件显示在父组件中心位置,若fill选项为"both",则填充父组件的剩余空间;'yes'或自然数、'no'或0(默认)
fill 填充x或y方向的空间,x=水平,y=垂直,both=两边填充;当属性side='top'或'bottom'时,填充x方向;当属性side='left'或'right'时,填充y方向;当expand选项为'yes'时,填充父组件的剩余空间
ipadx,ipady 设置子组件之间的间隔
padx,pady 与之并列的组件之间的间隔
side 定义停靠父组件的那个位置(left'、'right'、'top'、'bottom')
before 将本组件于所选组件对象之前pack,类似于先创建本组件选定的组件
after 将本组件与选择组件对象之后pack,类似先创建选定组件后再创建本组件
anchor 对齐方式(n,s,w,e,nw,sw,se,ne,center)
in_ 将本组件所选组件对象的子组件,类似于指定本组件的masster为选定组件

示例:

root = tk.Tk()
root.title('画布练习')
root.geometry("700x220")
# Frame 画布区域,用于放置子组件
f1 = tk.Frame(root, bg='blue');  f1.pack()
f2 = tk.Frame(root,  bg='yellow');  f2.pack()
btnText = ["A", "B", "C", "D"]
for t in btnText:
    tk.Button(f1, text=t).pack(side='left', padx="10")
for i in range(1, 10):
    tk.Label(f2, text=i, width=2, height=3, borderwidth=1, relief="raised",
bg="red").pack(side="left", padx=4)
root.mainloop()

place 布局

通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景。

place()方法选项:

选项 说明 取值范围
x,y 组件左上角的绝对坐标(相对于窗口) 非负整数,x和y选项用于设置偏移(像素),如果同时设置relx(rely) 和x(y),那么place将优先计算relx和rely,然后在实现x和y指定的偏移值
relx,rely 组件左上角的坐标(相对于父窗口) 相对父组件位置,0为最左边,0.5为中间,1位最右边
width,height 组件的宽度和高度 正整数
relwidth,relheight 组件的宽度和高度(相对于父窗口) 与relx,rely类似,但是相对于父组件的尺寸
anchor 对齐方式 n,s,w,e,nw,sw,se,ne,center

示例:

root = tk.Tk()
root.title('画布练习')
root.geometry("500x300")
# Frame 画布区域,用于放置子组件
f1 = tk.Frame(root, width=200, height=200, bg='blue');  f1.place(x=20, y=20)
tk.Button(root, text='root位置').place(relx=0.1, relwidth=0.3, relheight=0.2)
tk.Button(f1, text='f1位置').place(relx=0.2, rely=0.5)
root.mainloop()

事件处理

一个 GUI 应用整个生命周期都处在一个消息循环(eventloop)中。它等待事件的发生,并作出相应的处理。

tkinter 提供了用以处理相关事件的机制,处理函数可被绑定给各个空间的各个事件。

widget.bind(event,handler) 如果相关事件发生, handler 函数会触发,事件对象 event 会传递给 handler 函数。

event 代表事件

handler 代表事件要处理的方式(函数)

鼠标和键盘事件

代码 说明
<Button-1> 或 <ButtonPress-1>或<1> 鼠标左键按下。1=左键,2=中键,3=右键
<ButtonRelease-1> 鼠标左键释放。 1=左键,2=中键,3=右键
<B1-Motion> 鼠标左键移动
<Double-Button-1> 双击左键
<Enter> 鼠标指针进入某一组件区域
<Leane> 鼠标指针离开某一组件区域
<MouseWheel> 滚动滑轮
<KeyPress> 响应按下的任意键
<KeyPress-a> 按下 a 键,a可自定义(a-zA-Z)
<KeyRelease-a> 释放 a 键,a可自定义其它小写字母
<Alt-KeyPress-a> 同时按下 alt和a ;alt可用Ctrl和shift代替
<Double-KeyPress-a> 快速双击a
<Control-V> Ctrl和V键同时按下,V可以自定义

event 对象常用属性

名称 说明
char 按键字符,仅对键盘事件有效
Keycode 按键编码,仅对键盘事件有效
Keysym 按键名称,仅对键盘事件有效。比如按下 a 键:char:a、 Keycode:65、Keysym:a
num 鼠标按键,仅对鼠标事件有效
type 所触发的事件类型
widget 引起事件的组件
width,height 组件改变后的大小,仅Configure有效
x,y 鼠标当前位置,相对于父容器
x_root,y_root 鼠标当前位置,相对于整个屏幕

示例:

import tkinter as tk
# 创建主窗口
root = tk.Tk()
root.title("TK title")
root.geometry("500x500+10+20")
''' 鼠标和键盘事件 '''
C1 = tk.Canvas(root, width=200, height=200, bg='green')
C1.pack()
def mouseT(event):
    print("鼠标左键单击位置(父容器):{},{}".format(event.x,event.y))
    print("鼠标左键单击位置(屏幕):{},{}".format(event.x_root,event.y_root))
    print("事件绑定的组件:{}".format(event.widget))
def dragT(event):
    print("鼠标移动点位置:", event.x, event.y, event.x+1, event.y+1)
    C1.create_oval(event.x, event.y, event.x+10, event.y+10)
def keyboardT(event):
    '''响应按下的任意键'''
    print("键的keyconde:{}, 键的char:{}, 键的keysym:{}".format(event.keycode, event.char, event.keysym))
def press_a_T(event):
    print("按下a键")
def release_a_T(event):
    print("释放a键")
# 对 C1 画布范围内有效
C1.bind("<Button-1>", mouseT)
C1.bind("<B1-Motion>", dragT)
# 对整个GUI界面有效
root.bind("<KeyPress>", keyboardT)
# 只针对小写字母 a 有效
root.bind("<KeyPress-a>", press_a_T)
root.bind("<KeyRelease-a>", release_a_T)

root.mainloop()  # 运行GUI

多种事件绑定方式

  • 组件对象绑定

    1. 通过 command 属性绑定(适合不需要获取event对象)
    Button(root, text='登录', command=login)
    
    1. 通过 bind()方法绑定(合适需要获取event对象)
    c1 = tkinter.Canvas()
    c1.bind("<Button-1>", drawLine)
    
  • 组件类的绑定

    调用对象的 bind_class函数,将该组件类型的所有组件绑定事件:w.bind_class('Widget', 'event', eventhanler)

    w 代表控件

    Widget=控件,event=事件,eventhanler=调用函数

    # 比如给GUI上的所有按钮绑定事件
    btn01.bind_class("Button", "<Button-1>",func)
    

    示例:

    import tkinter as tk
    root = tk.Tk()
    root.title("TK title")
    def mouseT(event):
        print("bind()方式绑定,可以获取event对象:{}".format(event.widget))
    def mouseT1(a,b):
        print("command方式绑定,不能获取event对象:a={},b={}".format(a,b))
    def mouset2(event):
        print("右键单击事件,给所有的按钮绑定事件:{}".format(event.widget))
    btn1 = tk.Button(root, text="测试bind()事件绑定")
    btn1.pack()
    btn1.bind("<Button-1>", mouseT)   # bind方式绑定
    
    btn2 = tk.Button(root, text="测试command绑定", command=lambda :mouseT1('AA', "BB"))
    btn2.pack()
    # 绑定给所有的按钮右键事件
    btn2.bind_class("Button", "<Button-3>", mouset2)
    
    root.mainloop()  # 运行GUI
    

OptionMenu 下拉选择项

下拉选项,用来做多选一,选中的项在顶部显示。

示例:

import tkinter as tk
root = tk.Tk()
root.title("TK title")
strvar = tk.StringVar() 
strvar.set('编程')   # 设置默认值
om = tk.OptionMenu(root, strvar, 'java', 'python', 'php')
om['width'] = 10
om.pack()
root.mainloop()  # 运行GUI

Scale 移动滑块

用于在指定的数值区间,通过滑块的移动选择值。

相关属性:

activebackground, background, bigincrement, bd,bg, borderwidth, command, cursor, digits, fg, font, foreground, from,highlightbackground, highlightcolor, highlightthickness, label,
length, orient, relief, repeatdelay, repeatinterval, resolution,showvalue, sliderlength, liderrelief, state, takefocus, tickinterval, to, troughcolor, variable, width

示例:(默认为垂直方向)

import tkinter as tk
root = tk.Tk()
root.title("TK title")
def test(value):
    print("滑块的值:", value)
    # 重新设置按钮字体的大小,滑块控制
    lab.config(font=('宋体', value))
sca = tk.Scale(root, from_=5, to=50, length=200, tickinterval=5, command=test, orient='horizontal')  # 滑块方式,horizontal=水平
sca.pack()
lab = tk.Label(root, text='测试滑块', width=10, height=1, fg='white', bg='black', font=('宋体', 5))
lab.pack()
root.mainloop()  # 运行GUI

颜色选择框

可以帮助我们设置背景色、前景色、画笔色、字体颜色等等。

导入方式:

from tkinter.colorchooser import askcolor

示例:

import tkinter as tk
from tkinter.colorchooser import askcolor
root = tk.Tk()
root.title("TK title")
def test():
    # 创建颜色选择器,color=默认颜色
    ask = askcolor(color='red', title='颜色标题')
    print("背景色:", ask)  # 返回是 RGB 值
    root.config(bg=ask[1])  # 修改背景颜色
tk.Button(root, text='选择', command=test).pack()
root.mainloop()  # 运行GUI

文件对话框

可以帮助我们实现可视化的操作目录、文件。最后将文件、目录的信息传入到程序中。

导入方式:

from tkinter.filedialog import askopenfilename

常用函数:

函数名 对话框 说明
askopenfilename(**options) 文件对话框 返回打开的文件名
askopenfilenames(**options) 文件对话框 返回打开的多个文件名列表
askopenfile(**options) 文件对话框 返回打开文件对象
askopenfiles(**options) 文件对话框 返回打开的文件对象的列表
askdirectory(**options) 目录对话框 返回目录名
asksaveasfile(**options) 保存对话框 返回保存的文件对象
asksaveasfilename(**options) 保存对话框 返回保存的文件名

options 常用的值:

参数 说明
defaultextension 默认后缀:.xxx 用户没有输入则自动添加
filetypes=[(label1,pattern1),(label2,pattern2)] 文件显示过滤器
initaldir 初始目录
initalfile 初始目录
parent 父窗口,默认根窗口
title 窗口标题

选择文件示例:

import tkinter as tk
from tkinter.filedialog import askopenfilename
root = tk.Tk()
root.title("TK title")
# initialdir=默认打开目录路径,filetypes=文件过滤
def test():
    ask = askopenfilename(title='上传文件', initialdir='d:\\', filetypes=[('PY文件','.py')])
    show["text"] = ask
    vv.set(ask)  # 将选择的文件绝对路径,输入到文本框中
show = tk.Button(root, text='选择文件', command=test)
show.pack()
vv = tk.StringVar()
e = tk.Entry(root, textvariable=vv, width=50) 
e.pack()
root.mainloop()  # 运行GUI

直接读取文件的内容示例:

import tkinter as tk
from tkinter.filedialog import askopenfile
root = tk.Tk()
root.title("TK title")
def test():
    with askopenfile(title='上传文件', initialdir="d:\\", filetypes=[('文件', '.txt')]) as f:
        show.delete(1.0, 'end')  # 删除文本框中之前的内容
        show.insert('insert', f.read())  # 将读取到的文件内容写入到文本框中
tk.Button(root, text='选择文件', command=test).pack()
show = tk.Text(root, width=60, height=20)
show.pack()
root.mainloop()  # 运行GUI

对话弹框

simpledialog(简单对话框)包含如下常用函数:

函数 说明
askfloat(title,prompt,**kw) 输入并返回浮点数
askinteger(title,prompt,**kw) 输入并返回整数
askstring(title,prompt,**kw) 输入并返回字符串

title 代表窗口标题;prompt 是提示描述内容;
kw 代表不定长参数,可以为:initialvalue(初始值), minvalue(最小值), maxvalue(最大值)

示例:

import tkinter as tk
from tkinter.simpledialog import askfloat, askinteger,askstring
root = tk.Tk()
root.title("TK title")
def integer_t(): # 其他两个用法一样
    integer = askinteger(title='整数', prompt='请输入整数:', initialvalue=10, minvalue=0, maxvalue=100)
    show.insert('end', integer)
But = tk.Button(root, text='输入整数', command=integer_t)
But.pack()
show = tk.Text(root, width=60, height=20)
show.pack()
root.mainloop()  # 运行GUI

菜单

主菜单通常位于 GUI 程序的上方位置。

导入方式:

from tkinter.filedialog import *

创建主菜单步骤:

  1. 创建主菜单栏对象:
menubar = tk.Menu(master=root)
  1. 创建菜单选项,并添加到主菜单栏对象中:
menuFile = tk.Menu(menubar)
menubar.add_cascade(label="文件(F)", menu=menuFile)
menuEdit = tk.Menu(menubar)
menubar.add_cascade(label="编辑(E)", menu=menuEdit)
  1. 添加子菜单项到第2步骤的菜单中:
menuFile.add_command(label="新建", accelerator="ctrl+n", command=self.newfile)
menuFile.add_command(label="打开", accelerator="ctrl+o", command=self.openfile)
menuEdit.add_command(label="查找", accelerator="ctrl+f", command=self.test)
menuEdit.add_command(label="删除", accelerator="ctrl+d", command=self.test)
  1. 将菜单添加到root窗口:
root["menu"] = menubar

创建菜单示例:

# ''' 创建主菜单 '''
menubar = tk.Menu(root)
# ''' 创建子菜单 '''
menuFile = tk.Menu(menubar)
menuEdit = tk.Menu(menubar)
menuHelp = tk.Menu(menubar)
# '''将子菜单加入到主菜单栏'''
# label=名称;menu=对应的菜单
menubar.add_cascade(label="文件(F)", menu=menuFile)
menubar.add_cascade(label="编辑(E)", menu=menuEdit)
menubar.add_cascade(label="帮助(H)", menu=menuHelp)
# '''添加文件菜中的子菜单'''
# label=名称;accelerator=快捷键方式描述;command=选项执行事件
menuFile.add_command(label="新建", accelerator="ctrl+n", command=self.newfile)
menuFile.add_command(label="打开", accelerator="ctrl+o", command=self.openfile)
menuFile.add_command(label="保存", accelerator="ctrl+s", command=self.savefile)
menuFile.add_separator()  # 添加分割线
menuFile.add_command(label="退出", accelerator="ctrl+q", command=self.exit)
# '''添加编辑菜中的子菜单'''
menuEdit.add_command(label="查找", accelerator="ctrl+f", command=self.test)
menuEdit.add_command(label="删除", accelerator="ctrl+d", command=self.test)
# '''添加帮助菜中的子菜单'''
menuHelp.add_command(label="帮助", accelerator="ctrl+h", command=self.test)

# ''' 将主菜单添加到 root 主窗口 '''
root["menu"] = menubar

创建鼠标右键菜单步骤:

  1. 创建菜单对象:
contextMenu = tk.Menu(root)
  1. 创建菜单选项:
contextMenu.add_command(label="背景颜色", command=openAskcolor)
def openAskcolor():
   color = askcolor(color='red', title="选择背景颜色", )
   textpad.config(bg=color[1])
  1. 绑定鼠标右键事件:
def createContextMenu(event):
   # 右键菜单,在鼠标右键时显示菜单
   contextMenu.post(event.x_root, event.y_root)
root.bind("<Button-3>", createContextMenu)

示例:

# 文本编辑区
self.textpad = Text(root, width=60, height=30)
self.textpad.pack()
# 创建鼠标右键上下菜单
self.contextMenu = tk.Menu(root)
self.contextMenu.add_command(label="背景颜色", command=self.openAskcolor)

# 右键绑定事件
root.bind("<Button-3>", self.createContextMenu)
def openAskcolor(self):
    color = askcolor(color='red', title="选择背景颜色", )
    self.textpad.config(bg=color[1])  # 修改背景颜色

简单记事本案例:

import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter.ttk import Scrollbar, Checkbutton, Label, Button
import os

class NotePad(tk.Tk):

    def __init__(self):
        super().__init__()
        self.set_window()
        self.create_menu_bar()
        self.create_body()

    def set_window(self):
        '''设置窗口参数'''
        self.title("记事本")
        max_width, max_height = self.maxsize()  # 屏幕的大小
        align_center = "800x600+%d+%d" % ((max_width-800)/2, (max_height-600)/2)
        self.geometry(align_center)
        self.iconbitmap("www.gif")  # 图标

    def create_menu_bar(self):
        ''' 创建菜单对象 '''
        menu_bar = tk.Menu(self)

        # 添加菜单项目  tearoff=去掉默认分隔符
        File_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="文件", menu=File_menu)  # 菜单名称
        File_menu.add_command(label="打开", accelerator="ctrl+o", command=self.open_file)  # 下拉菜单名称
        File_menu.add_separator()
        File_menu.add_command(label="退出", accelerator="Ait+F4", command=self.quit)  # 二级菜单

        Editor_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="编辑", menu=Editor_menu)  # 菜单名称
        Editor_menu.add_command(label="查找", accelerator="Ctrl+F", command=self.find_text_dialog)

        View_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="视图", menu=View_menu)  # 菜单名称
        # 显示行号
        self.is_show_line = tk.IntVar()
        self.is_show_line.set(1)  # 默认从1开始
        View_menu.add_checkbutton(label="显示行号", onvalue=0, offvalue=1,
                                  variable=self.is_show_line, command=self.update_line_num)
        # 高亮当前行
        self.is_height_line = tk.IntVar()
        View_menu.add_checkbutton(label="高亮当前行", variable=self.is_height_line, command=self.toggle_row)
        # 主题,下拉菜单嵌套菜单
        themes_menu = tk.Menu(menu_bar, tearoff=0)
        View_menu.add_cascade(label="主题", menu=themes_menu)  # 菜单名称
        themes_menu.add_command(label="主题1", accelerator="a+b", command="")

        About_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="关于", menu=About_menu)  # 菜单名称
        About_menu.add_command(label="", command="")

        # 将设置的菜单项目加的菜单栏中
        self.config(menu=menu_bar)

    def create_body(self):
        ''' 左边=行号,中间编辑区,右边=滚动条 '''
        # 行号区域
        self.line_num_bar = tk.Text(self, width=2, padx=1, takefocus=0, border=0,
                                    background="#AAADAA", state="disable")
        self.line_num_bar.pack(side="left", fill="y")
        # 文本编辑区
        self.context_text = tk.Text(self, wrap="word", undo=True)
        self.context_text.pack(fill="both", expand='yes')

        # 热键绑定-注意按键参数会传入到指定事件中
        self.context_text.bind("<Control-o>", self.open_file)
        self.context_text.bind("<Any-KeyPress>", self.update_line_num)
        self.context_text.bind('<Button-1>', self.toggle_row)

        # 设置文本输入区域
        self.context_text.tag_config("active_line", background="#F7E3AD")

        # 垂直滚动条
        scroll_bar = Scrollbar(self.context_text)  # 将滚动条与文本框关联
        scroll_bar['command'] = self.context_text.yview  # 将滚动条与文本框的Y轴关联
        self.context_text["yscrollcommand"] = scroll_bar.set  # 将文本框的Y轴滚动交给滚动条处理
        scroll_bar.pack(side="right", fill="y")

    def open_file(self, event=None):
        ''' 打开文件   '''
        input_fiel = filedialog.askopenfilename(filetype=[("文本类型", ".txt"), ("所有类型", "*.*")])
        if input_fiel:
            # 不同状态,修改标题显示
            self.title("{}**".format(os.path.basename(input_fiel)))
            self.context_text.delete(1.0, "end")
            with open(input_fiel, "r") as f:
                self.context_text.insert(1.0, f.read())
        else:
            messagebox.showerror("错误", "打开文件失败!")

    def update_line_num(self, event=None):
        ''' 行号处理 '''
        # self.is_show_line.get  0:代表选中, 1:代表未选中
        if self.is_show_line.get() == 0:
            # 获取所有的行
            row, col = self.context_text.index("end").split(".")
            # 列举每行的行号
            line_num_content = "\n".join([str(i) for i in range(1, int(row))])
            self.line_num_bar.config(state="normal")
            self.line_num_bar.delete(1.0, "end")
            self.line_num_bar.insert(1.0, line_num_content)
            self.line_num_bar.config(state="disable")
        else:
            self.line_num_bar.config(state="normal")
            self.line_num_bar.delete(1.0, "end")
            self.line_num_bar.config(state="disable")

    def toggle_row(self, event=None):
        ''' 高亮行 '''
        if self.is_height_line.get():
            self.context_text.tag_remove("active_line", 1.0, "end")
            # 设置高亮
            self.context_text.tag_add("active_line", "insert linestart", "insert lineend+1c")
            # 通过递归方式进行处理 或 鼠标左键事件
            # self.context_text.after(200, self.toggle_row)
        else:
            self.context_text.tag_remove("active_line", 1.0, "end")

    def find_text_dialog(self):
        ''' 查找对话框 '''
        # 查找对话框大小设置
        search_dialog = tk.Toplevel(self)
        search_dialog.title("查找文本")
        max_width, max_height = self.maxsize()
        align_center = "300x80+{}+{}".format(int((max_width-300)/2), int((max_height-80)/2))
        search_dialog.geometry(align_center)
        search_dialog.resizable(False, False)
        Label(search_dialog, text="查找全部").grid(row=0, column=0, sticky="e")
        search_text = tk.Entry(search_dialog, width=25)
        search_text.grid(row=0, column=1, padx=2, pady=2, sticky="we")
        search_text.focus_set()
        # 忽略大小写
        ignore_case_value = tk.IntVar()
        Checkbutton(search_dialog, text="忽略大小写", variable=ignore_case_value).grid(
            row=1, column=1, padx=2, pady=2, sticky="e")
        Button(search_dialog, text="查找", command=lambda: self.search_result(search_text.get(),
                             ignore_case_value.get(), search_dialog, search_text)).grid(row=0, column=2,
                                                                        sticky='w'+"e", padx=10, pady=1)
        # 关闭
        def colse_search_dialog():
            self.context_text.tag_remove('match', 1.0, 'end')
            search_dialog.destroy()
        search_dialog.protocol('WM_DELETE_WINDOW', colse_search_dialog)
        return "break"

    def search_result(self, text, ignore_case, search_dialog, search_box):
        '''
        :param text:  要查找的文本内容
        :param ignore_case:  是否忽略大小写
        :param search_dialog: 查找的目标窗体
        :param search_box:  查找结果焦点
        '''
        # 清楚匹配内容
        self.context_text.tag_remove('match', 1.0, 'end')
        matches_found = 0
        if text:
            start_pos = 1.0
            while True:
                start_pos = self.context_text.search(text, start_pos, nocase=ignore_case, stopindex='end')
                if not start_pos:
                    break
                end_pos = "{}+{}c".format(start_pos, len(text))
                self.context_text.tag_add('match', start_pos, end_pos)
                matches_found += 1
                start_pos = end_pos
            self.context_text.tag_config('match', foreground="red", background="yellow")
        search_box.focus_set()
        search_dialog.title("发现了 %d 个匹配项" % matches_found)

if __name__ == '__main__':
    app = NotePad()
    app.mainloop()
posted @ 2021-08-13 15:20  HonyL  阅读(1095)  评论(1编辑  收藏  举报