tkinter -python -中文文档
tkinter官方文档 (英文)
tkinter初学者文档 (英文)
一些例子和接口文档-python - Tkinter (英文)
具体的一些事件列表
一个学习笔记
其他备选 Gui库
更多python - Gui - 库(英文)
前面写的程序都是控制台,程序和用户的交互通过控制台来完成
现在我们 来学一下 GUI (graphics user interface)
,就是图形界面编程
。
Gui 编程类似于 搭建积木, 和 Scratch 儿童编程 有点类似。
将一个个 组件(widget
)放到窗口中
比如我们使用的 微信 qq 等
既然学习 python
那我们就以 python
自带的编辑器 IDLE
来说明
最上面的 各种按钮 , 比如 file ,edit , shell 等按钮 ,都是一个一个组件。 并且通过 ,增加 对事件 的处理 称为一个完整的程序。(这个编辑器就是 用 tkinter
写的), 其实挺好的唯一 一点就是丑。。。
另外说明一下,整个程序就是一个死循环。 一直监听 你的操作行为, 比如你是否点击了按钮,按下了鼠标等等。
Tkinter
的GUI组件关系Misc
和 Wm
Misc
: 他是所有组件的根父类Wm
: 它 主要提供了 一些与窗口管理器通信的功能函数
Tk
Misk
和 Wm
派生出子类 Tk,它代表应用程序的主窗口。 一般应用程序都需要直接或者间接使用Tk
Pack
, Place
, Grid
Tkinter类 | 名称 | 简介 |
---|---|---|
Toplevel |
顶层 | 容器类,可用于其他组件提供的单独的容器:Toplevel 有点类似于窗口 |
Button |
按钮 | 代表按钮组件 |
Canvas |
画布 | 提供绘图功能,包括直线,矩形,椭圆,多边形,位图等 |
Checkbutton |
复选框 | 可供用户勾选的复选框 |
Entry |
单行输入框 | 用户可输入内容 |
Frame |
容器 | 用于装载其他Gui 组件 |
Label |
标签 | 用于显示不可编辑的文本或图标 |
LabelFrame |
容器 | 也是容器组件,类似于Frame ,但它支持添加标题 |
ListBox |
列表框 | 列出多个选项,供用户选择 |
Menu |
菜单 | 菜单组件 |
Menubutton |
菜单按钮 | 用来包含菜单的按钮,(包括下拉式,层叠式) |
OptionMenu |
菜单按钮 | MenuButton 的子类,也代表菜单按钮,可通过按钮打开一个菜单 |
Message |
消息框 | 类似于标签,但可以显示多行文本,后来当Label 也能显示多行文本之后, 组件基本处于废弃状态 |
PanedWindwo |
分区窗口 | 该容器会被划分成多个区域,每添加一个组件占一个区域,用户可通过拖动分隔线来改变各区域的大小 |
RadioButton |
单选按钮 | 可供用户点击的单选按钮 |
Scale |
滑动条 | 拖动滑块可设定起始值 和结束值, 可显示当前位置的精确值 |
Spinbox |
微调选择器 | 用户可通过该组件的向上,向下箭头选择不同的值 |
Scollbar |
滑动条 | 用于组件(文本域,画布,列表框,文本框)提供滚动功能 |
Text |
多行文本框 | 显示多行文本 |
更多的类 可以查看文档
或者使用 tkinter.__dict__
来查看
In [28]: import tkinter as tk
In [29]: tk.__dict__
# 输出太长就不展示了。
# 下面截个图 。看一部分
属性里都可以看到的。
具体如何看 这篇 基础篇_98节_模块 有详细讲解 如何自学模块。
Tkinter
tkinter
( Tk interface ) 是Python 的 标准GUI 库, 支持跨平台 的 GUI 程序开发。 适合小型的GUI程序编写
。也特别适合初学者学习GUI 编程
。所以 原理都是一样的。 学会了 Tkinter
再学其他的 GUI
库 也很简单。wxPython
tkinter
. 整体框架类似于 MFC
(Microsoft Foundation Classes 微软基础类库)PyQT
Qt
是一种开源的Gui 库。 适合 大型 GUI程序开发, PyQt
是Qt 工具包标准的 python
实现 , 我们也可以用Qt 官方 的 Qt Desginer
界面设计快速开发 Gui
程序( 鼠标拖动,设置点参数,非常方便。 )tkinter
gui
程序# 第一个 tkinter gui程序
from tkinter import *
from tkinter import messagebox
root = Tk()
root.title('我的第一个gui程序')
# 显示 程序 名称
root.geometry('500x300+100+200')
# 500 宽度 300是高度 +100 是 距离左边 的位置 +200 是 距离上面的 位置
btn01 = Button(root)
btn01['text'] = 'please click me'
btn01.pack() # 组件 放到窗口里面
def cool(e): # e就是 事件对象
messagebox.showinfo('Message', 'the onepis is a cute boy')
print('嘻嘻,我好喜欢python')
btn01.bind('' , cool)
# 事件绑定 # 是固定写法。 就是一个事件的名字 button-1 是 左键单击
# 表示 点击这个按钮的时候 就调用 cool 函数。
# 其实我感觉 这也算是 一种高阶函数的用法。
# 又很像 回调函数。
root.mainloop() # 调用 组件的mainloop()方法 进入事件循环
from tkinter import *
from tkinter import messagebox
# 测试经典的GUi程序的写法,使用面向对象的方式.
# 继承 Frame
class Application(Frame):
'''一个经典的GUi程序的写法'''
def __init__(self, master=None): # 构造器 构造组件对象
super().__init__(master)
# super() 代表的是 父类的定义,而不是父类对象,调用父类的 构造方法
# 这里的 master 根据继承的父类是否需要传入参数 来 确认.
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
'''创建组件'''
self.btn01 = Button(self)# 创建一个 按钮
self.btn01['text'] = 'please click me !' # 设置按钮文本
self.btn01.pack() # 通过布局管理器显示
self.btn01['command'] = self.cool # 通过 command 设置 回调函数,或者说 事件响应函数。
# 绑定事件
# 创建一个退出按钮
# 和上面 的 btn01['command'] 性质一样
# 也可以 btnQuit['command]=root.destroy
# btnQuit['text']='退出'
self.btnQuit = Button(
self,
text='退出',
command=root.destroy # 这里destroy 不要 加 括号 () 因为是 作为参数。 而不是立即调用 执行 destroy 方法
) # 写的是退出整个程序
# 当然你可以写 self.destroy 这样就退出了 当前 app
# 但是还会有一个root 主窗口存在
self.btnQuit.pack() # 通过布局管理器显示
def cool(e):
# e就是 事件对象
messagebox.showinfo('Message', 'the onepis is a cute boy')
print('嘻嘻,我好喜欢python')
if __name__ == "__main__":
root = Tk() # 创建 根窗口对象
root.geometry("400x100+200+300") # 窗口 大小和 位置
root.title("一个经典的Gui程序类的测试")
app = Application(master=root) # 创建 一个虚拟矩形区域 app
root.mainloop() # 调用 组件的mainloop()方法 # 进入事件循环
Label
标签Label - 形参(parameters) 文档
arguments
是实参
约定俗成 的 称呼。
from tkinter import *
from tkinter import messagebox
import os
os.chdir("D:\Python\StudyPython\Jupyter\GUI编程\imgs")
# 关于Label的一些 用法
class Application(Frame):
'''一个经典的GUi程序的写法'''
def __init__(self, master=None): # 构造器 构造组件对象
super().__init__(master)
# super() 代表的是 父类的定义,而不是父类对象,调用父类的 构造方法
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
#创建组件
self.label01 = Label(self,
text="python coder or programmer",
width=30,
height=2,
bg="black",
fg="white",font=("黑体", 30))
# text 显示文本
# width 宽度
# height 高度
# bg 背景颜色
# fg 前景色
# font 字体的一些属性。比如字号, 字体
self.label01.pack() # 布局管理器
self.label02 = Label(self,
text="i love python",
width=30,
height=2,
bg="blue",
fg="white",
font=("黑体", 30))
self.label02.pack() # 布局管理器
# 显示图像
global photo
photo = PhotoImage(file="./a.gif")
# photo 局部变量 会被销毁,所以需要把 photo 声明 为全局变量
# 需要注意,不然无法加载图片 显示图片
# 加载图片
self.label03 = Label(self, image=photo) # 设置label
self.label03.pack() # 布局管理器
# 显示多行文本
self.label04 = Label(self,
text="我爱自由,\n在自由面前,\n生命和金钱都可以抛弃!",
borderwidth=1, # 边框宽度
relief="sunken", # 边框装饰。 实现 虚线等
# 可选项 flat无边框 , groove内凹边框, raised凸起, ridge 凸起, solid 实线 ,sunken内凹
justify="right") # 多行文本对齐方式
self.label04.pack() # 布局管理器
def cool(e):
# e就是 事件对象
messagebox.showinfo('Message', 'the onepis is a cute boy')
print('嘻嘻,我好喜欢python')
if __name__ == "__main__":
root = Tk() # 创建 根窗口对象
# 因为在一个程序中只能存在一个根窗口,也就是说只能存在一个Tk()
# 所以报错的同学
# 当然一般出现这种情况的都是在命令行下
# 我们这时候可以输入 root.destroy 退出 root .
# 或者单独运行下 root.mainloop() 然后关掉 弹出的窗口
root.geometry("800x900+200+300") # 窗口 大小和 位置
root.title("测试label标签")
app = Application(master=root) # 创建 一个虚拟矩形区域 app
root.mainloop() # 调用 组件的mainloop()方法 # 进入事件循环
Button
按钮# button 可以是文本 也可以是图像,本质上和 Label 组件使用方法是一样的
from tkinter import *
from tkinter import messagebox
# 测试经典的GUi程序的写法,使用面向对象的方式.
class Application(Frame):
'''一个经典的GUi程序的写法'''
def __init__(self, master=None): # 构造器 构造组件对象
super().__init__(master) # super() 代表的是 父类的定义,而不是父类对象,调用父类的 构造方法
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
'''创建组件'''
self.btn01 = Button(self,anchor=E,width=16,height=9) # 设置宽高
# anchor 可以作为定位用, 东南西北,西北等 NE 是东南西北英文首字母
self.btn01['text'] = 'please click me' # 设置文本
self.btn01.pack()
self.btn01['command'] = self.cool # 绑定事件
# 创建一个退出按钮
self.btnQuit = Button(
self,
text='退出',
command=root.destroy # 这里destroy 不要 加 括号 ()
)
self.btnQuit.pack()
def cool(e):
# e就是 事件对象
messagebox.showinfo('Message', 'the onepis is a cute boy')
print('嘻嘻,我好喜欢python')
if __name__ == "__main__":
root = Tk() # 创建 根窗口对象
root.geometry("400x600+200+300") # 窗口 大小和 位置
root.title("一个经典的Gui程序类的测试")
app = Application(master=root) # 创建 一个虚拟矩形区域 app
root.mainloop() # 调用 组件的mainloop()方法 # 进入事件循环
"""测试Button组件的基本用法,使用面向对象的方式"""
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
"""创建组件"""
self.btn01 = Button(root, text="闯荡大海",
width=16,height=9,anchor=NE,command=self.login)
self.btn01.pack()
global photo
photo = PhotoImage(file="./start.gif")
self.btn02 = Button(root, image=photo,command=self.login)
self.btn02.pack()
self.btn02.config(state="disabled") #设置按钮为禁用
def login(self):
messagebox.showinfo("海贼王", "成为四王之一!onepis !")
if __name__ == '__main__':
root = Tk()
root.geometry("400x130+200+300")
app = Application(master=root)
root.mainloop()
entry
单行文本# entry单行文本框
# 用来接收一行字符串的控件
# 比如输密码的时候 的文本框
"""测试Entry组件的基本用法,使用面向对象的方式"""
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
"""创建登录界面的组件"""
self.label01 = Label(self,text="用户名")
self.label01.pack()
# StringVar变量绑定到指定的组件。
# StringVar变量的值发生变化,组件内容也变化;
# 组件内容发生变化,StringVar变量的值也发生变化。
v1 = StringVar()
self.entry01 = Entry(self,textvariable=v1)
self.entry01.pack()
v1.set("admin")
print(v1.get());print(self.entry01.get())
# 创建密码框
self.label02 = Label(self,text="密码")
self.label02.pack()
v2 = StringVar()
self.entry02 = Entry(self,textvariable=v2, show="*")
self.entry02.pack()
Button(self,text="登陆",command=self.login).pack()
def login(self):
username = self.entry01.get()
pwd = self.entry02.get()
print("去数据库比对用户名和密码!")
print("用户名:"+username)
print("密码:"+pwd)
if username=="onepis" and pwd=="123456":
messagebox.showinfo("海贼王悬赏系统", "登录成功!去剿灭海军吧!")
else:
messagebox.showinfo("海贼王悬赏系统","登录失败!你是海军的奸细吧!")
if __name__ == '__main__':
root = Tk()
root.geometry("400x130+200+300")
app = Application(master=root)
root.mainloop()
Text
多行文本"""测试Text多行文本框组件的基本用法,使用面向对象的方式"""
from tkinter import *
import webbrowser
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
self.w1 = Text(root, width=40, height=12,bg="white")
# 宽度20个字母(10个汉字),高度一个行高
self.w1.pack()
self.w1.insert(1.0, "0123456789\nabcdefg")# 第一行第0列
self.w1.insert(2.3, "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦\n") # 第2行第3列 插入文本
Button(self,text="重复插入文本",command=self.insertText).pack(side="left")
# side 默认是垂直排列 ,left 左对齐
Button(self,text="返回文本",command=self.returnText).pack(side="left")
Button(self,text="添加图片",command=self.addImage).pack(side="left")
Button(self,text="添加组件",command=self.addWidget).pack(side="left")
Button(self,text="通过tag精确控制文本",command=self.testTag).pack(side="left")
def insertText(self):
# INSERT索引表示在光标处插入
self.w1.insert(INSERT, ' pis1 ')
# END索引号表示在最后插入
self.w1.insert(END, '[programming]')
self.w1.insert(1.8, "onepis")# 第一行 8列插入文本
def returnText(self):
# Indexes(索引)是用来指向Text组件中文本的位置,Text的组件索引也是对应实际字符之间的位置。
# 核心:行号以1开始 列号以0开始
print(self.w1.get(1.2, 1.6))# 返回2345
print("所有文本内容:\n"+self.w1.get(1.0, END))
def addImage(self):
# global photo # 或者 可以定义为下面的 对象的一个属性
self.photo = PhotoImage(file="./a.gif")
self.w1.image_create(END, image=self.photo)
def addWidget(self):
b1 = Button(self.w1, text='爱编程')
# 在text创建组件的命令
self.w1.window_create(INSERT, window=b1)
def testTag(self):
self.w1.delete(1.0,END)
self.w1.insert(INSERT, "good good study,day day up!\n我爱写代码\n程序员\n百度,搜一下就知道")
self.w1.tag_add("good", 1.0, 1.9) # 添加标记 1行0 列。 到 1行9列
self.w1.tag_config("good", background="yellow", foreground="red")# 标记 颜色 背景色设为黄色 前景色 设为红色
self.w1.tag_add("baidu", 4.0, 4.2)
self.w1.tag_config("baidu", underline=True)
self.w1.tag_bind("baidu", "" , self.webshow)# 绑定事件 可以
def webshow(self,event):
webbrowser.open("http://www.baidu.com")
if __name__ == '__main__':
root = Tk()
root.geometry("450x300+200+300")
app = Application(master=root)
root.mainloop()
RadioButton
"""测试Radiobutton组件的基本用法,使用面向对象的方式"""
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
self.v = StringVar();
self.v.set("F")# 默认是把女性选中了
self.r1 = Radiobutton(self, text="男性", value="M", variable=self.v)# man
self.r2 = Radiobutton(self, text="女性", value="F", variable=self.v)#
self.r1.pack(side="left");self.r2.pack(side="left")# 通过分号把两个语句隔开
Button(self, text="确定", command=self.confirm).pack(side="left")
def confirm(self):
messagebox.showinfo("测试","选择的性别:"+self.v.get())
if __name__ == '__main__':
root = Tk()
root.geometry("400x50+200+300")
app = Application(master=root)
root.mainloop()
Checkbutton
"""测试Checkbutton组件的基本用法,使用面向对象的方式"""
# 复选按钮 用法和单选框,单选按钮是一样的
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
self.codeHobby = IntVar();
self.videoHobby = IntVar()
print(self.codeHobby.get()) # 默认值是0
self.c1 = Checkbutton(self, text="敲代码",
variable=self.codeHobby, onvalue=1, offvalue=0)
self.c2 = Checkbutton(self, text="看视频",
variable=self.videoHobby, onvalue=1, offvalue=0)
self.c1.pack(side="left");self.c2.pack(side="left")
Button(self, text="确定", command=self.confirm).pack(side="left")
def confirm(self):
if self.videoHobby.get() == 1:
messagebox.showinfo("测试","看视频,都是正常人有的爱好!你喜欢看什么类型?")
if self.codeHobby.get() == 1:
messagebox.showinfo("测试","抓获野生程序猿一只,赶紧送给他学习视频充饥吧!")
if __name__ == '__main__':
root = Tk()
root.geometry("400x50+200+300")
app = Application(master=root)
root.mainloop()
canvas
画布# canvas画布
#基本用法
"""测试Canvas组件的基本用法,使用面向对象的方式"""
from tkinter import *
from tkinter import messagebox
import random
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
self.canvas = Canvas(self, width=300, height=200, bg="green")
self.canvas.pack()
# 画一条直线
line = self.canvas.create_line(10, 10, 30, 20, 40, 50)
# 10 10 是一个点的坐标, 30,20 又是 一个坐标,甚至元组或者 列表放进去 也可以的
# 画一个矩形.
rect = self.canvas.create_rectangle(50, 50, 100, 100)# 50,50 指的是左上角的坐标,到右下角的坐标100,100
# 画一个椭圆.坐标两双。为椭圆的边界矩形左上角和底部右下角
oval = self.canvas.create_oval(50, 50, 100, 100)# 指的是外切矩形的 坐标
global photo
photo = PhotoImage(file="./a.gif")
self.canvas.create_image(150,170,image=photo)
Button(self, text="画10个矩形", command=self.draw50Recg).pack(side="left")
def draw50Recg(self):
for i in range(0, 10):
x1 = random.randrange(int(self.canvas["width"])/2)
y1 = random.randrange(int(self.canvas["height"])/2)
x2 = x1 + random.randrange(int(self.canvas["width"])/2)
y2 = y1 + random.randrange(int(self.canvas["height"])/2)
self.canvas.create_rectangle(x1, y1, x2, y2)
if __name__ == '__main__':
root = Tk()
root.geometry("400x300+200+300")
app = Application(master=root)
root.mainloop()
Grid
布局管理器"""测试Grid布局管理器的基本用法,使用面向对象的方式"""
# tkinter 提供3种 布局管理器
# pack grid place
# grid是表格布局 比较常用和典型
from tkinter import *
from tkinter import messagebox
import random
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
"""通过grid布局实现登录界面"""
self.label01 = Label(self,text="用户名")
self.label01.grid(row=0,column=0)# 通过行列定位0行 0列
self.entry01 = Entry(self)
self.entry01.grid(row=0,column=1)
Label(self,text="用户名为手机号").grid(row=0,column=2)
Label(self, text="密码").grid(row=1, column=0)
Entry(self, show="*").grid(row=1, column=1)
Button(self, text="登录").grid(row=2, column=1, sticky=EW)# 也是东南西北中 以及4个脚,也可以ws,西东 拉伸
Button(self, text="取消").grid(row=2, column=2, sticky=E)
if __name__ == '__main__':
root = Tk()
root.geometry("400x90+200+300")
app = Application(master=root)
root.mainloop()
"""计算器软件界面的设计"""
# 小实例
# 做一个计算器的界面
from tkinter import *
from tkinter import messagebox
import random
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
"""通过grid布局实现计算器的界面"""
btnText = (("MC","M+","M-","MR"),
("C","±","/","*"),
(7,8,9,"-"),
(4,5,6,"+"),
(1,2,3,"="),
(0,"."))
# 输入文本框
Entry(self).grid(row=0,column=0,columnspan=4,pady=10)
# 输入按钮
for rindex,r in enumerate(btnText):
for cindex,c in enumerate(r):
if c == "=":
Button(self,text=c,width=2).grid(row=rindex+1,column=cindex,sticky=NSEW,rowspan=2)# 设置按钮,sticky 东南西北都贴住
# 设置跨行 rowspan
elif c == 0 :
Button(self,text=c,width=2).grid(row=rindex+1,column=cindex,sticky=NSEW,columnspan=2)# 设置按钮,sticky 东南西北都贴住
elif c == "." :
Button(self,text=c,width=2).grid(row=rindex+1,column=cindex+1,sticky=NSEW)# 设置按钮,sticky 东南西北都贴住
else:
Button(self,text=c,width=2).grid(row=rindex+1,column=cindex,sticky=NSEW)# 设置按钮,sticky 东南西北都贴住
if __name__ == '__main__':
root = Tk()
root.geometry("200x250+200+300")
app = Application(master=root)
root.mainloop()
pack
布局管理器#pack布局管理器
# coding=utf-8
# 测试pack布局管理
#如上列出了 pack 布局所有的属性, 但是不需
# 要挨个熟悉, 了解基本的即可。 pack 适用于简单的垂直或水
# 平排布, 如果需要复杂的布局可以使用 grid 或 place。
from tkinter import *
root = Tk();root.geometry("700x220")
# Frame是一个矩形区域,就是用来放置其他子组件
f1 = Frame(root)
f1.pack()
f2 = Frame(root)
f2.pack()
btnText = ("流行风","中国风","日本风","重金属","轻音乐")
for txt in btnText:
Button(f1,text=txt).pack(side="left",padx="10")
# side =left 水平排布
for i in range(1,13):
Label(f2,width=5,height=10,borderwidth=1,relief="solid",
bg="black" if i%2==0 else "white").pack(side="left",padx=2)
# 这样的 if else 更方便一些
root.mainloop()
place
布局管理器# place 布局管理器
# place 布局管理器可以通过坐标精确控制组件的位置, 适用
# 于一些布局更加灵活的场景。
# coding=utf-8
from tkinter import *
root = Tk();root.geometry("500x300")# 500宽 300 高
root.title("布局管理place");root["bg"]="white"
f1 = Frame(root,width=200,height=200,bg="green")
f1.place(x=30,y=30)
# 宽度用的是相对高度 相对于root父容器 主要是 定位的用法
Button(root,text="python").place(relx=0.2,x=100,y=20,relwidth=0.2,relheight=0.5)
Button(f1,text="程序员").place(relx=0.6,rely=0.7)
Button(f1,text="Crawl").place(relx=0.5,rely=0.2)
root.mainloop()
"""扑克牌游戏的界面设计"""
# 通过 place 布局管理器做一个扑克牌 游戏界面,并且增加一个出牌 事件
from tkinter import *
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
"""通过place布局管理器实现扑克牌位置控制"""
# self.photo = PhotoImage(file="./puke/puke1.gif")
# self.puke1 = Label(self.master,image=self.photo)
# self.puke1.place(x=10,y=50)
self.photos=[PhotoImage(file="./puke/puke"+str(i+1)+".gif") for i in range(10) ]
self.pukes=[Label(self.master,image=self.photos[i]) for i in range(10) ]
for i in range(10):
self.pukes[i].place(x=10+40*i,y=50)# 放入布局管理器中 显示
self.pukes[0].bind_class("Label","" ,self.chupai)
def chupai(self,event):# event 事件
print(event.widget.winfo_geometry())
print(event.widget.winfo_y())
if event.widget.winfo_y()==50:
event.widget.place(y=30)
else:
event.widget.place(y=50)
if __name__ == '__main__':
root = Tk()
root.geometry("600x270+200+300")
app = Application(master=root)
root.mainloop()
# 事件处理
# 一个 GUI 应用整个生命周期都处在一个消息循环 (event
# loop) 中。 它等待事件的发生, 并作出相应的处理。
# Tkinter 提供了用以处理相关事件的机制. 处理函数可被绑
# 定给各个控件的各种事件。
# widget.bind(event, handler)
# 鼠标左键按下 2 表示中键, 3 表示右键
#
# <1> 也可以执行, 就是鼠标单击的意思
# coding=utf-8
# 测试键盘和鼠标事件
from tkinter import *
root = Tk();root.geometry("530x300")
c1 = Canvas(root,width=200,height=200,bg="green")
c1.pack()
def mouseTest(event):
print("鼠标左键单击位置(相对于父容器):{0},{1}".format(event.x,event.y))
# 左键单击打印坐标
print("鼠标左键单击位置(相对于屏幕):{0},{1}".format(event.x_root,event.y_root))
# 左键单击打印坐标
print("事件绑定的组件:{0}".format(event.widget))
# 打印绑定的组件
def testDrag(event):
c1.create_oval(event.x,event.y,event.x+1,event.y+1)
# 画圆
def keyboardTest(event):
print("键的keycode:{0},键的char:{1},键的keysym:{2}"
.format(event.keycode,event.char,event.keysym))
def press_a_test(event):
print("press a")
def release_a_test(event):
print("release a")
c1.bind("" ,mouseTest)# 左键点击的方法 绑定的是
c1.bind("" ,testDrag)# 拖动 的时候做什么 testdrag
root.bind("" ,keyboardTest)# 对任意键 都响应
root.bind("" ,press_a_test) #只针对小写的a,大写的A不管用
root.bind("" ,release_a_test)# 对释放a键做相应
root.mainloop()
在绿色区域监听鼠标键盘的操作
lambda
表达式和属性绑定(command
方式)# lambda表达式 详解
# lambda 定义的匿名函数也有输入、 也有输出, 只是没有名
# 字。 语法格式如下:
# lambda 参数值列表: 表达式
# 参数值列表即为输入。
# 表达式计算的结构即为输出。
# 传入 参数
# coding=utf-8
# 测试command属性绑定事件,测试lambda表达式帮助传参
# 使用 lambda 帮助 command 属性绑定时传参
from tkinter import *
root = Tk();root.geometry("270x50")
def mouseTest1():
print("command方式,简单情况:不涉及获取event对象,可以使用")
def mouseTest2(a,b):
print("a={0},b={1}".format(a,b))
Button(root, text="测试command1",
command=mouseTest1).pack(side="left")
# 这种方式适合 不获取 event 对象的方式
Button(root, text="测试command2",
command=lambda: mouseTest2("onepis", "hasaki")).pack(side="left")
# lambda 方式 传入方法 函数 传递参数
root.mainloop()
# 多种事件 绑定方式汇总
# coding=utf-8
# 多种事件绑定方式汇总
# 组件对象的绑定
# 1. 通过 command 属性绑定(适合简单不需获取 event 对象)
# Button(root,text=”登录”,command=login)
# 2. 通过 bind()方法绑定(适合需要获取 event 对象)
# c1 = Canvas(); c1.bind(“”,drawLine)
# · 组件类的绑定
# 调用对象的 bind_class 函数, 将该组件类所有的组件绑定事件:
# w.bind_class(“Widget”,”event”,eventhanler)
# 比如: btn01.bind_class(“Button”,””,func)
from tkinter import *
root = Tk();root.geometry("270x30")
def mouseTest1(event):
print("bind()方式绑定,可以获取event对象")
print(event.widget)
def mouseTest2(a, b):
print("a={0},b={1}".format(a, b))
print("command方式绑定,不能直接获取event对象")
def mouseTest3(event):
print("右键单击事件,绑定给所有按钮啦!!")
print(event.widget)
b1 = Button(root, text="测试bind()绑定")
b1.pack(side="left")
# bind方式绑定事件 2 是滚轮按钮
b1.bind("" , mouseTest1)
# command属性直接绑定事件
b2 = Button(root, text="测试command2",
command=lambda: mouseTest2("onepis", "hasaki"))
b2.pack(side="left")
# 给所有Button按钮都绑定右键单击事件
b1.bind_class("Button","" , mouseTest3)
root.mainloop()
OptionMenu
选择项(下拉框)# OptionMenu 选择项
# OptionMenu(选择项)用来做多选一, 选中的项会在顶部显
# 示。
"""optionmenu的使用测试"""
from tkinter import *
root = Tk(); root.geometry("200x100")
v = StringVar(root)
v.set("the Yamateh")
om = OptionMenu(root, v, "onepis", "hasaki", "yaphets")
om["width"] = 10
om.pack()
def test1():
print("perfect is shit :", v.get())
# v.set("尚学堂") # 直接修改了optionmenu中选中的值
Button(root, text="确定", command=test1).pack()
root.mainloop()
scale
滑块使用"""scale滑块的使用测试"""
# Scale 移动滑块
# Scale(移动滑块)用于在指定的数值区间, 通过滑块的移动来
# 选择值
from tkinter import *
root = Tk();root.geometry("400x150")
def test1(value):
print("滑块的值:",value)
newFont = ("宋体",value)
a.config(font=newFont)
# 设置字体 字体为宋体 字号 为滑块的值
s1 = Scale(root,from_=10,to=50,length=200,tickinterval=5,orient=HORIZONTAL,command=test1)
# length 长度 ,宽度的意思 orient=HORIZONTAL 默认是垂直的 ,现在是 水平的
# from_=10 开始 to 结束 tickinterval=5 相当于步长 command 调用方法 test1 也就是事件
s1.pack()
a = Label(root,text="onepeace",width=10,height=1,bg="black",fg="white")
a.pack()
root.mainloop()
colorchooser
颜色选择框# 颜色选择框
"""askcolor颜色选择框的测试,改变背景色"""
from tkinter import *
from tkinter.colorchooser import *
root = Tk();root.geometry("400x150")
def test1():
s1 = askcolor(color="red", title="选择背景色")
print(s1)
# 前三位是 rgb 颜色 表示法
# 第二个是 16进制码 表示
# s1的值是:((0.0, 0.0, 255.99609375), '#0000ff')
root.config(background=s1[1])
# 设置颜色
Button(root,text="选择背景色",command=test1).pack()
root.mainloop()
filedialog
文件对话框 """文件对话框获取文件"""
# 文件对话框帮助我们实现可视化的操作目录、 操作文件。 最
# 后, 将文件、 目录的信息传入到程序中。
from tkinter import *
from tkinter.filedialog import *
root = Tk();root.geometry("400x100")
def test1():
f = askopenfilename(title="上传文件",
initialdir="f:",filetypes=[("视频文件",".mp4")])
# 实现文件的过滤filetypes
# initialdir="f:" 初始化的打开目录
# print(f)
show["text"]=f
Button(root,text="选择编辑的视频文件",command=test1).pack()
show = Label(root,width=40,height=3,bg="green")
show.pack()
root.mainloop()
#coding=utf-8
"""文件对话框获取文件"""
# 文件对话框帮助我们实现可视化的操作目录、 操作文件。 最
# 后, 将文件、 目录的信息传入到程序中。
from tkinter import *
from tkinter.filedialog import *
root = Tk();root.geometry("400x500")
def test1():
# -filetypes, -initialdir, -initialfile, -multiple, -parent, -title, or -typevariable
# with askopenfile(title="上传文件",
# initialdir="d:",filetypes=[("文本文件",".txt")]) as f:
# 如果上面的代码 读取 文档 报编码错误。那么 可以使用下面这个函数
with open(askopenfilename(), encoding='utf-8') as f:
show["text"]=f.read()
# 读取文件
Button(root,text="选择读取的文本文件",command=test1).pack()
show = Label(root,width=400,height=300,bg="gray")
show.pack()
root.mainloop()
askinteger
简单输入框"""简单输入对话框"""
from tkinter.simpledialog import *
root = Tk();root.geometry("400x100")
def test1():
a = askinteger(title="输入年龄",prompt="请输入年龄",initialvalue=18,minvalue=1,maxvalue=150)
# askstring、askfloat框使用方式一样
# 简单对话框.prompt 提问
# initialvalue=18 默认值
show["text"]=a
Button(root,text="hey,how old are you?",command=test1).pack()
show = Label(root,width=40,height=3,bg="green")
show.pack()
root.mainloop()
messagebox
消息框# 消息框
# 如果遇到较复杂的 界面
# 推荐使用 wxpython
# 和 PyQt 模块
"""通用消息框"""
from tkinter import *
from tkinter.messagebox import *
root = Tk();root.geometry("400x100")
a1 = showinfo(title="程序员",message="the perfect is shit\n大多数人都生活在平静的绝望之中,行将就木之际,还未唱出心底的生命之歌!\n在这个世界上,取得成功的人是那些努力寻找他们想要的机会的人,如果找不到机会,那就创造机会!\n所谓勇敢并不意味着,无所畏惧,无所畏惧其实是一种心理疾病!\n要胆大妄为,要标新立异,要不切实际,要追求一切能够将意义和富于想象力的美好前景结合起来的东西,并以此挑战那些不敢越雷池半步的人,平庸的物种,及平凡普通的奴隶!")
print(a1)
root.mainloop()
Menu
菜单# 菜单
#coding=utf-8
#记事本软件,练习主菜单的设计
from tkinter.filedialog import *
root = Tk();root.geometry("400x400")
#创建主菜单栏
menubar = Menu(root)
#创建子菜单
menuFile = Menu(menubar)
menuEdit = Menu(menubar)
menuHelp = Menu(menubar)
#将子菜单加入到主菜单栏
menubar.add_cascade(label="文件(F)",menu=menuFile)
menubar.add_cascade(label="编辑(E)",menu=menuEdit)
menubar.add_cascade(label="帮助(H)",menu=menuHelp)
filename = ""
def openfile():
global filename
w1.delete('1.0', 'end') # 先把Text控件中的内容清空
# with askopenfile(title="打开文件") as f: # 如果编码错误,请使用 askopenfilename
# content = f.read()
# w1.insert(INSERT, content)
# filename = f.name
# print(f.name)
with open(askopenfilename(), encoding='utf-8') as f:
content = f.read()
w1.insert(INSERT, content)
filename = f.name
print(f.name)
def savefile():
with open(filename, "w") as f:
content = w1.get(1.0, END)
f.write(content)
def exit():
root.quit()
# 添加菜单项
menuFile.add_command(label="打开", accelerator="ctrl+o", command=openfile)
menuFile.add_command(label="保存", command=savefile)
menuFile.add_separator() # 添加分割线
menuFile.add_command(label="退出", command=exit)
# 将主菜单栏加到根窗口
root["menu"] = menubar
w1 = Text(root, width=50, height=30)
w1.pack()
root.mainloop()
"""开发记事本软件的菜单"""
from tkinter.filedialog import *
from tkinter.colorchooser import *
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.textpad = None # textpad表示Text文本框对象
self.pack()
self.createWidget()
def createWidget(self):
# 创建主菜单栏
menubar = Menu(root)
# 创建子菜单
menuFile = Menu(menubar)
menuEdit = Menu(menubar)
menuHelp = Menu(menubar)
# 将子菜单加入到主菜单栏
menubar.add_cascade(label="文件(F)", menu=menuFile)
menubar.add_cascade(label="编辑(E)", menu=menuEdit)
menubar.add_cascade(label="帮助(H)", menu=menuHelp)
# 添加菜单项
menuFile.add_command(label="新建", accelerator="ctrl+n", command=self.test)
menuFile.add_command(label="打开", accelerator="ctrl+o", command=self.test)
menuFile.add_command(label="保存", accelerator="ctrl+s",command=self.test)
menuFile.add_separator() # 添加分割线
menuFile.add_command(label="退出", accelerator="ctrl+q",command=self.test)
# 将主菜单栏加到根窗口
root["menu"] = menubar
#文本编辑区
self.textpad = Text(root, width=50, height=30)
self.textpad.pack()
# 创建上下菜单
self.contextMenu = Menu(root)
self.contextMenu.add_command(label="背景颜色", command=self.test)
#为右键绑定事件
root.bind("" ,self.createContextMenu)
def test(self):
pass
def createContextMenu(self,event):
# 菜单在鼠标右键单击的坐标处显示
self.contextMenu.post(event.x_root, event.y_root)
if __name__ == '__main__':
root = Tk()
root.geometry("450x300+200+300")
root.title("程序员的简易记事本")
app = Application(master=root)
root.mainloop()
# 相对比较完整的记事本软件开发
from tkinter.filedialog import *
from tkinter.colorchooser import *
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.textpad = None # textpad表示Text文本框对象
self.pack()
self.createWidget()
def createWidget(self):
# 创建主菜单栏
menubar = Menu(root)
# 创建子菜单
menuFile = Menu(menubar)
menuEdit = Menu(menubar)
menuHelp = Menu(menubar)
# 将子菜单加入到主菜单栏
menubar.add_cascade(label="文件(F)", menu=menuFile)
menubar.add_cascade(label="编辑(E)", menu=menuEdit)
menubar.add_cascade(label="帮助(H)", menu=menuHelp)
# 添加菜单项
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)
# 将主菜单栏加到根窗口
root["menu"] = menubar
# 增加快捷键的处理
root.bind("" ,lambda event:self.newfile())
root.bind("" ,lambda event:self.openfile())
root.bind("" ,lambda event:self.savefile())
root.bind("" ,lambda event:self.exit())
#文本编辑区
self.textpad = Text(root, width=50, height=30)
self.textpad.pack()
# 创建上下菜单
self.contextMenu = Menu(root)
self.contextMenu.add_command(label="背景颜色", command=self.openAskColor)
#为右键绑定事件
root.bind("" ,self.createContextMenu)
def newfile(self):
self.textpad.delete("1.0", "end") # 把text控件中所有的内容清空
self.filename= asksaveasfilename(title="另存为",initialfile="未命名.txt",
filetypes=[("文本文档","*.txt")],
defaultextension=".txt")
self.savefile()
def openfile(self):
self.textpad.delete("1.0","end") # 把text控件中所有的内容清空
with askopenfile(title="打开文本文件") as f:
self.textpad.insert(INSERT,f.read())
self.filename = f.name
def savefile(self):
with open(self.filename,"w") as f:
c = self.textpad.get(1.0,END)
f.write(c)
def exit(self):
root.quit()
def openAskColor(self):
s1 = askcolor(color="red",title="选择背景色")
self.textpad.config(bg=s1[1])
def createContextMenu(self,event):
# 菜单在鼠标右键单击的坐标处显示
self.contextMenu.post(event.x_root, event.y_root)
if __name__ == '__main__':
root = Tk()
root.geometry("450x300+200+300")
root.title("程序员的简易记事本")
app = Application(master=root)
root.mainloop()
pyinstaller
)pyinstaller 文档(英文))
# pyinstaller打包文件
# pyinstaller -F xxxx.py
# 【注】 相关参数如下:
# --icon=图标路径(pyinstaller -F --icon=my.ico XXXX.py)
# -F 打包成一个 exe 文件
# -w 使用窗口, 无控制台
# -c 使用控制台, 无窗口
# -D 创建一个目录, 里面包含 exe 以及其他一些依赖性文件
# 开发一款简单的画图软件, 包含如下功能:
# 1. 画笔
# 2. 矩形/椭圆绘制
# 3. 清屏
# 4. 橡皮擦
# 5. 直线/带箭头的直线
# 6. 修改画笔颜色、 背景颜色
"""开发画图软件的菜单
"""
from tkinter.filedialog import *
from tkinter.colorchooser import *
#窗口的宽度和高度
win_width=900
win_height=450
class Application(Frame):
def __init__(self, master=None,bgcolor="#000000"):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.bgcolor=bgcolor
self.x = 0
self.y = 0
self.fgcolor = "#ff0000"
self.lastDraw = 0 # 表示最后绘制的图形的id
self.startDrawFlag = False
self.pack()
self.createWidget()
def createWidget(self):
# 创建绘图区
self.drawpad = Canvas(root,width=win_width,height=win_height*0.9,bg=self.bgcolor)
self.drawpad.pack()
#创建按钮
btn_start = Button(root,text="开始",name="start")
btn_start.pack(side="left",padx="10")
btn_pen = Button(root,text="画笔",name="pen")
btn_pen.pack(side="left",padx="10")
btn_rect = Button(root,text="矩形",name="rect")
btn_rect.pack(side="left",padx="10")
btn_clear = Button(root,text="清屏",name="clear")
btn_clear.pack(side="left",padx="10")
btn_erasor = Button(root,text="橡皮擦",name="erasor")
btn_erasor.pack(side="left",padx="10")
btn_line = Button(root,text="直线",name="line")
btn_line.pack(side="left",padx="10")
btn_lineArrow = Button(root,text="箭头直线",name="lineArrow")
btn_lineArrow.pack(side="left",padx="10")
btn_color = Button(root,text="颜色",name="color")
btn_color.pack(side="left",padx="10")
#事件处理
btn_pen.bind_class("Button","<1>",self.eventManager)
self.drawpad.bind("" ,self.stopDraw)
#增加颜色切换的快捷键
root.bind("" ,self.kuaijiejian)
root.bind("" ,self.kuaijiejian)
root.bind("" ,self.kuaijiejian)
def eventManager(self,event):
name = event.widget.winfo_name()
print(name)
if name=="line":
self.drawpad.bind("" ,self.myline)
elif name=="lineArrow":
self.drawpad.bind("" ,self.mylineArrow)
elif name=="rect":
self.drawpad.bind("" ,self.myRect)
elif name=="pen":
self.drawpad.bind("" ,self.myPen)
elif name=="erasor":
self.drawpad.bind("" ,self.myErasor)
elif name=="clear":
self.drawpad.delete("all")
elif name=="color":
c = askcolor(color=self.fgcolor,title="选择画笔颜色")
#[(255,0,0),"#ff0000"]
self.fgcolor = c[1]
# 修改颜色
def stopDraw(self,event):
self.startDrawFlag = False
self.lastDraw = 0
# 赋值 为 0 发生事件的时候,然后再myline 开始画的时候就删除不到 了
def startDraw(self,event):
self.drawpad.delete(self.lastDraw)
if not self.startDrawFlag:
self.startDrawFlag = True
self.x = event.x
self.y = event.y
def myline(self,event):
self.startDraw(event)
self.lastDraw = self.drawpad.create_line(self.x,self.y,event.x,event.y,fill=self.fgcolor)
def mylineArrow(self,event):
self.startDraw(event)
self.lastDraw = self.drawpad.create_line(self.x,self.y,event.x,event.y,arrow=LAST,fill=self.fgcolor)
def myRect(self,event):
self.startDraw(event)
self.lastDraw = self.drawpad.create_rectangle(self.x,self.y,event.x,event.y,outline=self.fgcolor)
def myPen(self,event):
self.startDraw(event)
self.drawpad.create_line(self.x,self.y,event.x,event.y,fill=self.fgcolor)
#这里不指定给 lastdraw 就可以实现多次 画笔 画线
self.x = event.x
self.y = event.y
def myErasor(self,event):
self.startDraw(event)
self.drawpad.create_rectangle(event.x-4,event.y-4,event.x+4,event.y+4,fill=self.bgcolor)
self.x = event.x
self.y = event.y# 可删除 直接大矩形 套接 删除颜色
def kuaijiejian(self,event):
if event.char =="r":
self.fgcolor = "#ff0000"
elif event.char =="g":
self.fgcolor = "#00ff00"
elif event.char =="y":
self.fgcolor = "#ffff00"
if __name__ == '__main__':
root = Tk()
root.geometry(str(win_width)+"x"+str(win_height)+"+200+300")
root.title("onepis的画图软件")
app = Application(master=root)
root.mainloop()