GUI图形界面编程

GUI 图形用户界面编程

文章目录

    • GUI 图形用户界面编程
      • 常用的 GUI 库
      • tkinter 模块
      • GUI 编程的核心步骤和第一个 GUI 程序
      • tkinter 主窗口
      • UI 编程整体描述
        • 常用组件汇总列表
        • GUI 应用程序类的经典写法
      • 简单组件
        • Label标签
        • Options 选项详解
        • Button
        • Entry 单行文本框
        • Text 多行文本框
        • Radiobutton 单选按钮
        • Checkbutton 复选按钮
        • canvas 画布
      • 布局管理器
        • grid 布局管理器
        • pack 布局管理器
        • place 布局管理器
      • 事件处理
        • 鼠标和键盘事件
        • lambda 表达式详解
      • 多种事件绑定方式汇总
        • 组件对象的绑定
      • 其他组件
        • OptionMenu 选择项
        • Scale 移动滑块
        • 颜色选择框
        • 文件对话框
      • 简单输入对话框
        • 通用消息框
        • ttk 子模块控件
      • 菜单
        • 主菜单
        • 上下文菜单

​ 我们前面实现的都是基于控制台的程序,程序和用户的交互通过控制台来完成。本章,我们将学习GUI(Graphics User Interface),即图形用户界面编程,我们可以通过 python 提供的丰富的组件,快速的实现使用图形界面和用户交互。
​ GUI 编程类似于“搭积木”,将一个个组件(Widget)放到窗口中。如下是 windows 中的画图软件,就是一个典型的 GUI 程序:上面的各种按钮、菜单、编辑区域等都是一个个组件,它们都放置到窗口中,并通过增加“对事件的处理”成为一个完整的程序。

常用的 GUI 库

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

tkinter 模块

本章中,涉及大量的 API 讲解。学习 API 最好的来源就是官方提供的文档:tkinter 官方网址:
https://docs.python.org/3.7/library/tk.html或者:http://effbot.org/tkinterbook/ (相对规整,适合初学者查找)。

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

基于 tkinter 模块创建 GUI 程序包含如下 4 个核心步骤:

  1. 创建应用程序主窗口对象(也称:根窗口)
(1) 通过类 Tk 的无参构造函数
from tkinter import *
root = Tk()
  1. 在主窗口中,添加各种可视化组件,比如:按钮(Button)、文本框(Label)等。
btn01 = Button(root)
btn01["text"] = "点我就送花"
  1. 通过几何布局管理 器,管理组件的大小和位置
btn01.pack()
  1. 事件处理
(1) 通过绑定事件处理程序,响应用户操作所触发的事件(比如:单击、双击等)
def songhua(e):
messagebox.showinfo("Message","送你一朵玫瑰花,请你爱上我")
print("送你 99 朵玫瑰花")
btn01.bind("",songhua)

【示例】使用 tkinter 模块,创建 GUI 应用程序,并实现点击按钮的事件处理

from tkinter import *
from tkinter import messagebox
root = Tk()
btn01 = Button(root)
btn01["text"] = "点我就送花"
btn01.pack()
def songhua(e):
messagebox.showinfo("Message","送你一朵玫瑰花,请你爱上我")
print("送你 99 朵玫瑰花")
btn01.bind("",songhua)
root.mainloop() #调用组件的 mainloop 方法,进入事件循环

tkinter 主窗口

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

【示例】测试 tkinter 主窗口位置和大小的设置

from tkinter import *
root = Tk()
root.title("测试主窗口的位置和大小")
root.geometry("500x400+100+200") #宽度 500,高度 400;距屏幕左边 100,距屏幕上边
200
root.mainloop()

执行结果:

GUI图形界面编程_第1张图片

UI 编程整体描述

​ 图形用户界面是由一个个组件组成,就像小孩“搭积木”一样最终组成了整个界面。有的组件还能在里面再放置其他组件,我们称为“容器”。Tkinter 的 GUI 组件关系图如下:

[外链图片转存失败(img-EcrnVCCL-1563689228532)(C:\Users\Administrator\Documents\gui组件关系图.jpg)]

  • Misc 和 Wm:

    Tkinter 的 GUI 组件有两个根父类,它们都直接继承了 object 类:

    ·Misc:它是所有组件的根父类。

    ·Wm:它主要提供了一些与窗口管理器通信的功能函数。

  • Tk

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

  • Pack、Place、Grid

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

  • BaseWidget

    BaseWidget 是所有组件的父类

  • Widget

    Widget 是所有组件类的父类。Widget 一共有四个父类:BaseWidget、Pack、Grid、Place。意味着,所有 GUI 组件同时具备这四个父类的属性和方法。

GUI图形界面编程_第2张图片

【注】想观察类的层次结构可以在类定义处的类名上单击右键,选择 Diagram–>showDiagram。

常用组件汇总列表

Tkinter 类 名称 简介
Toplevel 顶层 容器类,可用于为其他组件提供单独的容器;Toplevel 有点类似于窗口
Button 按钮 代表按键组件
Canvas 画布 提供绘画功能,包括直线、矩形、椭圆、多边形、位图等
Checkbutton 复选框 可供用户勾选的复选框
Entry 单行输入框 用户可输入内容
Frame 容器 用于装载其他GUI组件
Label 标签 用于显示不可编辑的文本或图标
LabelFrame 容器 也是容器组件,类似于Frame,但它支持添加标题
Listbox 列表框 列出多个选项,供用户选择

GUI 应用程序类的经典写法

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

【栗子】 标准的 GUI 程序类的写法

"""测试一个经典的 GUI 程序的写法,使用面向对象的方式"""
from tkinter import *
from tkinter import messagebox

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)
        self.btn01["text"] = "点击送花"
        self.btn01.pack()
        self.btn01["command"] = self.songhua
       
        # 创建一个退出按钮
        self.btnQuit = Button(self, text="退出", command=root.destroy)
        self.btnQuit.pack()
        def songhua(self):
        messagebox.showinfo("送花","送你 99 朵玫瑰花")

if __name__ == '__main__':
    root = Tk()
    root.geometry("400x100+200+300")
    root.title("一个经典的 GUI 程序类的测试")
    app = Application(master=root)
    root.mainloop()

简单组件

Label标签

Label(标签)主要用于显示文本信息,也可以显示图像。

Label(标签)有这样一些常见属性:

  1. width,height:
    用于指定区域大小,如果显示是文本,则以单个英文字符大小为单位(一个汉字宽度占2 个字符位置,高度和英文字符一样);如果显示是图像,则以像素为单位。默认值是根据具体显示的内容动态调整。
  2. font
    指定字体和字体大小,如:font = (font_name,size)
  3. image:
    显示在 Label 上的图像,目前 tkinter 只支持 gif 格式。
  4. fg 和 bg
    fg(foreground):前景色、bg(background):背景色。
  5. justify
    针对多行文字的对齐,可设置 justify 属性,可选值"left", “center” or "right。

【栗子】

from tkinter import *
from tkinter import messagebox


class Application(Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widget()

    def create_widget(self):
        self.label01 = Label(self,text = "百战程序员", width=8, height=2,
                             fg="blue", bg="white")
        #对上面的字典参数,重新赋值改变
        #self.label01["text"] = "ccc"       
        #self.label01.config(fg="red", bg="green")
        self.label01.pack()
        self.label02 = Label(self, text="Mr_huang,你好", width=15, height=2,
                             fg="red",bg="black",font=("楷体",18))
        self.label02.pack()
        # 显示图片
        global photo   #声明为全局变量,如果是局部变量,本方法执行完毕后自动销毁,窗口显示不出来
        photo = PhotoImage(file="imgs/logo.gif")
        self.label03 = Label(self, image=photo)
        self.label03.pack()
        #多行显示
        self.label04 = Label(self,text = "北京尚学堂\nPython培训\n黄璞",
                             borderwidth=3,relief="ridge", justify="left")
        self.label04.pack()


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300+200+100")
    root.title("Gui/Lable组件测试")
    app = Application(master=root)
    root.mainloop()

运行结果为:
GUI图形界面编程_第3张图片

Options 选项详解

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

  1. 创建对象时,使用可变参数
fred = Button(self, fg="red", bg="blue")
  1. 创建对象后,使用字典索引方式
fred["fg"] = "red"
fred["bg"] = "blue"
  1. 创建对象后,使用 config()方法

    fred.config(fg="red", bg="blue")
    

常用的组件汇总如下:

选项名 (别名) 含义
activebackground 指定组件处于激活状态时的背景色
activeforeground 指定组件处于激活状态时的前景色
anchor
指定组件内的信息(比如文本或图片)在组件中如何显示(当所在组件
比信息大时,可以看出效果)。必须为下面的值之一:N、NE、E、SE、
S、SW、W、NW 或 CENTER。比如 NW(NorthWest)指定将信息显示在组件的左上角
background(bg) 指定组件正常显示时的背景色
bitmap
指定在组件上显示该选项指定的位图,该选项值可以是 Tk_GetBitmap
接收的任何形式的位图。位图的显示方式受 anchor、justify 选项的
影响。如果同时指定了 bitmap 和 text,那么 bitmap 覆盖文本;如果
同时指定了 bitmap 和 image,那么 image 覆盖 bitmap
borderwidth 指定组件正常显示时的 3D 边框的宽度,该值可以是 Tk_GetPixels 接
收的任何格式
cursor
指定光标在组件上的样式。该值可以是 Tk_GetCursors 接受的任何格

command 指定按组件关联的命令方法,该方法通常在鼠标离开组件时被触发调

disabledforeground 指定组件处于禁用状态时的前景色
font 指定组件上显示的文本字体
foreground(fg) 指定组件正常显示时的前景色
highlightbackgroun
d 指定组件在高亮状态下的背景色
highlightcolor 指定组件在高亮状态下的前景色
highlightthickness
指定组件在高亮状态下的周围方形区域的宽度,该值可以是
Tk_GetPixels 接收的任何格式
height
指定组件的高度,以 font 选项指定的字体的字符高度为单位,至少为
1
image
指定组件中显示的图像,如果设置了 image 选项,它将会覆盖 text、
bitmap 选项
justify
指定组件内部内容的对齐方式,该选项支持 LEFT(左对齐)、CENTER
(居中对齐)或 RIGHT(右对齐)这三个值
padx
指定组件内部在水平方向上两边的空白,该值可以是 Tk_GctPixels 接
收的任何格式
pady
指定组件内部在垂直方向上两地的空白,该值可以是 Tk_GctPixels 接
收的任何格式
relief
指定组件的 3D 效果,该选项支持的值包括 RAISED、SUNKEN、FLAT、
RIDGE、SOLID、GROOVE。该值指出组件内部相对于外部的外观样式,
比如 RAISED 表示组件内部相对于外部凸起
selectbackground 指定组件在选中状态下的背景色
selectborderwidth 指定组件在选中状态下的 3D 边框的宽度,该值可以是 Tk_GetPixels
接收的任何格式
selectforeground 指定组在选中状态下的前景色
state
指定组件的当前状态。该选项支持 NORMAL(正常)、DISABLE(禁用)
这两个值
takefocus
指定组件在键盘遍历(Tab 或 Shift+Tab)时是否接收焦点,将该选
项设为 1 表示接收焦点;设为 0 表示不接收焦点
text 指定组件上显示的文本,文本显示格式由组件本身、anchor 及justify 选项决定
textvariable
指定一个变量名,GUI 组件负责显示该变量值转换得到的字符串,文
本显示格式由组件本身、anchor 及 justify 选项决定
underline
指定为组件文本的第几个字符添加下画线,该选项就相当于为组件绑
定了快捷键
width 指定组件的宽度,以 font 选项指定的字体的字符高度为单位,至少为
1
wraplength 对于能支持字符换行的组件,该选项指定每行显示的最大字符数,超
过该数量的字符将会转到下行显示
xscrollcommand
通常用于将组件的水平滚动改变(包括内容滚动或宽度发生改变)与
水平滚动条的 set 方法关联,从而让组件的水平滚动改变传递到水平
滚动条
yscrollcommand
通常用于将组件的垂直滚动改变(包括内容滚动或高度发生改变)与
垂直滚动条的 set 方法关联,从而让组件的垂直滚动改变传递到垂直
滚动条

Button

​ Button(按钮)用来执行用户的单击操作。Button 可以包含文本,也可以包含图像。按钮被单击后会自动调用对应事件绑定的方法。

【栗子】:

from tkinter import *
from tkinter import messagebox


class Application(Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widget()

    def create_widget(self):
        #创建组件
        self.btn01 = Button(root,text="登录", width=10, height=2,
                             command=self.login)
        self.btn01.pack()
        #显示图片
        global photo    #声明全局变量
        photo = PhotoImage(file="imgs/start.gif")
        self.btn02 = Button(root, image=photo,  width=200, height=150, command=self.login)
        self.btn02.pack()
        # self.btn02.config(state="disabled")  # 设置按钮为禁用

    def login(self):
        messagebox.showinfo("登录", "登录成功!")

if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300+200+100")
    root.title("Gui/Lable组件测试")
    app = Application(master=root)
    root.mainloop()

运行结果如下:

GUI图形界面编程_第4张图片

Entry 单行文本框

​ Entry 用来接收一行字符串的控件。如果用户输入的文字长度长于 Entry 控件的宽度时, 文字会自动向后滚动。如果想输入多行文本, 需要使用 Text 控件。

from tkinter import *
from tkinter import messagebox


class Application(Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widget()

    def create_widget(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)
        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=="huangpu"and pwd=="123456":
            messagebox.showinfo("测试登录系统", "登录成功,你好棒!")
        else:
            messagebox.showinfo("测试登录系统", "登录失败,请核对用户名和密码!")

if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300+200+100")
    root.title("Gui/Lable组件测试")
    app = Application(master=root)
    root.mainloop()

Text 多行文本框

​ Text(多行文本框)的主要用于显示多行文本,还可以显示网页链接, 图片, HTML 页面, 甚至 CSS 样式表,添加组件等。因此,也常被当做简单的文本处理器、文本编辑器或者网
页浏览器来使用。比如 IDLE 就是 Text 组件构成的。
【示例】Text 多行文本框基本用法(文本输入、组件和图像显示

from tkinter import *
from tkinter import messagebox
import webbrowser


class Application(Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widget()

    def create_widget(self):
        self.w1 = Text(root,width=150,height=50,bg="gray")
        self.w1.pack()

        self.w1.insert(1.0,"0123456789\nabcdefg")
        self.w1.insert(2.7,"欲穷千里目,更上一层楼。")

        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.w1.insert(INSERT, 'huangpu')
        #END 索引号表示在最后插入
        self.w1.insert(END, '[python]')
        self.w1.insert(1.8, 'Mr_huang')

    def returnText(self):
        # Indexes(索引)是用来指向Text组件中文本的位置,Text的组件索引也是对应实际字符之间的位置。
        # 核心:行号以1开始 列号以0开始
        print(self.w1.get(1.2, 1.6))
        print("所有文本内容:\n"+self.w1.get(1.0, END))

    def addImage(self):
        # global photo
        self.photo = PhotoImage(file="imgs/logo.gif")
        self.w1.image_create(END,image=self.photo)

    def addWidget(self):
        b1 = Button(self.w1,text="爱python")
        #在text创建组件的命令
        self.w1.window_create(INSERT, window=b1)

    def textTag(self):
        self.w1.delete(1.0, END)
        self.w1.insert(INSERT, "good good study,day day up!\n 人生苦短\n 快用python\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", "", self.webshow)

    def webshow(self,event):
        webbrowser.open("http://www.baidu.com")
if __name__ == '__main__':
    root = Tk()
    root.geometry("860x640+300+100")
    root.title("Gui/Lable组件测试")
    app = Application(master=root)
    root.mainloop()

Radiobutton 单选按钮

​ 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)
        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 控件用于选择多个按钮的情况。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
        # offvalue 是没选中为0, onvalue为选中为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()

canvas 画布

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

from tkinter import *
from tkinter import messagebox
import random

class Application(Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.pack()
        self.master = master
        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)
        ocal = self.canvas.create_oval(50,50,100,100)
        global photo
        photo = PhotoImage(file="imgs/logo.gif")
        self.canvas.create_image(150,170,image=photo)

        Button(self,text="画十个矩形",command=self.draw50Recg).pack()
    def draw50Recg(self):
        for i in range(10):
            x1 = random.randrange(int(self.canvas["width"])/2)
            y1 = random.randrange(int(self.canvas["height"])/2)
            x2 = random.randrange(int(self.canvas["width"])/2)
            y2 = random.randrange(int(self.canvas["height"])/2)
            self.canvas.create_rectangle(x1,y1,x2,y2)

if __name__ == '__main__':
    root = Tk()
    root.geometry("840x620+200+300")
    app = Application(master=root)
    root.mainloop()

布局管理器

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

grid 布局管理器

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

grid()方法提供的选项
选项 说明 取值范围
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”(默认)

【栗子】

from tkinter import *
from tkinter import messagebox
import random

class Application(Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.pack()
        self.master = master
        self.createWidget()

    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="取消").grid(row=2,column=2,sticky=E)

if __name__ == '__main__':
    root = Tk()
    root.geometry("840x620+200+300")
    app = Application(master=root)
    root.mainloop()

pack 布局管理器

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

​ pack()方法提供的选项
名称 描述 取值范围
expand
当值为“yes”时,side 选项无效。组件显示在父配
件中心位置;若 fill 选项为”both”,则填充父组件
的剩余空间
“yes”, 自然数,”no”, 0
(默认值”no”或 0)
fill 填充 x(y)方向上的空间,当属性 side=”top”或”
bottom”时,填充 x 方向;当属性 side=”left”或”
right”时,填充”y”方向;当 expand 选项为”yes”
时,填充父组件的剩余空间
“x”, “y”, “both”,
“none”
(默认值为 none)
ipadx
,
ipady
设置子组件之间的间隔,x 方向或者 y 方向,默认单
位为像素
非负浮点数,默认 0.0
padx,
pady
与之并列的组件之间的间隔,x 方向或者 y 方向,默
认单位是像素
非负浮点数,默认 0.0
side 定义停靠在父组件的哪一边上 “ top ” , “ bottom ” , “left”, “right”
(默认为”top”)
befor
e
将本组件于所选组建对象之前 pack,类似于先创建本
组件再创建选定组件
已经 pack 后的组件对象
after 将本组件于所选组建对象之后 pack,类似于先创建选
定组件再本组件
已经 pack 后的组件对象
in_ 将本组件作为所选组建对象的子组件,类似于指定本
组件的 master 为选定组件
已经 pack 后的组件对象
anchor
对齐方式,左对齐”w”,右对齐”e”,顶对齐”n”,
底对齐”s”
“n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)

【建议】如上列出了 pack 布局所有的属性,但是不需要挨个熟悉,了解基本pack 适用于简单的垂直或水平排布,如果需要复杂的布局可以使用 grid 或 place。

【栗子】制作钢琴按键布局

from tkinter import *
root = Tk(); root.geometry("800x300")
# 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,15):
    Button(f2, width=5, height=10, bg="black" if i % 2 == 0 else "white").pack(side="left")


root.mainloop()

place 布局管理器

​ place 布局管理器可以通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景。
place()方法的选项
选项 说明 取值范围
x,y 组件左上角的绝对
​ 坐标(相对于窗口)
非负整数
x 和 y 选项用于设置偏移(像素),如果同时设置
relx(rely) 和 x(y),那么 place 将优先计算 relx 和
rely,然后再实现 x 和 y 指定的偏移值
relx
rely
组件左上角的坐标
(相对于父容器)
relx 是相对父组件的位置。0 是最左边,0.5 是正中间,1
是最右边;
rely 是相对父组件的位置。0 是最上边,0.5 是正中间,1
是最下边;
width,
height
组件的宽度和高度 非负整数
relwid
th,
relhei
组件的宽度和高度
(相对于父容器)
与 relx、rely 取值类似,但是相对于父组件的尺寸

ght
anchor 对齐方式,左对齐”
w”,右对齐”e”,
顶对齐”n”,底对
齐”s”
“n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)

【栗子】用palce 布局管理-- 基本用法

#coding=utf-8
from tkinter import *

root = Tk(); root.geometry("500x300")
root.title("place 布局管理器"); root["bg"]= "white"

f1 = Frame(root,width=200,height= 200, bg="green")
f1.place(x=30, y= 30 )

Button(root,text="python").place(relx=0.5,rely=0, x=100, y=200, relwidth= 0.2, relheight=0.2)
Button(f1,text='程序猿').place(relx=0.6, rely=0.7)
Button(f1,text="python程序猿").place(relx=0.2,rely=0.2)

root.mainloop()

GUI图形界面编程_第5张图片

【栗子】place 布局管理 - 扑克游戏 demo

# coding = utf-8
from tkinter import *

class Application(Frame):

    def __init__(self,master=None):
        super().__init__(master)
        self.pack()
        self.createWidget()

    def createWidget(self):
        """  通过布局管理器实现puke 位置控制 """
        # self.photo = PhotoImage(file="imgs/puke/puke1.gif")
        # self.puke1 = Label(self.master,image=self.photo)
        # self.puke1.pack()
        self.photos = [PhotoImage(file = "imgs/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=55+i*40,y=50)

        # 为所有的Label 增加时间处理
        self.pukes[0].bind_class("Label","",self.chupai)

    def chupai(self,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)
如果相关事件发生, handler 函数会被触发, 事件对象 event 会传递给 handler 函数。

鼠标和键盘事件

代码 说明


<1>
鼠标左键按下。 2 表示右键,3 表示中键;
鼠标左键释放
按住鼠标左键移动
双击左键
鼠标指针进入某一组件区域
鼠标指针离开某一组件区域
滚动滚轮;
按下 a 键,a 可用其他键替代
释放 a 键。
按下 A 键(大写的 A)
同时按下 alt 和 a;alt 可用 ctrl 和 shift 替代
快速按两下 a
CTRL 和 V 键被同时按下,V 可以换成其它键位
event 对象常用属性
名称 说明
char 按键字符,仅对键盘事件有效
keycode 按键编码,仅对键盘事件有效
keysym 按键名称,仅对键盘事件有效
比如按下空格键:
键的 char: 键的 keycode:32 键的 keysym:space
比如按下 a 键:
键的 char:a 键的 keycode:65 键的 keysym:a
num 鼠标按键,仅对鼠标事件有效

type 所触发的事件类型
widget 引起事件的组件
width,height 组件改变后的大小,仅 Configure 有效
x,y 鼠标当前位置,相对于父容器
x_root,y_root 鼠标当前位置,相对于整个屏幕

【栗子】键盘和鼠标事件的测试

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 testDrg(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("", testDrg)    # 鼠标左键拖动事件

root.bind("",keyboardTest)
root.bind("", press_a_test)
root.bind("",release_a_test)

root.mainloop()

lambda 表达式详解

​ lambda 表达式定义的是一个匿名函数,只适合简单输入参数,简单计算返回结果,不适合功能复杂情况。
lambda 定义的匿名函数也有输入、也有输出,只是没有名字。语法格式如下:
​ lambda 参数值列表:表达式
参数值列表即为输入,表达式计算的结构即为输出。
我们写一个最简单的案例:

add3args = lambda x,y,z:x+y+z
#print(add3args(10,20,30))

上面的 lambda 表达式相当于如下函数定义:

def add3args(x,y,z):
	return x+y+z

lambda 表达式的参数值列表可以为如下内容:

lambda 格式 说明
lambda x, y: xy 函数输入是 x 和 y,输出是它们的积 xy
lambda:None 函数没有输入参数,输出是 None
lambda:aaa(3,4) 函数没有输入参数,输出是 aaa(3,4)的结果
lambda *args: sum(args) 输入是任意个数的参数,输出是它们的和
lambda **kwargs: 1 输入是任意键值对参数,输出是 1
​ 我们在平时使用时,注意 lambda 只是一个匿名函数(没有名字的函数),功能不强,不要过度使用;

使用 lambda 表达式实现传参
【示例】使用 lambda 帮助 command 属性绑定时传参

# 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()

多种事件绑定方式汇总

组件对象的绑定

  1. 通过 command 属性绑定(适合简单不需获取 event 对象)

​ Button(root,text=”登录”,command=login)

  1. 通过 bind()方法绑定(适合需要获取 event 对象)
    c1 = Canvas(); c1.bind(“”,drawLine) ·

  2. 组件类的绑定调用对象的 bind_class 函数,将该组件类所有的组件绑定事件:
    w.bind_class(“Widget”,”event”,eventhanler)
    比如:btn01.bind_class(“Button”,””,func)

【栗子】组件对象的绑定

#coding=utf-8
# 多种事件绑定汇总
from tkinter import *

root = Tk();root.geometry("320x40")

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")

b1.bind("",mouseTest1)

b2 = Button(root, text="测试command2", command=lambda: mouseTest2("zhangsan","lisi"))
b2.pack(side="left")

b1.bind_class("Button", "", mouseTest3)

root.mainloop()

其他组件

​ 我们在前面介绍了最常用的几个组件,接下来我们介绍其他一些组件。

OptionMenu 选择项

​ OptionMenu(选择项)用来做多选一,选中的项在顶部显示。

from tkinter import *

root = Tk(); root.geometry("200x100")
v = StringVar(root)
v.set("程序猿")
om=OptionMenu(root,v,"程序猿","python程序猿","java程序猿","go程序猿")

om["width"] = 10
om.pack()

def test1():
    print("最喜爱的计算机语言:",v.get())

Button(root,text="确定",command=test1).pack()

root.mainloop()

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)
s1.pack()

a = Label(root,text="python程序猿",width=20, height=1, bg="black", fg="white")
a.pack()

root.mainloop()

颜色选择框

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

from tkinter import *
from tkinter.colorchooser import *

root = Tk();root.geometry("400x200")

def test1():
    s1 = askcolor(color="red",title="选择背景")
    print(s1)
    print(s1[0])
    root.config(bg=s1[1])

Button(root,text = "选择背景色",command=test1).pack()

root.mainloop()

文件对话框

​ 文件对话框帮助我们实现可视化的操作目录、操作文件。最后,将文件、目录的信息传入到
程序中。文件对话框包含如下一些常用函数。

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

返回目录名
asksaveasfile(**options) 保 存 对 话

返回保存的文件对象
asksaveasfilename(**options) 返回保存的文件名
命名参数 options 的常见值如下:
参数名 说明
defaultextension 默认后缀:.xxx
用户没有输入则自动添加
filetypes=[(label1,pattern1),(labe2,patt
ern2)]
文件显示过滤器
initialdir 初始目录
initialfile 初始文件
parent 父窗口,默认根窗口
title 窗口标题

【栗子】文件对话框获取文件

from tkinter import *
from tkinter.filedialog import *

root = Tk(); root.geometry("400x200")

def test1():
    f = askopenfilename(title ="上传文件", initialdir="e:/",filetypes=[("视频文件",".mp4")])
    print(f)
    show["text"] = f

Button(root,text="选择文件",command=test1).pack()

show = Label(root, width=40, height=3, bg="green")
show.pack()

root.mainloop()

简单输入对话框

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

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

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

【栗子】简单对话框的基本用法

from tkinter.simpledialog import *

root = Tk(); root.geometry("400x200")

def test1():
    a = askinteger(title ="输入年龄", prompt="请输入年龄", initialvalue=18, minvalue=1, maxvalue=100)
    show["text"]=a


Button(root,text="你多大了?请输入", command=test1).pack()

show = Label(root, width=40, height=3, bg="green")
show.pack()

root.mainloop()

通用消息框

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

函数名 说明 例子
askokcancel(title,message,**opt
ions)
OK/Cancel 对话框
askquestion(title,message,opt
ions)
Yes/No 问题对话框
askretrycancel(title,message,

options)
Retry/Cancel问题对
话框
showerror(title,message,**optio
ns)
错误消息对话框
showinfo(title,message,**option
s)
消息框
showwarning(title,message,**opt
ions)
警告消息框

【栗子】通用消息框的基本用法

from tkinter import *
from tkinter.messagebox import *

root = Tk(); root.geometry("400x200")

def test1():
    a = showwarning(title ="python", message="哈哈哈~ 你被骗了。")
    print(a)

Button(root,text="点进去有惊喜~~", command=test1).pack()

root.mainloop()

ttk 子模块控件

​ 我们再前面学的组件是 tkinter 模块下的组件,整体风格较老较丑。为了弥补这点不足,推出了 ttk 组件。ttk 组件更加美观、功能更加强大。 新增了 LabeledScale(带标签的Scale)、Notebook(多文档窗口)、Progressbar(进度条)、Treeview(树)等组件。使用 ttk 组件与使用普通的 Tkinter 组件并没有多大的区别,只要导入 ttk 模块即可。

ttk 子模块的官方文档:
https://docs.python.org/3.7/library/tkinter.ttk.html
【注】此处我们不展开细讲 ttk。如果你的项目确实需要用到复杂的界面,推荐大家使用wxpython 或者 pyQt。

菜单

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

  1. 主菜单
    主菜单通常位于 GUI 程序上方。
  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

【栗子】记事本软件的主菜单

"""  开发记事本软件的菜单 """

from tkinter import *
from tkinter.colorchooser import *

class Application(Frame):

    def __init__(self,master=None):
        super().__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=80,height=50)
        self.textpad.pack()
    def test(self):
        pass


if __name__ == '__main__':
    root = Tk();root.geometry("450x300+300+200")
    root.title("Mr_huang的简易记事本")
    app = Application(master=root)
    root.mainloop()

上下文菜单

​ 快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:

  1. 创建菜单
    menubar = tk.Menu(root)
    menubar.add_command(label=”字体”)
  2. 绑定鼠标右键单击事件
    def test(event):
    menubar.post(event.x_root,event.y_root) #在鼠标右键单击坐标处显示菜单
    root.bind(“”,test)
#coding=utf-8
from tkinter import *
from tkinter.colorchooser import *
from tkinter.filedialog 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)
menuedit = Menu(menubar2,tearoff=0)
menuedit.add_command(label="剪切")
menuedit.add_command(label="复制")
menuedit.add_command(label="粘贴")
menubar2.add_cascade(label="编辑",menu=menuedit)
def test(event):
    #菜单在鼠标右键单击的坐标处显示
    menubar2.post(event.x_root,event.y_root)
#编辑区
w1 = Text(root,width=50,height=30)
w1.pack()
w1.bind("",test)
root.mainloop()

将主菜单栏加入根窗口
root[“menu”] = menubar

    #文本编辑区
    self.textpad = Text(root,width=80,height=50)
    self.textpad.pack()
def test(self):
    pass

if name == ‘main’:
root = Tk();root.geometry(“450x300+300+200”)
root.title(“Mr_huang的简易记事本”)
app = Application(master=root)
root.mainloop()


#### 上下文菜单

​	快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:

1. 创建菜单
     menubar = tk.Menu(root)
       menubar.add_command(label=”字体”)
2. 绑定鼠标右键单击事件
     def test(event):
       menubar.post(event.x_root,event.y_root) #在鼠标右键单击坐标处显示菜单
       root.bind(“”,test)

```python
#coding=utf-8
from tkinter import *
from tkinter.colorchooser import *
from tkinter.filedialog 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)
menuedit = Menu(menubar2,tearoff=0)
menuedit.add_command(label="剪切")
menuedit.add_command(label="复制")
menuedit.add_command(label="粘贴")
menubar2.add_cascade(label="编辑",menu=menuedit)
def test(event):
    #菜单在鼠标右键单击的坐标处显示
    menubar2.post(event.x_root,event.y_root)
#编辑区
w1 = Text(root,width=50,height=30)
w1.pack()
w1.bind("",test)
root.mainloop()

你可能感兴趣的:(GUI图形界面编程)