1. Tkinter
tkinter (Tk interface)是python 的标准 GUI 库,支持跨平台的GUI 程序开发
Tkinter适合小型的GUI程序编写,也特别适合初学者学习GUI 编程。本书以 Tkinter为核心进行讲解。
2.wxpython
wxpython是比较流行的GUI 库,适合大型应用程序开发,功能强于 Tkinter
整体设计框架类似于MFC(Microsoft Foundation Classes 微软基础类库
3,PyQT是QT的工具包,QT是开源的GUI
基于 tkinter 模块创建 GUI 程序包含如下 4 个核心步骤:
1,创建应用程序主窗口对象(也称:根窗口)
(1)通过类 Tk 的无参构造函数
1 2 3 4
| from tkinter import*
root = Tk()
|
2,在主窗口中,添加各种可视化组件,比如:按钮(Button),文本框(Label)等
1 2 3
| btn01 = Button(root)
btn01["text"] ="点我就送花"
|
3,通过几何布局管理器,管理组件的大小和位置
4**,事件处理**
(1)通过绑定事件处理程序,响应用户操作所触发的事件(比如:单击,双击等)
1 2 3 4 5 6
| def songhua(e):
messagebox.showinfo("Message","送你一朵玫瑰花") print("送你99朵玫瑰花")
btn01.bind("<Button-1>",songhua)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from tkinter import * from tkinter import messagebox root = Tk()
btn01 = Button(root) btn01["text"] = "点我就送花"
btn01.pack()
def songhua(e): # e就是事件对象 messagebox.showinfo("Message", "送你一朵玫瑰花") print("送你99朵玫瑰花")
btn01.bind("<Button-1>", songhua)
root.mainloop() #调用组件的mainloop()方法,进入事件循环
|
可变参数
指的是”可变数量的参数“,分两种情况
1,*param(一个星号),将多个参数收集到一个”元组“对象中
2,**param(两个星号),将多个参数收集到一个”字典“对象中
特殊方法和运算符重载
python 的 运算符实际上是通过调用对象的特殊方法实现的,比如:
1 2 3 4 5 6
| a = 20 b = 30 c = a+b d = a.__add__(b) print("c=",c) print("d=",d)
|
Entry 单行文本框
Entry 用来接收一行字符串的控件。如果用户输入的文字长度长于 Entry 控件的宽度时,文字会自动向后滚动。如果想输入多行文本,需要使用 Text 控件
Text 多行文本框
主要用于显示多行文本,还可以显示网页链接,图片,HTML 页面,甚至 CSS 样式表,添加组件等。因此,也常 被当做简单的文本处理器,文本编辑器或者网页浏览器来使用。比如 IDLE 就是 Text 组件构成的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| 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="gray") self.w1.pack()
self.w1.insert(1.0, "0123456789\nabcdefg") self.w1.insert(2.3, "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦\n")
Button(self, text="重复插入文本",command=self.insertText).pack(side="left") Button(self, text="返回文本", command=self.returnText).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): self.w1.insert(INSERT, 'Gaoqi') self.w1.insert(END, '[sxt]')
def returnText(self): print(self.w1.get(1.2, 1.6)) self.w1.insert(1.8, "gaoqi") print("所有文本内容: \n"+self.w1.get(1.0, END))
def addWidget(self): b1 = Button(self.w1, 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) 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", "<Button-1>", 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()
|
以上代码存在问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| 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): # 修改这里:将父容器从 root 改为 self self.w1 = Text(self, width=40, height=12, bg="gray") # 改为 self self.w1.pack()
self.w1.insert(1.0, "0123456789\nabcdefg") self.w1.insert(2.3, "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦\n")
Button(self, text="重复插入文本", command=self.insertText).pack(side="left") Button(self, text="返回文本", command=self.returnText).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): self.w1.insert(INSERT, 'Gaoqi') self.w1.insert(END, '[sxt]')
def returnText(self): print(self.w1.get(1.2, 1.6)) self.w1.insert(1.8, "gaoqi") print("所有文本内容: \n"+self.w1.get(1.0, END))
def addWidget(self): b1 = Button(self.w1, 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百度,搜一下就知道了") # 添加 good tag self.w1.tag_add("good", 1.0, 1.9) self.w1.tag_config("good", background="yellow", foreground="red") # 添加 baidu tag - 这里索引要正确 self.w1.tag_add("baidu", 4.0, 4.2) # "百度"两个字 self.w1.tag_config("baidu", underline=True, foreground="blue") self.w1.tag_bind("baidu", "<Button-1>", 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 可以显示文本,也可以显示图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| """测试Radiobutton组件的基本用法,使用面向对象的方式"""
from tkinter import * from tkinter import messagebox
class Application(Frame): def __init__(self, master=None): super().__init__(master)
self.master = master self.pack() self.creareWidget()
def createWidget(self): self.v = StringVar(); self.v.set("F")
self.r1 = Radiobutton(root, text="男性", value="M", variable=self.v) self.r2 = Radiobutton(root, text="女性", value="F", variable=self.v)
self.r1.pack(side="left");self.r2.pack(side="left")
Button(root,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()
|
以上代码也存在一点问题,不能执行
父容器选择:
使用 self 作为父容器
因为 Application 是继承自 Frame 的,所有在类内部创建的组件应该放在这个 Frame 内部,而不是直接放在根窗口 root 上。
代码结构说明:
- 使用
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">self</font> 作为父容器可以让所有组件都在 <font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">Application</font> 的 Frame 内部,这样布局更清晰,也更符合面向对象的设计。
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">self.v = StringVar()</font> 创建了一个 StringVar 变量,用于绑定到 RadioButton 组件。当选择不同的 RadioButton 时,<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">self.v</font> 的值会自动更新。
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">self.v.set("F")</font> 设置了默认选中"女性"选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| """测试Radiobutton组件的基本用法,使用面向对象的方式"""
from tkinter import * from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None): super().__init__(master) self.master = master self.pack() self.createWidget() # 这里拼写错误:creareWidget -> createWidget
def createWidget(self): self.v = StringVar() self.v.set("F")
# 修改这里:将父容器从 root 改为 self self.r1 = Radiobutton(self, text="男性", value="M", variable=self.v) 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 可以显示文本,也可以显示图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| """测试Radiobutton组件的基本用法,使用面向对象的方式"""
from tkinter import * from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None): super().__init__(master) self.master = master self.pack() self.createWidget()
def createWidget(self): self.codeHobby = IntVar(); self.videHobby = IntVar()
print(self.codeHobby.get()) self.c1 = Checkbutton(self, text="敲代码", variable=self.codeHobby, onvalue=1, offvalue=0) self.c2 = Checkbutton(self, text="看视频", variable=self.videHobby, 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.videHobby.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 是一个矩形区域,可以放置图形,图像,组件等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| """测试canvas组件的基本用法,使用面向对象的方式"""
from tkinter import * import random
class Application(Frame):
def __init__(self, master=None): super().__init__(master) self.master = master self.pack() self.createWidget()
def createWidget(self): # 创建画布 self.canvas = Canvas(self, width=300, height=200, bg="lightgreen") self.canvas.pack() # 画线 line = self.canvas.create_line(10, 10, 30, 20, 40, 50) # 画矩形 rect = self.canvas.create_rectangle(50, 50, 100, 100, fill="red") # 画椭圆 oval = self.canvas.create_oval(150, 50, 200, 100, fill="blue")
# 尝试加载图片(如果存在) try: self.photo = PhotoImage(file="imgs/logo.gif") self.canvas.create_image(150, 170, image=self.photo) except Exception as e: print(f"无法加载图片: {e}") # 如果没有图片,画一个替代图形 self.canvas.create_rectangle(130, 150, 170, 190, fill="yellow") self.canvas.create_text(150, 170, text="图片")
# 按钮 Button(self, text="画10个矩形", command=self.draw50Recg).pack(side="left")
def draw50Recg(self): colors = ["red", "blue", "green", "yellow", "orange", "purple", "pink"] for i in range(0, 10): x1 = random.randrange(int(self.canvas["width"])) y1 = random.randrange(int(self.canvas["height"])) x2 = x1 + random.randrange(20, 50) y2 = y1 + random.randrange(20, 50) color = random.choice(colors) self.canvas.create_rectangle(x1, y1, x2, y2, fill=color, outline="black", width=2)
if __name__ == '__main__': root = Tk() root.title("Canvas测试") root.geometry("400x300+200+300") # 修正窗口大小 app = Application(master=root) root.mainloop()
|
布局管理器
一个 GUI 应用程序必然有大量的组件,需要使用 tkinter 提供的布局管理器帮助我们组织,管理在父组件中子组件的布局方式,tkinter 提供了三种管理器:pack,grid,place
grid 布局 管理器
grid 表格布局,采用表格结构组织组件,子组件的位置由行和列的单元格来确定,并且可以跨行和跨列,从而实现复杂的布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| """计算器软件界面的设计"""
from tkinter import * import random
class Application(Frame):
def __init__(self, master=None): super().__init__(master) 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): Button(self, text=c,width=2).grid(row=rindex+1,column=cindex,sticky=NSEW)
if __name__ == '__main__': root = Tk() root.geometry("200x200+200+300") app = Application(master=root) root.mainloop()
|
pack 布局管理器
pack 按照组件的创建顺序将子组件添加到父组件中,按照垂直或者水平的方向自然排布,如果不指定任何选项,默认在父组件中自顶向下垂直添加组件
pack 是代码量最少,最简单的一种,可以用于快速生成界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #coding=utf-8 #测试pack布局管理
from tkinter import *
root = Tk() root.geometry("700x200")
f1 = Frame(root) f1.pack() f2 = Frame(root);f2.pack()
btnText = ("流行风", "中国风","日本风","重金属","轻音乐")
for txt in btnText: Button(f1,text=txt).pack(side="left",padx="10")
for i in range(1,20): Label(f2,width=5,height=10,borderwidth=1,relief="solid", bg="black" if i%2==0 else "white").pack(side="left",padx=2)
root.mainloop()
|
place 布局管理器
place 布局管理器可以通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景
1 2 3 4 5 6 7 8 9 10 11 12 13
| # coding=utf-8 from tkinter import *
root = Tk() root.geometry("500x300")
f1 = Frame(root,width=200,height=200,bg="green") f1.place(x=30,y=30)
Button(root,text="尚学堂").place(relx=0.2, x=100,y=20,relheight=0.5,relwidth=0.2) Button(f1,text="百战程序员").place(relx=0.6,rely=0.7) Button(f1,text="高琦老师").place(relx=0.5,rely=0.2) root.mainloop()
|
lambda 表达式–事件传参应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # coding=utf-8 # 测试command属性绑定事件,测试lambda表达式帮助传参 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")
Button(root, text="测试command2", command=lambda:mouseTest2("gaoqi", "xixi")).pack(side="left")
root.mainloop()
|