python笔记14-GUI编程

本文围绕Python图形用户界面编程展开,介绍了常用GUI库,如Tk interface等。阐述编程核心步骤,包括创建主窗口、添加组件、布局管理和事件处理等。还列举多种组件及用法,如标签、按钮等,并给出记事本和画图小项目开发示例,最后说明程序打包方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GUI(Graphics User Interface)图形用户界面编程

1.常用的GUI库

  1. Tkinter
    tkinter(Tk interface)是 Python 的标准 GUI 库,支持跨平台的 GUI 程序开发。tkinter适合小型的 GUI 程序编写,也特别适合初学者学习GUI 编程。
  2. wxPython
    wxPython 是比较流行的 GUI 库,适合大型应用程序开发,功能强tkinter,整体设计框架类似于 MFC(Microsoft Foundation Classes 微软基础类库)。
  3. PyQT
    Qt 是一种开源的 GUI 库,适合大型 GUI 程序开发,PyQTQt 工具包标准的 Python 实现。我们也可以使用 Qt Desginer 界面设计器快速开发 GUI 应用程序

2.tkinter模块:

官方网址

3.GUI 编程的核心步骤和第一个 GUI 程序

  1. 创建应用程序主窗口对象(也称根窗口)
    通过类Tk的无参构造函数
    from tkinter import *
    root = Tk()
    
  2. 在主窗口中添加各种可视化组件(比如:按钮Button,文本框Lable)
    btn01 = Button(root)
    btn01["text"] = "hello world"
    
  3. 通过几何布局管理器,管理组件的位置和大小
    btn01.pack()
    
  4. 事件处理
    通过绑定事件处理程序,响应用户操作所触发的事件(比如:单击,双击)
    def hello(e):
    	messagebox.showinfo("Message","hello world")
    	print("你好,世界")
    	btn01.bind("<Button-1>",hello)
    
  5. 上述整体代码组合示例:
    from tkinter import *
    from tkinter import messagebox
    
    
    root = Tk()
    btn01 = Button(root)
    btn01["text"] = "hello world"
    btn01.pack()
    
    
    def hello(e):  # 记得这里一定需要参数
        messagebox.showinfo("Message", "hello world")
        print('你好,世界!')
    
    
    btn01.bind("<Button-1>", hello)
    
    
    root.mainloop()
    
    

4.tkinter主窗口

通过 geometry(‘wxh±x±y’)进行设置。w 为宽度,h 为高度。+x 表示距屏幕左边的距离;-x 表示距屏幕右边的距离;+y 表示距屏幕上边的距离;-y 表示距屏幕下边的距离。

5.GUI编程整体描述

  1. TkinterGUI 组件关系图
    在这里插入图片描述

  2. MiscWm
    TkinterGUI 组件有两个根父类,它们都直接继承了 object 类:
    Misc:它是所有组件的根父类。
    Wm:它主要提供了一些与窗口管理器通信的功能函数。

  3. Tk
    MiscWm 派生出子类 Tk,它代表应用程序的主窗口。一般应用程序都需要直接或间接使用 Tk

  4. PackPlaceGrid
    PackPlaceGrid 是布局管理器。布局管理器管理组件的:大小、位置。通过布局管理器可以将容器中的组件实现合理的排布。

  5. BaseWidget
    BaseWidget 是所有组件的父类

  6. Widget
    Widget 是所有组件类的父类。Widget 一共有四个父类:BaseWidgetPackGridPlace。意味着,所有 GUI 组件同时具备这四个父类的属性和方法。

7.常用组件汇总

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

8.GUI应用程序经典写法

本节程序也是 GUI 应用程序编写的一个主要结构,采用了面向对象的方式,更加合理的组织代码。
通过类 Application 组织整个 GUI 程序,类 Application 继承了 Frame 及通过继承拥有了父类的特性。通过构造函数__init__()初始化窗口中的对象,通过 createWidgets()方法创建窗口中的对象。
Frame 框架是一个 tkinter 组件,表示一个矩形的区域。Frame 一般作为容器使用,可以放置其他组件,从而实现复杂的布局。

"""测试一个经典的面向对象写法的GUI"""
from tkinter import *
from tkinter import messagebox


class Application(Frame):
    """一个经典的GUI程序的写法"""

    def __init__(self, master=None):  # 将master作为Application的属性,后面传参root,间接调用root将Application与root绑定
        super().__init__(master)  # super().__init__(master)实际上已经将master作为self的一个属性了 后面传参root将Frame与root绑定
        # Frame.__init__(self,master)
        self.master = master  # 这应该是为了让代码更加方便理解,所以多一步初始化。其实就是可读性,格式的问题,有没有都一样,但官方教程这么写。
        self.pack()  # Application类的实例化对象其实也是一个组件,所以要调用布局管理器
        self.createWidget()

    def createWidget(self):
        """创建组件"""
        self.btn01 = Button(self)  # 在这里Button是一个按钮类,它需要继承我们的Application类来显示,所以传入self指我们实例化出来的Application类
        self.btn01['text'] = '点击送花'
        self.btn01.pack()
        self.btn01['command'] = self.songhua
        # 创建一个退出按钮
        self.btnQuit = Button(self, text='退出', command=root.destroy)  # 证明绑定的master是root,将root换为self.master,\
        # 看命令是否成功,测试结果成功,代码解释合理!
        self.btnQuit.pack()

    def songhua(self):
        messagebox.showinfo('送花', '送你一朵花')


if __name__ == '__main__':
    root = Tk()  # 每一个应用程序都要直接或间接的调用它
    root.geometry("400x100+200+300")
    root.title("一个经典的GUI测试")
    app = Application(master=root)

    root.mainloop()

在这里插入图片描述

9.Label标签

  1. 简介:
    Label(标签)主要用于显示文本信息,也可以显示图像
  2. 属性:
    Label(标签)有这样一些常见属性:
    1. width,height
      用于指定区域大小,如果显示是文本,则以单个英文字符大小为单位(一个S字宽度占2 个字符位置,高度和英文字符一样);如果显示是图像,则以像素为单位。默认值是根据具体显示的内容动态调整。
    2. font
      指定字体和字体大小,如:font = (font_name,size)
    3. image:
      显示在 Label 上的图像,目前 tkinter 只支持 gif 格式。
    4. fgbg
      fg(foreground):前景色、bg(background):背景色
    5. justify
      针对多行文字的对齐,可设置 justify 属性,可选值"left", "center" or "right"
  3. 代码示例:
    # 这里的代码完全是将面向对象的代码框架拿来填补的,所有的不理解请返回去看面向对象的代码框架解释
    from tkinter import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.creatWidget()
    
        def creatWidget(self):
            """创建组件"""
            # 创建不同属性的Label组件
            self.label01 = Label(self, height=2, width=10, text='这是一个标签',
                                 bg='black', fg='white')  # 注意除了self其它属性顺序没有定义,可以从它的源码中看出来,ctrl+点击Label,
            # 但一般按照内容,大小,颜色来传
            self.label02 = Label(self, height=2, width=20, text='这是一个标签',
                                 bg='blue', fg='white', font=('黑体', 30))
            self.label01.pack()
            self.label02.pack()
            # Label显示图片
            global photo  # 如果不声明为全局变量,调用一次就销毁,而mainloop()一直在循环,需要一直调用
            photo = PhotoImage(file='imgs/baidu.gif')
            self.label03 = Label(self, image=photo,)
            self.label03.pack()
    
            # Label显示多行文本
            self.label04 = Label(self, text='人生如洗牌\n我们抽老千', borderwidth=1, relief='solid', justify='right')
            self.label04.pack()
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("800x500+200+300")
        root.title("Label测试")
        a = Application(master=root)
    
        root.mainloop()
    
    

运行结果
在这里插入图片描述

10.Options 选项详解

通过学习 Label 组件,我们发现可以通过 Options 设置组件的属性,从而控制组件的各种状态。比如:宽度、高度、颜色、位置等等。我们可以通过三种方式设置 Options 选项,这在各种 GUI 组件中用法都一致。

  1. 创建对象时,使用可变参数
    fred = Button(self, fg="red", bg="blue")

  2. 创建对象后,使用字典索引方式
    fred["fg"] = "red"
    fred["bg"] = "blue"

  3. 创建对象后,使用 config()方法
    fred.config(fg="red", bg="blue")

  4. 如何查看组件的 Options 选项:

    1. 可以通过打印 config()方法的返回值,查看 Options 选项
      print(fred.config())
    2. 通过在 IDE 中,点击组件对象的构造方法,进入到方法内观察
      在这里插入图片描述
  5. 关于“standard options 标准选项”和“widget-specific options 组件特定选 项”。我们将常见的选项汇总如下:

选项名(别名)含义
activebackground指定组件处于激活状态时的背景色
activeforeground指定组件处于激活状态时的前景色
anchor指定组件内的信息(比如文本或图片)在组件中如何显示(当所在组件比信息大时,可以看出效果)。必须为下面的值之一:N、NE、E、SE、S、SW、W、NW 或 CENTER。比如 NW(NorthWest)指定将信息显示在组件左上角
bitmap指定在组件上显示该选项指定的位图,该选项值可以是 Tk_GetBitmap接收的任何形式的位图。位图的显示方式受 anchorjustify 选项的影响。如果同时指定了 bitmaptext,那么 bitmap 覆盖文本;如果同时指定了 bitmapimage,那么 image 覆盖 bitmap
borderwidth指定组件正常显示时的 3D 边框的宽度,该值可以是Tk_GetPixels 接收的任何格式
cursor指定光标在组件上的样式。该值可以是 Tk_GetCursors 接受的任何格式
command指定按组件关联的命令方法,该方法通常在鼠标离开组件时被触发调用
disabledforeground指定组件处于禁用状态时的前景色
font指定组件上显示的文本字体
foreground(fg)指定组件正常显示时的前景色
highlightbackground指定组件在高亮状态下的背景色
highlightcolor指定组件在高亮状态下的前景色
highlightthickness指定组件在高亮状态下的周围方形区域的宽度,该值可以是Tk_GetPixels 接收的任何格式
height指定组件的高度,以 font 选项指定的字体的字符高度为单位,至少为1
image指定组件中显示的图像,如果设置了 image 选项,它将会覆盖 textbitmap选项
justify指定组件内部内容的对齐方式,该选项支持 `LEFT(左对齐)、CENTER(居中对齐)或 RIGHT(右对齐)这三个值
padx指定组件内部在水平方向上两边的空白,该值可以是 Tk_GctPixels 接收的任何格式
pady指定组件内部在垂直方向上两地的空白,该值可以是 Tk_GctPixels 接收的任何格式
relief指定组件的 3D 效果,该选项支持的值包括RAISEDSUNKENFLATRIDGESOLIDGROOVE。该值指出组件内部相对于外部的外观样式,比如 RAISED 表示组件内部相对于外部凸起
selectbackground指定组件在选中状态下的背景色
selectborderwidth指定组件在选中状态下的 3D 边框的宽度,该值可以是 Tk_GetPixels接收的任何格式
selectforeground指定组在选中状态下的前景色
state指定组件的当前状态。该选项支持 NORMAL(正常)、DISABLE(禁用)这两个值
takefocus指定组件在键盘遍历(TabShift+Tab)时是否接收焦点,将该选项设为 1 表示接收焦点;设为 0 表示不接收焦点
text指定组件上显示的文本,文本显示格式由组件本身、anchorjustify 选项决定
textvariable指定一个变量名,GUI 组件负责显示该变量值转换得到的字符串,文本显示格式由组件本身、anchorjustify 选项决定
underline指定为组件文本的第几个字符添加下画线,该选项就相当于为组件绑定了快捷键
width指定组件的宽度,以 font 选项指定的字体的字符高度为单位,至少为1
wraplength对于能支持字符换行的组件,该选项指定每行显示的最大字符数,超过该数量的字符将会转到下行显示
xscrollcommand通常用于将组件的水平滚动改变(包括内容滚动或宽度发生改变)与水平滚动条的 set 方法关联,从而让组件的水平滚动改变传递到水平滚动条
yscrollcommand通常用于将组件的垂直滚动改变(包括内容滚动或高度发生改变)与垂直滚动条的 set 方法关联,从而让组件的垂直滚动改变传递到垂直滚动条

11.Button组件

  1. 简介:
    Button(按钮)用来执行用户的单击操作。Button 可以包含文本,也可以包含图像。按钮被单击后会自动调用对应事件绑定的方法
  2. 代码示例:
    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)  # 关于这里为什么要加master参数
            # 可以在IDE中通过ctrl+单击Frame中查看__init__方法中的参数有master
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            self.btn01 = Button(self, text="登录", width=5, height=2, anchor=CENTER, command=self.login)
            self.btn01.pack()
            global photo
            photo = PhotoImage(file=r'E:\my_python\2.python深入与提高\GUI编程\imgs\baidu.gif', height=200, width=150)
            self.btn02 = Button(root, image=photo, command=self.login)
            self.btn02.pack()
            self.btn02.config(state="disabled")
    
        def login(self):
            messagebox.showinfo("登录信息", "欢迎登录,hello world")
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("200x300+100+300")
        a = Application(root)
        root.mainloop()
    
    

运行结果
在这里插入图片描述

12.Entry单行文本框组件

  1. 简介:
    Entry 用来接收一行字符串的控件。如果用户输入的文字长度长于 Entry 控件的宽度时, 文字会自动向后滚动。如果想输入多行文本, 需要使用 Text 控件
  2. 代码示例(一个简单登录框):
    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master):
            super().__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            self.lab1 = Label(self, text='用户名')
            self.lab1.pack()
            # StringVar变量绑定到指定的组件
            # StringVar变量的值发生变化,组件内容也变化
            # 组件内容发生变化,StringVar变量的值也发生变化
            v1 = StringVar()
            self.eny1 = Entry(self, textvariable=v1)  # textvariable显示v1变量设置好的初始值
            self.eny1.pack()
            v1.set("admin")  # 给v1变量设置初始值
            print(v1.get())  # 通过StringVar变量的get方法获取其设置的值
            print(self.eny1.get())  # 通过Entry组件的get方法获取其中输入的值
            self.lab2 = Label(self, text='密码')
            self.lab2.pack()
            v2 = StringVar()
            self.eny2 = Entry(self, show='*', textvariable=v2)
            self.eny2.pack()
            v2.set(123456)
            self.btn1 = Button(self, text='登录', command=self.login)
            self.btn1.pack()
            # Button(self,text='登录',command=self.login).pack()  # 这是按钮创建的另一种方式,优点是代码简洁;缺点是只能使用按钮一次
    
        def login(self):
            username = self.eny1.get()
            pwd = self.eny2.get()
            print("去数据库比对用户名和密码")
            print("用户名:" + username)
            print("密码:" + pwd)
            if username == 'python' and pwd == '654321':
                messagebox.showinfo(title='show', message='你好,pythonchi!')
            else:
                messagebox.showinfo(title='python', message='登录失败,用户名或密码错误!')
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x130+200+300")
        app = Application(master=root)
        root.mainloop()
    
    

运行结果
在这里插入图片描述

13.Text多行文本框组件

  1. 简介:
    Text(多行文本框)的主要用于显示多行文本,还可以显示网页链接, 图片,HTML 页面, 甚至 CSS 样式表,添加组件等。因此,也常被当做简单的文本处理器、文本编辑器或者网页浏览器来使用。比如 IDLE 就是 Text 组件构成的。
  2. 代码示例
    import webbrowser
    from tkinter import *
    
    
    class Application(Frame):
        def __init__(self, master):
            super().__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            self.t1 = Text(self, width=40, height=12, bg='gray')
            self.t1.pack()
    
            self.t1.insert(1.0, "0123456789\nabcdefghij")
            self.t1.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.addImage).pack(side='left')
            Button(self, text="添加组件", command=self.addWidget).pack(side='left')
            Button(self, text="通过tag精确控制文本", command=self.textTag).pack(side='left')
    
        def insertText(self):
            # INSERT索引表示在光标处插入
            self.t1.insert(INSERT, 'python')
            # END索引表示在最后插入
            self.t1.insert(END, 'python_end')
    
        def returnText(self):
            # Indexes(索引)是用来指向Text组件中文本的位置,Text的组件索引也是对应实际字符之间的位置
            # 核心:行号以1开始,列号以0开始
            print(self.t1.get(1.2, 1.6))  # 测试Indexes索引返回值
            print("所有文本内容:\n" + self.t1.get(1.0, END))
    
        def addImage(self):
            self.photo = PhotoImage(file="imgs/baidu.gif")
            self.t1.image_create(END, image=self.photo)
    
        def addWidget(self):
            b1 = Button(self.t1, text='love_python')  # Button继承的是self.t1组件
            # 在text创建组件的命令
            self.t1.window_create(INSERT, window=b1)
    
        def textTag(self):
            self.t1.delete(1.0, END)
            self.t1.insert(INSERT, '用知识改变现状,命运之轮以动力运转!\n学习python,兴趣促使成功\n百度,百度一下,就能知道!')
            self.t1.tag_add('python', 2.0, 2.8)  # tag_add添加标签name:python可以是任意的名字,就是为了方便设置
            self.t1.tag_config('python', background='yellow', foreground='red')
    
            self.t1.tag_add('百度', 3.0, 3.2)
            self.t1.tag_config('百度', underline=True, foreground='red')
            self.t1.tag_bind('百度', '<Button-1>', self.webshow)
    
        def webshow(self, event):
            webbrowser.open('www.baidu.com')
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x300+300+200")
        app = Application(master=root)
        root.mainloop()
    
    

运行结果:
在这里插入图片描述

14.Radiobutton 单选按钮

  1. 简介:
    Radiobutton 控件用于选择同一组单选按钮中的一个。Radiobutton 可以显示文本,也可以显示图像。
  2. 代码示例:
    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)
            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)  # 继续明确这里的self指的是什么,
            # 这里的self指的是根窗口也就是root,也可以认为指的是我们的实例化类Application,
            # 因为我们继承的是root类,Application中的参数中的master就是等于root
            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())
            print(self.v.get())
    
    
    if __name__ == '__main__':
        root = Tk()
        a = Application(master=root)
        root.mainloop()
    
    

15.Checkbutton复选按钮

  1. 简介:
    Checkbutton 控件用于选择多个按钮的情况。Checkbutton 可以显示文本,也可以显示图像
  2. 代码示例:
    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            self.codeHobby = IntVar()
            self.videoHobby = IntVar()
            print(self.videoHobby.get())  # 默认值是0
            # Checkbutton 未激活的时候默认是0,激活时是1
            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()
    
    

16.canvas画布

  1. 简介:
    canvas(画布)是一个矩形区域,可以放置图形、图像、组件等。
  2. 代码示例:
    """测试 Canvas 组件的基本用法,使用面向对象的方式"""
    import random
    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):
            self.canvas = Canvas(self, width=300, height=200, bg="green")
            self.canvas.pack()
            # 画一条直线
            line = self.canvas.create_line(10, 10, 30, 20, 40, 50)
            # 画一个矩形.
            rect = self.canvas.create_rectangle(50, 50, 100, 100)
            # 画一个椭圆.坐标两双。为椭圆的边界矩形左上角和底部右下角
            oval = self.canvas.create_oval(50, 50, 100, 100)
            global photo
            photo = PhotoImage(file="imgs/baidu.gif", width="100", height="100")
            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(int(self.canvas["width"]) / 2))
                y1 = random.randrange(int(int(self.canvas["height"]) / 2))
                x2 = x1 + random.randrange(int(int(self.canvas["width"]) / 2))
                y2 = y1 + random.randrange(int(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()
    
    

17.布局管理器简介

  1. 简介:
    一个 GUI 应用程序必然有大量的组件,这些组件如何排布?这时候,就需要使用 tkinter提供的布局管理器帮助我们组织、管理在父组件中子组件的布局方式。tkinter 提供了三种管理器:packgridplace

18.grid布局管理器

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

  2. 功能简介:

    选项说明取值范围
    column单元格的列号从 0 开始的正整数
    columnspan跨列,跨越的列数正整数
    row单元格的行号从 0 开始的正整数
    rowspan跨行,跨越的行数正整数
    ipadx,ipady设置子组件之间的间隔,x 方向或者 y 方向,默认单位为像素非负浮点数,默认 0.0
    padx, pady与之并列的组件之间的间隔,x 方向或者 y 方向,默认单位是像素非负浮点数,默认 0.0
    sticky组件紧贴所在单元格的某一角,对应于东南西北中以及 4 个角n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)
  3. 代码示例:
    登录界面设计

    from tkinter import *
    
    
    class Application(Frame):
        def __init__(seljkf, master=None):
            super(Application, self).__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
    	# row行,column列
        def createWidget(self):
            self.label01 = Label(self, text="用户名")
            self.label01.grid(row=0, column=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)
            Button(self, text="取消", command=quit).grid(row=2, column=2, sticky=E)
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x90+200+300")
        app = Application(master=None)
        root.mainloop()
    
    

    通过 grid 布局-实现计算器软件界面。根据实际简易计算器的按键分布,设计一个相仿的计算器界面,相应的功能暂不需要实现。
    在这里插入图片描述

    from tkinter import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            btnText = [("MC", "M+", "M-", "MR"),
                       ("C", "±", "÷", "×"),
                       (7, 8, 9, "-"),
                       (4, 5, 6, "+"),
                       (1, 2, 3, "="),
                       (0, ".")]
            Entry(self, width=25).grid(row=0, column=0, columnspan=4, pady=3)
            # enumerate 循环带前标
            for row_index, row in enumerate(btnText):
                for column_index, column in enumerate(row):
                    if column == 0:
                        Button(self, text=column, width=5)\
                            .grid(row=row_index+1, column=column_index, columnspan=2, sticky=NSEW)
                    elif column == "=":
                        Button(self, text=column, width=5)\
                            .grid(row=row_index+1, column=column_index, rowspan=2, sticky=NSEW)
                    elif column == ".":
                        Button(self, text=column, width=5)\
                            .grid(row=row_index+1, column=column_index+1, sticky=NSEW)
                    else:
                        Button(self, text=column, width=5)\
                            .grid(row=row_index+1, column=column_index, sticky=NSEW)
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("200x220")
        a = Application(master=root)
        root.mainloop()
    
    

19.pack 布局管理器

  1. 简介:
    pack 按照组件的创建顺序将子组件添加到父组件中,按照垂直或者水平的方向自然排布。如果不指定任何选项,默认在父组件中自顶向下垂直添加组件。pack 是代码量最少,最简单的一种,可以用于快速生成界面。

  2. 功能:
    在这里插入图片描述

  3. 代码示例:
    在这里插入图片描述

    # coding=utf-8
    # 测试 pack 布局管理
    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")
    for i in range(1, 20):
        Button(f2, width=5, height=10, bg="black" if i % 2 == 0 else "white").pack(side="left")
    root.mainloop()
    
    

20.place布局管理器

  1. 简介:
  2. 功能:
    在这里插入图片描述在这里插入图片描述
  3. 代码示例:
    在这里插入图片描述
    from tkinter import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)
            self.master = master
            self.pack()
            self.photos = [PhotoImage(file=f"imgs/{i+1}.png") for i in range(7)]
            print(self.photos)
            self.pukes = [Label(self.master, image=self.photos[i]) for i in range(7)]
            # 这里为什么要加self.master,因为Label标签要继承root根窗口,而master传递进来的值就是root根窗口
            # 以前在createWidget传递的是self这是因为,createWidget是属于实例方法,self指的是刚刚创建的实例
            # 刚刚创建的实例已经调用了初始化方法__init__,已经继承了root根窗口
            self.createWidget()
    
        def createWidget(self):
            i = 0
            for lab in self.pukes:
                lab.place(x=10+i*40, y=50)
                i += 1
            self.pukes[0].bind_class("Label", "<Button-1>", self.chupai)
    
        def chupai(self, event): # 这里必须传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()
    
    

21.事件处理

  1. 简介:
    一个 GUI 应用整个生命周期都处在一个消息循环 (event loop) 中。它等待事件的发生,并作出相应的处理。Tkinter 提供了用以处理相关事件的机制. 处理函数可被绑定给各个控件的各种事件。widget.bind(event, handler)如果相关事件发生, handler 函数会被触发, 事件对象 event 会传递给 hand
  2. 鼠标和键盘事件:
    代码说明
    <Button-1> <ButtonPress-1> <1>鼠标左键按下。 2 表示右键,3 表示中键;
    <ButtonRelease-1>鼠标左键释放
    <B1-Motion>按住鼠标左键移动
    <Double-Button-1>双击左键
    <Enter>鼠标指针进入某一组件区域
    <Leave>鼠标指针离开某一组件区域
    <MouseWheel>滚动滚轮;
    <KeyPress-a>按下 a 键,a 可用其他键替代
    <KeyRelease-a>释放 a 键。
    <KeyPress-A>按下 A 键(大写的 A
    <Alt-KeyPress-a>同时按下 altaalt 可用 ctrlshift 替代
    <Double-KeyPress-a>快速按两下 a
    <Control-V>CTRLV 键被同时按下,V 可以换成其它键
  3. event 对象常用属性
    名称说明
    char按键字符,仅对键盘事件有效
    keycode按键编码,仅对键盘事件有效
    keysym按键名称,仅对键盘事件有效。比如按下空格键:键的 char ,键的 keycode:32, 键的 keysymspace;比如按下 a 键:键的 chara, 键的 keycode65, 键的 keysyma
    num鼠标按键,仅对鼠标事件有效
    type所触发的事件类型
    widget引起事件的组件
    width,height组件改变后的大小,仅 Configure 有效
    xy鼠标当前位置,相对于父容器
    x_root,y_root鼠标当前位置,相对于整个屏幕
  4. 鼠标事件和键盘事件用法测试
    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("<Button-1>", mouseTest)
    c1.bind("<B1-Motion>", testDrag)
    root.bind("<KeyPress>", keyboardTest)
    root.bind("<KeyPress-a>", press_a_test)  # 只针对小写的a,大写的A不管用
    root.bind("<KeyRelease-a>", release_a_test)
    root.mainloop()
    
    

22.lambda表达式详解

  1. 简介:
    lambda 表达式定义的是一个匿名函数,只适合简单输入参数,简单计算返回结果,不适合功能复杂情况。lambda 定义的匿名函数也有输入、也有输出,只是没有名字。语法格式如下:
    lambda 参数值列表:表达式
    参数值列表即为输入。
    表达式计算的结构即为输出。
  2. 功能:
    lambda 表达式的参数值列表可以为如下内容:
    lambda 格式说明
    lambda x, y: x*y函数输入是 xy,输出是它们的积 x*y
    lambda:None函数没有输入参数,输出是 None
    lambda:aaa(3,4)函数没有输入参数,输出是 aaa(3,4)的结果
    lambda *args: sum(args)输入是任意个数的参数,输出是它们的和
    lambda **kwargs: 1输入是任意键值对参数,输出是 1
    我们在平时使用时,注意 lambda 只是一个匿名函数(没有名字的函数),功能不强,不要过度使用
  3. 代码示例:
    使用 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("hello", "world")).pack(side='left')
    
    root.mainloop()
    
    

22.多种绑定事件汇总

  1. 组件对象的绑定
    1. 通过 command 属性绑定(适合简单不需获取 event 对象)
      Button(root,text=”登录”,command=login)
    2. 通过 bind()方法绑定(适合需要获取 event 对象)
      c1 = Canvas(); c1.bind(“<Button-1>”,drawLine)
  2. 组件类对象的绑定
    1. 调用对象的 bind_class 函数,将该组件类所有的组件绑定事件:
      w.bind_class(“Widget”,”event”,eventhanler)
      比如:btn01.bind_class(“Button”,”<Button-1>”,func)
  3. 代码示例:
    # coding=utf-8
    # 多种事件绑定方式汇总
    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 方式绑定事件
    b1.bind("<Button-1>", mouseTest1)
    # command 属性直接绑定事件
    b2 = Button(root, text="测试 command2",
                command=lambda: mouseTest2("hello", "world"))
    b2.pack(side="left")
    # 给所有 Button 按钮都绑定右键单击事件<Button-2>
    b1.bind_class("Button", "<Button-1>", mouseTest3)
    # Button b1 mouseTest1和mouseTest3均可执行;Button b2 mouseTest3覆盖了mouseTest2,只执行mouseTest3
    root.mainloop()
    
    

23.OptionMenu 选择项

  1. 简介:
    OptionMenu(选择项)用来做多选一,选中的项在顶部显示。
  2. 代码示例:
    from tkinter import *
    
    root = Tk()
    root.geometry("200x100")
    v = StringVar(root)
    v.set("hello world")
    om = OptionMenu(root, v, "hello python", "hello program", "hello css")
    om["width"] = 10
    om.pack()
    
    
    def test1():
        print("你好:", v.get())
    
    
    Button(root, text="确定", command=test1).pack()
    root.mainloop()
    

24.Scale移动滑块

  1. 简介:
    Scale(移动滑块)用于在指定的数值区间,通过滑块的移动来选择值。
    在这里插入图片描述
    在这里插入图片描述

  2. 代码示例:
    在这里插入图片描述

    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)
    s1.pack()
    
    
    a = Label(root, text="你好,世界", width=10, height=1, bg="green", fg="blue")
    a.pack()
    
    
    root.mainloop()
    

25.askcolor颜色选择框

  1. 简介:
    颜色选择框可以帮助我们设置背景色、前景色、画笔颜色、字体颜色等等。

  2. 代码示例:
    在这里插入图片描述

    from tkinter import *
    from tkinter.colorchooser import *
    
    
    root = Tk()
    root.geometry("400x150")
    
    
    def test1():
        s1 = askcolor(color="red", title="选择背景色")
        print(s1)
        # pass
        root.config(bg=s1[1])
    
    
    Button(root, text="选择背景色", command=test1).pack()
    
    root.mainloop()
    

26.askopenfilename文件对话框

  1. 简介:
    文件对话框帮助我们实现可视化的操作目录、操作文件。最后,将文件、目录的信息传入到程序中。文件对话框包含如下一些常用函数:在这里插入图片描述
    命名参数 options 的常见值如下:
    在这里插入图片描述

  2. 示例代码:
    在这里插入图片描述

    from tkinter import *
    from tkinter.filedialog import *
    
    root = Tk()
    root.geometry("400x100")
    
    
    def test1():
    	# 获取文件绝对路径名
        f = askopenfilename(title="上传文件", initialdir="E:/my_python/2.python深入与提高/GUI编程/imgs",
                            filetypes=[("1", ".png")])
        # 字典赋值法为Label标签添加文本
        show["text"] = f
    
    
    Button(root, text="选择编辑的视频文件", command=test1).pack()
    
    show = Label(root, width=60, height=3, bg="white")
    show.pack()
    
    
    root.mainloop()
    
  3. 打开txt文件读取内容到窗口
    在这里插入图片描述

    from tkinter import *
    from tkinter.filedialog import *
    
    root = Tk()
    root.geometry("400x400")
    
    
    def test1():
        # 最开始的编码报错信息 <_io.TextIOWrapper name='E:/my_python/测试/test.txt' mode='r' encoding='cp936'>
        with open(askopenfile(title="上传文件", initialdi="E:/my_python/测试/",
                              filetypes=[("a", ".txt")]).name, "r", encoding="utf-8") as f:
            show["text"] = f.read()
    
    
    Button(root, text="选择读取的文本文件", command=test1).pack()
    show = Label(root, width=40, height=10, bg="white")
    show.pack()
    
    root.mainloop()
    
    

    关于其中编码错误的解决方案为:将with askopenfile(**option) as f重新改写为with open(askopenfile(**option).name,"r",encoding="utf-8") as f即可修改编码,原本错误编码为:# <_io.TextIOWrapper name='E:/my_python/测试/test.txt' mode='r' encoding='cp936'>

27.simpledialog简单输入对话框

  1. simpledialog(简单对话框)包含如下常用函数:
    函数名说明
    askfloat(title,prompt,**kw)输入并返回浮点数
    askinteger(title,prompt,**kw)输入并返回整数
    askstring(title,prompt,**kw)输入并返回字符串

参数中,title 表示窗口标题;prompt 是提示信息;命名参数 kw 为各种选项:initialvalue(初始值)、minvalue(最小值)、maxvalue(最大值)。

  1. 代码示例:
    在这里插入图片描述

    """简单对话框"""
    from tkinter.simpledialog import *
    
    root = Tk()
    root.geometry("400x100")
    
    
    def test1():
        a = askinteger(title="输入年龄", prompt="请输入年龄", initialvalue=18, minvalue=1, maxvalue=150)
        # askstring、askfloat 框使用方式一样
        show["text"] = a
    
    
    Button(root, text="今年你多大了? 请输入", command=test1).pack()
    show = Label(root, width=40, height=3, bg="green")
    show.pack()
    root.mainloop()
    
    

28.通用消息框

  1. 简介:
    messagebox(通用消息框)用于和用户简单的交互,用户点击确定、取消。如下列出了messagebox 的常见函数。

  2. 功能:
    在这里插入图片描述

  3. 代码示例:
    在这里插入图片描述

    from tkinter import *
    from tkinter.messagebox import *
    
    
    root = Tk()
    root.geometry("400x100")
    
    a1 = showinfo(title="测试", message="hello world 你好,世界")
    print(a1)
    
    root.mainloop()
    

29.ttk子模块控件

  1. 简介:
    我们再前面学的组件是 tkinter 模块下的组件,整体风格较老较丑。为了弥补这点不足,推出了 ttk 组件。ttk 组件更加美观、功能更加强大。 新增了 LabeledScale(带标签的Scale)、Notebook(多文档窗口)、Progressbar(进度条)、Treeview(树)等组件。使用 ttk 组件与使用普通的 Tkinter 组件并没有多大的区别,只要导入 ttk 模块即可。
    ttk子模块的官方文档入口
    【注】此处我们不展开 ttk。如果你的项目确实需要用到复杂的界面,推荐大家使用wxpython 或者 pyQt

30.菜单

  1. GUI 程序通常都有菜单,方便用户的交互。我们一般将菜单分为两种:

    1. 主菜单:通常位于 GUI 程序上方。
    2. 快捷菜单(上下文菜单):通过鼠标右键单击某个组件对象而弹出的菜单,一般是与该组件相关的操作。
  2. 主菜单
    主菜单一般包含:文件、编辑、帮助等,位于 GUI 窗口的上面。创建主菜单一般有如下 4步:

    1. 创建主菜单栏对象
      menubar = tk.Menu(root)
    2. 创建菜单,并添加到主菜单栏对象
      file_menu = tk.Menu(menubar)
      menubar.add_cascade(label=”文件”,menu=file_menu)
    3. 添加菜单项到 2 步中的菜单
      file_menu.add_command(label=”打开”)
      file_menu.add_command(label=”保存”,accelerator=”^p command=mySaveFile)
      file_menu.add_separator()
      file_menu.add_command(label=”退出”)
    4. 将主菜单栏添加到根窗口
      root[“menu”]=menubar
  3. 代码示例:
    记事本软件菜单
    在这里插入图片描述

    from tkinter import *
    from tkinter.filedialog import *
    from tkinter.colorchooser import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)
            self.master = master
            self.textpad = None
            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()
    
        def test(self):
            pass
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry('450x300+200+300')
        root.title("my_记事本")
        app = Application(master=root)
        root.mainloop()
    
  4. 上下文菜单
    快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:

    1. 创建菜单
      menubar = tk.Menu(root)
      menubar.add_command(label=”字体”)
    2. 绑定鼠标右键单击事件
    def test(event):
    	menubar.post(event.x_root,event.y_root) #在鼠标右键单击坐标处显示菜单
    	root.bind(<Button-3>,test)
    
  5. 代码示例:
    为记事本程序增加上下文菜单
    在这里插入图片描述

    from tkinter import *
    from tkinter.colorchooser import *
    
    root = Tk()
    root.geometry("400x400")
    
    
    def openAskColor():
        s1 = askcolor(color="red", title="选择背景色")
        root.config(bg=s1[1])
    
    
    # 创建快捷菜单
    menubar2 = Menu(root)
    menubar2.add_command(label="颜色", command=openAskColor)
    
    # 添加命令型用add_command,添加菜单项用add_cascade
    menudit = Menu(menubar2, tearoff=0)
    menudit.add_command(label="剪切")
    menudit.add_command(label="复制")
    menudit.add_command(label="粘贴")
    
    menubar2.add_cascade(label="编辑", menu=menudit)
    
    
    def test(event):
        # 菜单在鼠标右键点击的坐标处显示
        menubar2.post(event.x_root, event.y_root)
    
    
    # 编辑区
    w1 = Text(root, width=50, height=30)
    w1.pack()
    
    w1.bind("<Button-3>", test)
    
    root.mainloop()
    
    

31.记事本小项目开发

  1. 结合所学 GUI 知识,开发一款模仿 windows 记事本的软件。包含了基本的功能:
    1. 新建文本文件
    2. 保存文件
    3. 修改文件内容
    4. 退出
    5. 各种快捷键处理
    6. 修改文本区域背
  2. 记事本源代码
    """
    开发一个简单记事本
        包含:新建、保存、修改文本内容、退出
        包含:各种快捷键的处理
        version 1.0
    """
    from tkinter import *
    from tkinter.colorchooser import *
    from tkinter.filedialog import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super(Application, self).__init__(master)
            self.master = master
            self.textpad = None  # textpad 表示 Text文本框对象
            self.filename = None  # filename 表示打开文本文件的名字
            self.contextMenu = None  # contextMenu 表示上下文菜单对象
            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.quit)
    
            # 将主菜单添加到根窗口
            root["menu"] = menubar
    
            # 添加快捷键事件处理
            root.bind("<Control-n>", lambda event: self.newfile())
            root.bind("<Control-o>", lambda event: self.openfile())
            root.bind("<Control-s>", lambda event: self.savefile())
            root.bind("<Control-q>", 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("<Button-3>", self.createContextMenu)
    
        def newfile(self):
            self.textpad.delete("1.0", "end")  # 把Text控件中的内容清空
            self.filename = asksaveasfilename(title="另存为", initialfile="未命名.txt",
                                              filetypes=[("文本文档", "*.txt")], defaultextension=".txt")
            print(self.filename)
            self.savefile()
    
        def openfile(self):
            self.textpad.delete("1.0", "end")  # 先把Text控件中的内容清空
            with open(askopenfile(title="打开文件").name, "r+", encoding="utf-8") as f:
                self.textpad.insert(INSERT, f.read())
                self.filename = f.name
                print(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)
            print(event.x_root, event.y_root)
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry('450x400+200+300')
        root.title("my_记事本")
        app = Application(master=root)
        root.mainloop()
    
    
  3. 实现效果图
    在这里插入图片描述

32. 画图小项目开发

  1. 结合所学GUI知识开发一款简单的画图软件,包含如下功能:
    1. 画笔
    2. 矩形/椭圆绘制
    3. 清屏
    4. 橡皮擦
    5. 直线/带箭头的直线
    6. 修改画笔颜色、背景颜色
  2. 画图小项目源代码
    from tkinter import *
    from tkinter.filedialog import *
    from tkinter.colorchooser import *
    
    
    # 设置窗口的宽度和高度
    win_width = 900
    win_height = 450
    
    
    class Application(Frame):
        def __init__(self, master=None, bgcolor="white"):
            super(Application, self).__init__(master)
            self.master = master
            self.bgcolor = bgcolor
            self.x = 0
            self.y = 0
            self.fgcolor = "white"
            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("<ButtonRelease-1>", self.stopDraw)
    
            # 增加颜色切换的快捷键
            root.bind("<KeyPress-r>", self.kuaijiejian)
            root.bind("<KeyPress-g>", self.kuaijiejian)
            root.bind("<KeyPress-y>", self.kuaijiejian)
    
        def eventManager(self, event):
            name = event.widget.winfo_name()
    
            print(name)
            if name == "line":
                self.drawpad.bind("<B1-Motion>", self.myline)
            elif name == "lineArrow":
                self.drawpad.bind("<B1-Motion>", self.mylineArrow)
            elif name == "rect":
                self.drawpad.bind("<B1-Motion>", self.myRect)
            elif name == "pen":
                self.drawpad.bind("<B1-Motion>", self.myPen)
            elif name == "erasor":
                self.drawpad.bind("<B1-Motion>", 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
    
        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)
            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("my_画图")
        app = Application(master=root)
        root.mainloop()
    
    
  3. 画图项目效果图
    在这里插入图片描述

33.将python程序打包成exe文件

我们可以使用 pyinstaller 模块实现将 python 项目打包成 exe 文件。操作步骤如下:
1. 安装 pyinstaller 模块
在 pycharm 中操作:file-->setting-->Project:xxx -->Project interpretor,再点击+即可。
2. 在 pycharm 的 Terminal 终端输入如下命令:pyinstaller -F xxxx.p
【注】相关参数如下:
--icon=图标路径(pyinstaller -F --icon=my.ico XXXX.py)
-F 打包成一个 exe 文件
-w 使用窗口,无控制台
-c 使用控制台,无窗口
-D 创建一个目录,里面包含 exe 以及其他一些依赖性

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值