当前位置: 首页 > news >正文

python——GUI图形用户界面编程

GUI简介

我们前面实现的都是基于控制台的程序,程序和用户的交互通过控制台来完成

本章,我们来学习GUI图形用户界面编程,我们可以通过python提供的丰富的组件,快速的视线使用图形界面和用户交互

GUI变成类似于“搭积木”,将一个个组件(widget)放到窗口中。windows中的画图软件,就是一个典型的GUI程序:

上面的各种按钮、菜单、编辑区域等都是一个个组件,它们都防止到窗口中,并通过增加“对时间的处理”称为一个完整的程序。

常用的GUI库

  1. Tkinter
    tkinter是Python的标准GUI库,支持跨平台的GUI程序开发,主要一tkinter为核心进行讲解
  2. wxPython
    wxPyhton是比较流行的GUI库,适合大型应用程序开发,功能强于tkinter,整体设计框架类似于MFC
  3. PyQT
    Qt是一种开源的GUI库,适合大型GUI程序开发,PyQT是Qt工具包标准的Python实现。我们也可以使用Qt Desginer界面设计器快速开发GUI应用程序

tkinter模块

本章设计大量的API讲解。学习API最好的来源就是官方提供的文档:tkinter官方网站:

Graphical User Interfaces with Tk — Python 3.7.17 documentation或者:https://effbot.org/tkinternook/

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

1.创建应用程序主窗口对象
        (1)通过类Tk的无参数构造函数

from tkinter import *

root=Tk()

2.在主窗口中,添加各种可视化组件,比如:按钮(Button)、文本框(Label)等

btn01=Button(root)
btn01["text"]="点我就送花"

3.通过几何布局管理器,管理组件的大小和位置

btn01.pack()

4.事件处理

(1)通过绑定事件处理程序,响应用户操作所触发的时间(比如:单击、双击等)

def songhua(e):
    messagebox.showinfo("Message", "送你一朵玫瑰花,请你爱上我")
    print("送你99多玫瑰花")


btn01.bind("<Button-1>", songhua())

总代码

from tkinter import *
from tkinter import messagebox

root = Tk()

btn01 = Button(root)
btn01["text"] = "点我就送花"

btn01.pack()


def songhua(e):  # e就是事件对象
    messagebox.showinfo("Message", "送你一朵花")
    print("送你999多玫瑰花")


btn01.bind("<Button-1>", songhua)

root.mainloop()  # 调用组件的mainloop()方法,进入时间循环

主窗口大小和位置

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

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

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox

root = Tk()

root.title("这是我的第一个GUI程序")
root.geometry("500x300+100+200")

btn01 = Button(root)
btn01["text"] = "点我就送花"

btn01.pack()


def songhua(e):  # e就是事件对象
    messagebox.showinfo("Message", "送你一朵花")
    print("送你999多玫瑰花")


btn01.bind("<Button-1>", songhua)

root.mainloop()  # 调用组件的mainloop()方法,进入时间循环

GUI编程整体描述

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

GUI应用程序类的经典写法

本节程序也是GUI应用程序编写的恶一个主要结构,采用了面向对象的方式,更加合理的组织代码。

通过类Application组织整个GUI程序,类Application继承了Frame及通过继承拥有了父类的特性。通过构造函数__init__()初始化窗口中的对象,通过createWidfets()方法创建窗口中的对象。

Frame框架式一个tkinter组件,表示一个矩形的区域。Frame一般作为容器使用,可以放置其他组件,从而实现复杂的布局。

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


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

    def __init__(self, master=None):
        super().__init__(master)  # super()代表的是父类的定义,而不是父类对象
        self.master = master  # 传入root作为继承
        self.pack()  # 是用来将一个组件(如按钮、标签、框架等)添加到其父容器中的一种布局管理方法。

        self.createWiget()  # 调用创建组件方法

    def createWiget(self):
        """创建组件"""
        self.btn01 = Button(self.master)
        self.btn01["text"] = "点击送花"
        self.btn01.pack()
        self.btn01["command"] = self.songhua

        # 创建一个退出按钮
        self.btnQuit = Button(self.master, text="退出", command=root.destroy)
        self.btnQuit.pack()

    def songhua(self):
        messagebox.showinfo("送花", "送你99多玫瑰花")


if __name__ == '__main__':
    """Application 类实例化时,将 master 参数设为 root,这意味着 Application 的所有组件(在 Frame 中创建)都将在 root 主窗口中显示。"""
    root = Tk()  # 创建一个Tk对象
    root.geometry("400x100+200+300")  # 设置Tk的窗口大小和范围
    root.title("一个经典的GUI程序类的测试")  # 设置Tk的的名字
    app = Application(master=root)  # 实例化一个Application对象
    root.mainloop()  # 它的作用是启动 Tkinter 事件循环,让你的应用程序开始运行并等待用户的操作
  • root

    • 代表了 Tkinter 主窗口的实例,是 Tk 类的对象。
    • 这是 GUI 应用程序的顶层窗口,对整个应用进行管理。
  • Frame

    • 是一个容器控件,用于在应用程序窗口中组织其他控件(比如按钮、标签等)。
    • 在这里,Application 类是 Frame 的子类,它通过继承 Frame 来创建一个新的组件。

1. Button(self.master)

如果你的 master 是一个 Tk 实例(也就是主窗口),那么使用 Button(self.master) 是合适的。这种情况下,按钮会被添加到主窗口中。

2. Button(self)

如果你在一个自定义类中,且希望按钮添加到该类的实例(比如 Frame 或者 Toplevel),则使用 Button(self) 是合适的。按钮将被添加到这个特定的实例中。

简单组件

Label标签

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

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

  1. width,height:
    用于指定区域大小,如果显示是文本,则以单个英文字符大小为单位(一个汉字占2个字符位置,高度和英文字符一样);如果显示是图像,则以像素为单位。默认值是根据具体显示的内容动态调用。
  2. font
    指定字体和字体大小
  3. image
    显示在Label上的图像,目前tkinter只支持gif格式
  4. fg和bg
    fg(foreground):前景色、bg(background):背景色
  5. justify
    针对多行文字的对齐,可设置justify属性,可选值"left"、"center" or "right
# -*- coding: utf-8 -*-
"""测试一个经典的GUI程序写法,使用面向对象的方式"""
from tkinter import *


class Application(Frame):
    """
    测试labels标签
    """

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

        self.createWidget()  # 创建的框架

    def createWidget(self):
        self.label01 = Label(self, text="老高", width=10, height=2, bg="black", fg="white")
        self.label01.pack()

        self.label02 = Label(self, text="老杨", width=10, height=2, bg="blue", fg="white", font=("黑体", 30))
        self.label02.pack()

        # 显示图像
        # global photo  # 把photo声明成全局变量,如果是局部变量,不然本方法执行后,图像对象销毁
        self.photo = PhotoImage(file="img/3.gif")
        self.label03 = Label(self, image=self.photo)
        self.label03.pack()

        self.label04 = Label(self, text="你好!\n我是一名初学者\n正在学习python编程",
                             borderwidth=1, relief="solid", justify="right")
        self.label04.pack()


if __name__ == '__main__':
    root = Tk()
    root.geometry("600x500+200+200")
    app = Application(master=root)
    root.mainloop()

Option选项详解

通过学习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")

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.createWidget()

    def createWidget(self):
        """框架方法"""
        self.butn1 = Button(self.master, text="begin",
                            width=6, height=3, anchor=E, command=self.songhua)  # anchor的作用就是决定你的text文本唯一按钮的那个位置
        self.butn1.pack()

        # 插入图片
        self.photo = PhotoImage(file="img/1.gif")
        self.butn2 = Button(self.master, image=self.photo, command=self.login)
        self.butn2.pack()
        self.butn2.config(state=DISABLED)  # 加入state会让你不能点击,就让你取消点击状态

    def songhua(self):
        """点击开始送花"""
        messagebox.showinfo("恭喜你", "得到两朵玫瑰花")

    def login(self):
        messagebox.showinfo("系统", "登录成功!欢迎开始学习!")


if __name__ == '__main__':
    root = Tk()
    root.title("button练习")
    root.geometry("400x300+200+100")
    app = Application(root)
    root.mainloop()

Entry单行文本框

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

# -*- coding: utf-8 -*-

from tkinter import *
from tkinter import messagebox


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

    def createWidgt(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()

        # 登录按钮
        self.btn01 = Button(self, text="登录", command=self.login)
        self.btn01.pack()

    def login(self):
        username = self.entry01.get()
        pwd = self.entry02.get()
        print("用户名:" + username)
        print("密 码 :" + pwd)
        messagebox.showinfo("老杨系统", "恭喜登录成功")


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300+200+100")
    root.title("entry文本")
    app = Application(root)
    root.mainloop()

Text多行文本框

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

# -*- coding: utf-8 -*-
import webbrowser
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.w1 = Text(self.master, width=40, height=12, bg="gray")  # 注意这里的self.master可以是root,但是不能是self
        # 宽度20个字母(10个汉字),高度一个行高
        self.w1.pack()

        self.w1.insert(1.0, "0123456789\nabcdefg")  # 这里的1.0的意思是第一行第0列
        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.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, "老杨")
        # END索引号表示在最后插入
        self.w1.insert(END, "你好,我是你大哥")

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

    def addImage(self):
        """在光标位置插入图片"""
        global photo
        photo = PhotoImage(file="img/3.gif")
        self.w1.image_create(END, image=photo)  # 在文章的末尾插入
        self.w1.image_create(INSERT, image=photo)  # 在鼠标的光标出插入图片

    def addWidget(self):
        """增加新的控件方法"""
        b1 = Button(self.w1, text="爱你老杨", command=self.songhua)
        # 在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就像老鼠爱大米\n百度")
        self.w1.tag_add("good", 1.0, 1.9)  # 第一个参数为标记名称,第二个参数和第三个参数是索引的位置
        self.w1.tag_config("good", background="yellow", foreground="red")  # 第一个参数是需要添加配置的标记,第二和第三个参数的含义是添加前景色和背景色

        self.w1.tag_add("baidu", 5.0, 5.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")

    def songhua(self):
        """送花"""
        messagebox.showinfo("老杨系统", "送你9999朵玫瑰花")


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300+200+100")
    root.title("老杨系统")
    app = Application(root)
    root.mainloop()

Radiobutton单选按钮

Radiobutton控件用于选择同一组单选按钮中的一个

Radiobutton可以显示文本,也可以显示图像

# -*- coding: utf-8 -*-
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.v = StringVar()
        self.v.set("女")

        self.election1 = Radiobutton(self.master, text="男", value="男", variable=self.v)
        self.election2 = Radiobutton(self.master, text="女", value="女", variable=self.v)

        self.election1.pack(side="left")
        self.election2.pack(side="left")
        Button(self.master, text="确定", command=self.confirm).pack(side="left")

    def confirm(self):
        messagebox.showinfo("测试", "选择的性别:" + self.v.get())


if __name__ == '__main__':
    root = Tk()
    root.title("老杨的系统")
    root.geometry("400x300+200+100")
    app = Application(root)
    root.mainloop()

Checkbutton复选按钮

Checkbutton控件用于选择多个按钮的情况。Checkbutton可以显示文本,也可以显示图像

# -*- coding: utf-8 -*-
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.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.title("老杨的系统")
    root.geometry("400x300+200+100")
    app = Application(root)
    root.mainloop()

canvas画布

canvas(画布)是一个矩形区域,可以放置图形、图像、组件等。本节我们简单介绍canvas的使用。

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox
import random


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

    def createWidget(self):
        """创建组件"""
        self.canvas = Canvas(self, width=400, height=500, 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="img/3.gif")
        self.canvas.create_image(200, 300, 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)  # 生成一个从0到这个数中的其中一个数
            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.title("老杨的系统")
    root.geometry("400x600+200+100")
    app = Application(root)
    root.mainloop()

布局管理器

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

grid布局管理器

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

grid()方法提供的选项
选项说明取值范围
column单元格的列号从0开始的正整数
columnspan跨列,跨越的列数正整数
row单元格的行号从0开始的正整数
rowspankuahang,跨越的行数正整数
ipadx,ipady设置子组件之间的间隔,x方向或者y方向,默认单位为像素非负浮点数,默认为0.0
padx,pady与之并列的组件之间的间隔,x方向或者y方向,默认单位是像素非负浮点数,默认0.0
sticky组件紧贴所在单元格的某一角,对应于东南西北中以及4个角“n”,“s”,“w”,“e”,“nw”,“sw”,“se”,“ne”,“center(默认)”
# -*- coding: utf-8 -*-
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.label01 = Label(self, text="用户名")
        self.label01.grid(row=0, column=0)
        v1 = StringVar()
        self.entry01 = Entry(self, textvariable=v1)
        self.entry01.grid(row=0, column=1)
        v1.set("12346578")
        print(self.entry01.get())
        self.label02 = Label(self, text="用户名为手机号")
        self.label02.grid(row=0, column=2)

        # 密码
        self.label03 = Label(self, text="密码")
        self.label03.grid(row=1, column=0)
        v2 = StringVar()
        self.entry02 = Entry(self, textvariable=v2, show="*")
        self.entry02.grid(row=1, column=1)

        # 登录
        Button(self, text="登录", command=lambda: self.login(self.entry01.get(), self.entry02.get())).grid(row=2,
                                                                                                         column=1,
                                                                                                         sticky="EW")
        Button(self, text="取消", command=self.quxiao).grid(row=2, column=2, sticky="EW")

    def login(self, admin, key):
        """
        用于登录
        :param admin:用户名
        :param key: 密码
        :return:
        """
        if admin == "17729686779" and key == "123456":
            messagebox.showinfo("老杨的系统", "密码正确")
        else:
            messagebox.showinfo("老杨的系统", "密码错误")

    def quxiao(self):
        """取消登录"""
        self.entry01.delete(0, END)
        self.entry01.insert(0, "123456")
        self.entry02.delete(0, END)


if __name__ == '__main__':
    root = Tk()
    root.title("老杨的系统")
    root.geometry("400x300+200+100")
    app = Application(root)
    root.mainloop()

【示例】通过grid布局-实现计算器软件界面

根据实际建议计算器的按键分布,设计一个相仿的计算器界面,

如上界面,实际可以设计成一个7行4列的表格布局,然后将相应的按钮放置进去即可

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox


class Application(Frame):  # 制作一个简易的计算器
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        self.master = master
        self.createWidget()

    def createWidget(self):
        """通过grid布局实现计算器的界面"""
        # 按钮
        binText = (("MC", "M+", "M-", "MR"),
                   ("C", "±", "÷", "x"),
                   (7, 8, 9, "-"),
                   (4, 5, 6, "+"),
                   (1, 2, 3, "="),
                   (0, "."))

        # 输入框
        self.v1 = StringVar()
        self.entry01 = Entry(self, textvariable=self.v1)
        self.entry01.grid(row=0, column=0, columnspan=4, pady=10)  # pady=10 意味着在 entry01 的顶部和底部各添加 10 像素的空间。
        for rindex, rText in enumerate(binText):
            for cindex, cvalue in enumerate(rText):
                if cvalue == "=":
                    Button(self, text=cvalue, width=2, command=self.mainCaculate()).grid(row=rindex + 1, column=cindex,
                                                                                         rowspan=2, sticky=NSEW)
                elif cvalue == 0:
                    Button(self, text=cvalue, width=2).grid(row=rindex + 1, column=cindex, columnspan=2, sticky=NSEW)
                elif cvalue == ".":
                    Button(self, text=cvalue, width=2).grid(row=rindex + 1, column=cindex + 1, sticky=NSEW)
                else:
                    Button(self, text=cvalue, width=4).grid(row=rindex + 1, column=cindex, sticky=NSEW)
                    # sticky=NSEW,意味着按钮会填充整个单元格

    def mainCaculate(self):
        pass


if __name__ == '__main__':
    root = Tk()
    root.title("简易计算器")
    root.geometry("250x250+200+100")
    app = Application(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”)
before将本组件于所选组建对象之前pack,类似于先创建本组件再创建选定组件已经pack后的组件对象
after将本组件于所组建对象之后pack,类似于先创建选定组件再本组件已经pack后的组件对象
in_将本组件作为所选组建对象的子组件,类似于指定本组件的master为选定组件已经pack后的组件对象
anchor对齐方式,左对齐“w”,右对齐“e”,顶对齐“n”,底对齐“s”“n”,“s”,“w”,“e”,“nw”,“sw”,“se”,“ne”,“center”(默认)

【示例】pack布局用法,制作钢琴按键布局

# -*- coding: utf-8 -*-
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):
    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布局管理器可以通过坐标精准控制组件的位置,使用于一些布局更加灵活的场景

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

组件的宽度和高度非负整数

relwidth

relheight

组件的宽度和高度(相对于父容器)与relx、rely取值类似,但是相对于父组件的尺寸
anchor对齐方式,左对齐“w”,右对齐“e”,顶对齐“n”,底对齐“s”“n”,“s”,“w”,“e”,“nw”,“sw”,“se”,“ne”,“center”(默认)
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox

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=20)
Button(root, text="老杨").place(relx=0.5, x=100, y=20, relwidth=0.2, relheight=0.5)
# 如果relx和x同时存在的话,就是先定位relx的位置,然后再定位x的位置,就是定位了relx的位置后,向两边移x
Button(f1, text="老杨爱睡觉").place(relx=0.6, rely=0.7)
Button(f1, text="老杨大哥").place(relx=0.5, rely=0.2)

root.mainloop()

【示例】place布局管理-扑克牌游戏demo

!!!扑克图片需要自己在网上去找,然后做成gif图片的形式

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox


class Application(Frame):
    """通过place布局管理器实现扑克牌位置控制"""

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


    def createWidget(self):
        """创建控制组件"""
        # self.photo = PhotoImage(file="img/puke/1.gif")
        # self.puke1 = Label(self.master, image=self.photo)
        # self.puke1.place(x=10, y=50)
        self.photos = [PhotoImage(file="img/puke/" + str(i + 1) + ".gif") for i in range(6)]
        self.pukes = [Label(self.master, image=self.photos[i]) for i in range(6)]
        for i in range(6):
            self.pukes[i].place(x=10 + i * 120, y=self.yabs)

        # 为所有的Label增加事件处理
        for i in range(6):
            self.pukes[i].bind_class("Label", "<Button-1>", self.chupai)

    def chupai(self, event):
        print(event.widget.winfo_geometry())
        print(event.widget.winfo_y())
        if event.widget.winfo_y() == self.yabs:
            event.widget.place(y=self.yabs - 50)
        else:
            event.widget.place(y=self.yabs)


if __name__ == '__main__':
    root = Tk()
    root.geometry("800x400+300+200")
    root.title("扑克牌玩具")
    app = Application(root)
    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
print(add3args(10, 20, 30))

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

lambda格式说明
lambda x,y:x*y函数输入是x和y,输出是它们的积x*y
lambda:None函数没有输入参数,输出是None
lambda:aaa(3,4)函数没有输入参数,输出是aaa(3,4)的结果
lambda *args:sum(args)输入是任意个数的参数,输出是它们的和
lambda **kwargs:1输入是任意键值对参数,输出是1
使用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")

Button(root, text="测试command2", command=lambda: mouseTest2("laoyang", "xixi")).pack(side="left")

root.mainloop()

多种事件绑定方式汇总

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

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

# -*- 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={},b={}".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("laoyang", "xixi"))
b2.pack(side="left")

# 给所有Button按钮都绑定右键单击事件<Button-2>
b1.bind_class("Button", "<Button-3>", mouseTest3)  # Button-3是单击右键的意思

root.mainloop()

其他组件

OptionMenu选择项

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

【示例】OptioMenu(选择项)的基本用法

from tkinter import *
from tkinter import messagebox


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

    def createWidget(self):
        """创建组件"""
        Label(self, text="最爱的人是谁:").grid(row=0, column=0)
        v = StringVar(self)
        v.set("彭于晏")
        om = OptionMenu(self, v, "彭于晏", "刘亦菲", "yyx")
        om["width"] = 10
        om.grid(row=0, column=1)
        Btn1 = Button(self, text="确定", command=lambda: self.test1(v))
        Btn1.grid(row=1, column=1)

    def test1(self, v):
        print("最爱的人:", v.get())


if __name__ == '__main__':
    root = Tk()
    root.geometry("300x100")
    root.title("最爱的明星")
    app = Application(root)
    root.mainloop()

Scale滑块

Scale(移动滑块)用于在指定的数值区间,通过滑块的移动来选择值

【示例】使用Scale(移动滑块)控制字体大小变化

# -*- coding: utf-8 -*-
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()
        self.a

    def createWidget(self):
        """创建框架"""
        s1 = Scale(self, from_=10, to=50, length=200, tickinterval=5, orient=HORIZONTAL,
                   command=self.test1)  # 默认是竖直的,HORIZONTAL是水平的,这里调用test的时候,会默认将value的值传入函数中
        s1.pack()
        self.a = Label(self, text="yyx", width=10, height=1, bg="white", fg="red")
        self.a.pack()

    def test1(self, value):
        print("滑块的值:", value)
        newFont = ("宋体", value)
        self.a.config(font=newFont)


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的系统")
    app = Application(root)
    root.mainloop()

颜色选择框

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

【示例】颜色选择框的用法

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox
from tkinter import colorchooser


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

    def createWidget(self):
        """创建组件"""
        Button(self, text="选择背景色", command=self.test1).pack()

    def test1(self):
        s1 = colorchooser.askcolor(color="white", title="选择背景色")
        print(s1)
        self.master.config(bg=s1[1])


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的系统")
    app = Application(root)
    root.mainloop()

文件对话框

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

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

命名参数options的常见值如下:

参数名说明
defaultextension

默认后缀:.xxx

用户没有输入则自动添加

filetypes=[(label1,pattern1),(label2,pattern2)]文件显示过滤器
initialldir初始目录
initialfile初始文件
parent父窗口,默认根窗口
title窗口标题

【示例】文件对话框基本用法

# -*- coding: utf-8 -*-

from tkinter import *
from tkinter import messagebox
from tkinter import filedialog


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

    def createWidget(self):
        """用于创建组件"""
        Button(self, text="选择编辑的视频文件", command=self.test1).pack()
        self.show = Label(self, width=40, height=3, bg="green")
        self.show.pack()

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


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的程序")
    app = Application(root)
    root.mainloop()

简单输入对话框

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

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

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

【示例】简单对话框基本用法

# -*- coding: utf-8 -*-
from tkinter.simpledialog import *


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

    def createWidget(self):
        """用于创建组件"""
        Button(self, text="老杨你多大了?请输入", command=self.test1).pack()
        self.show = Label(self, width=40, height=3, bg="green")
        self.show.pack()

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


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的系统")
    app = Application(root)
    root.mainloop()

通用消息框

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

函数名说明
askokcancel(title,message,**options)OK/Cancel对话框
askquestion(title,message,**options)Yes/No问题对话框
askretrycancel(title,message,**options)Retry/Cancel问题对话框
showerror(title,message,**options)错误消息对话框
showinfo(title,message,**options)消息框
showwarning(title,message,**options)警告消息框
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter.messagebox import *


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

    def createWidget(self):
        """创建组件"""
        # a1 = showinfo(title="yyx系统", message="yyx大牛")
        # a2 = askokcancel(title="yyx系统", message="你是不是gay")
        # a3 = askquestion(title="yyx系统", message="你是不是傻逼")
        # a4 = showerror(title="yyx系统", message="报错了")
        # a5 = showwarning(title="yyx系统", message="无法进行这样的操作")


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的系统")
    app = Application(root)
    root.mainloop()

菜单

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

主菜单

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

上下文菜单

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

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox


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

        self.createWidget()

    def createWidget(self):
        """创建组件"""
        # 创建主菜单栏
        menubar = Menu(self)

        # 创建子菜单
        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)

        # 将主菜单栏加到根窗口
        self.master["menu"] = menubar

        # 文本编辑区
        self.textpad = Text(self, width=50, height=30)
        self.textpad.pack()

        # 创建上下菜单
        self.contentMenu = Menu(self.master)
        self.contentMenu.add_command(label="背景颜色", command=self.test)

        # 为右键绑定事件
        self.master.bind("<Button-3>", self.createContextMenu)

    def test(self):
        """用于测试"""
        pass

    def createContextMenu(self, event):
        """菜单在鼠标右键单击的坐标处显示"""
        self.contentMenu.post(event.x_root, event.y_root)


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的记事本系统")
    app = Application(root)
    root.mainloop()

记事本项目

【示例】创建一个记事本项目

# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox
from tkinter.filedialog import *
from tkinter.colorchooser import *


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

        # 对象参数
        self.textpad = None  # 初始化文本编辑区对象
        self.filename = None

        self.createWidget()

    def createWidget(self):
        """用于创建模块"""
        # 创建主菜单栏
        menubar = Menu(root)  # 创建一个主菜单对象

        # 创建子菜单
        menuFile = Menu(menubar)  # 将menuFile对象放在Menu中
        menuEdit = Menu(menubar)
        menuHelp = Menu(menubar)

        # 将子菜单加入到主菜单栏中
        menubar.add_cascade(label="文件(F)", menu=menuFile)  # 文件放到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.test)

        # 将主菜单栏加到根窗口
        self.master["menu"] = menubar

        # 文本编辑区
        self.textpad = Text(root)
        self.textpad.pack()

        # 创建上下菜单
        self.contentMenu = Menu(root)
        self.contentMenu.add_command(label="背景颜色", command=self.openAskColor)

        # 为右键绑定事件
        self.master.bind("<Button-3>", self.createContextMenu)

        # 绑定窗口变化事件
        root.bind('<Configure>', self.on_resize)

    def test(self):
        """用于测试专用"""
        pass

    def createContextMenu(self, event):
        """菜单在鼠标右键单击的坐标处显示"""
        self.contentMenu.post(event.x_root, event.y_root)

    def openFile(self):
        if self.textpad.get("1.0", "end") != "\n":
            if messagebox.askquestion("yyx的记事本", "请问是否要保存当前文本数据") == "yes":
                self.saveFile()
            else:
                self.textpad.delete("1.0", "end")
        try:
            with askopenfile(title="打开文本文件") as f:
                self.textpad.insert(INSERT, f.read())
                self.filename = f.name
                # print(self.filename)
        except:
            messagebox.showinfo("yyx的记事本", "未打开文件,请重试")

    def saveFile(self):
        if self.filename == None:
            # 说明未选择要存取的的文件
            messagebox.showinfo("yyx的记事本", "请选择你要存储的文件")
            try:
                with askopenfile(title="打开你要存储的文本文件") as f:
                    self.filename = f.name
            except:
                messagebox.showinfo("yyx的记事本", "未保存成功,请重试")
        else:
            # 如果当前文件板中存在数据,但是想存入新的文件
            if messagebox.askquestion("yyx的记事本", "请问要把当前的文本数据存入其他文件中吗") == "yes":
                try:
                    with askopenfile(title="打开你要存储的文本文件") as f:
                        self.filename = f.name
                    self.filename = None
                except:
                    messagebox.showinfo("yyx的记事本", "未保存成功,请重试")
            else:
                with open(self.filename, "w") as f:
                    c = self.textpad.get(1.0, END)
                    f.write(c)
                self.filename = None  # 再进行写入过后要将filename令为None值

    def newFile(self):
        """创建一个新的文件"""
        self.filename = asksaveasfilename(title="另存为", initialfile="未命名.txt", filetype=[("文本文档", "*.txt")],
                                          defaultextension=".txt")
        self.saveFile()

    def openAskColor(self):
        """打开背景颜色框"""
        s1 = askcolor(color="red", title="选择背景色")
        self.textpad.config(bg=s1[1])

    def on_resize(self, event):
        """textpad的窗口跟随root的窗口变化"""
        self.textpad.config(width=event.width, height=event.height)


if __name__ == '__main__':
    root = Tk()
    root.geometry("400x300")
    root.title("yyx的记事本")
    app = Application(root)
    root.mainloop()

python项目打包成exe可执行文件

在pycharm的Terminal终端输入如下命令:

pyinstaller -F xxxx.py

【注】相关参数如下:

        --icon=图标路径(pyinstaller -F --icon=my.ico XXXX.py)
        -F 打包成一个exe文件
        -W 使用窗口,无控制台
        -c 使用控制台,无窗口
        -D 创建一个目录,里面包含exe以及其他一些依赖性文件

在打包的过程中遇到一些问题:

1.如果提示AttributeError: 'str' object has no attribute 'decode'. Did you mean: 'encode'?
可以尝试将pyinstaller更新一下

pip install --upgrade pyinstaller

2.如果提示Please remove this package (located in D:\python3.10\lib\site-packages ) using "D:\python3.10\python.exe" -m pip uninstall pathlib then try again.
就将这个pathlib库删除即可

画图软件开发

开发一款简单的画图软件,包含如下功能:

  1. 画笔
  2. 矩形/椭圆绘制
  3. 清屏
  4. 橡皮擦
  5. 直线/带箭头的执行
  6. 修改画笔颜色、背景颜色
# -*- coding: utf-8 -*-
import tkinter
from tkinter import *
from tkinter import messagebox


class Application(Frame):
    def __init__(self, master=None, win_width=900, win_height=450):
        super().__init__(master)
        self.pack()
        self.master = master
        self.x = 0
        self.y = 0
        self.bgcolor = "#000000"
        self.fgcolor = "#ff0000"
        self.lastDraw = 0  # 表示最后绘制的图形
        self.startDrawFlag = False

        # 创建元素参数区域
        self.canvasPad = None
        self.win_width = win_width
        self.win_height = win_height

        self.createWidget()

    def createWidget(self):
        """创建控件"""
        # 创建绘图区
        self.canvasPad = Canvas(self.master, width=self.win_width, height=self.win_height * 0.9, bg=self.bgcolor)
        self.canvasPad.pack(fill=tkinter.BOTH, expand=True)

        # 创建功能按钮
        btn_start = Button(self.master, text="开始", name="start")
        btn_start.pack(side="left", padx="10")
        btn_pen = Button(self.master, text="画笔", name="pen")
        btn_pen.pack(side="left", padx="10")
        btn_rect = Button(self.master, text="矩形", name="rect")
        btn_rect.pack(side="left", padx="10")
        btn_clear = Button(self.master, text="清屏", name="clear")
        btn_clear.pack(side="left", padx="10")
        btn_eraser = Button(self.master, text="橡皮擦", name="eraser")
        btn_eraser.pack(side="left", padx="10")
        btn_line = Button(self.master, text="直线", name="line")
        btn_line.pack(side="left", padx="10")
        btn_lineArrow = Button(self.master, text="箭头直线", name="lineArrow")
        btn_lineArrow.pack(side="left", padx="10")
        btn_color = Button(self.master, text="颜色", name="color")
        btn_color.pack(side="left", padx="10")

        # 时间处理
        self.canvasPad.bind_class("Button", "<1>", self.eventManger)
        self.canvasPad.bind("<ButtonRelease-1>", self.stopDraw)

        """绑定快捷键区域"""
        # 绑定窗口变化
        self.master.bind("<Configure>", self.resize_canvas)

    def test(self):
        """用于测试专用"""
        pass

    def eventManger(self, event):
        """整个事件管理"""
        name = event.widget.winfo_name()
        print(name)
        if name == "line":
            self.canvasPad.bind("<B1-Motion>", self.myline)
        elif name == "lineArrow":
            self.canvasPad.bind("<B1-Motion>", self.mylineArrow)
        elif name == "rect":
            self.canvasPad.bind("<B1-Motion>", self.myRect)
        elif name == "pen":
            self.canvasPad.bind("<B1-Motion>", self.myPen)
        elif name == "eraser":
            self.canvasPad.bind("<B1-Motion>", self.myEraser)

    def startDraw(self, event):
        self.canvasPad.delete(self.lastDraw)
        if not self.startDrawFlag:
            self.startDrawFlag = True
            self.x = event.x
            self.y = event.y

    def stopDraw(self, evnet):
        self.startDrawFlag = False
        self.lastDraw = 0

    def myline(self, event):
        """创建直线"""
        self.startDraw(event)
        self.lastDraw = self.canvasPad.create_line(self.x, self.y, event.x, event.y, fill=self.fgcolor)

    def mylineArrow(self, event):
        """绘制箭头直线"""
        self.startDraw(event)
        self.lastDraw = self.canvasPad.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.canvasPad.create_rectangle(self.x, self.y, event.x, event.y, outline=self.fgcolor)

    def myPen(self, event):
        self.startDraw(event)
        self.canvasPad.create_line(self.x, self.y, event.x, event.y, fill=self.fgcolor)
        self.x = event.x
        self.y = event.y

    def myEraser(self, event):
        self.startDraw(event)
        self.canvasPad.create_rectangle(event.x - 6, event.y - 6, event.x + 6, event.y + 6,
                                                        fill=self.bgcolor)
        self.x = event.x
        self.y = event.y

    def resize_canvas(self, event):
        """canvas的窗口跟随root的窗口变化"""
        self.canvasPad.config(width=event.width, height=event.height * 0.9)


if __name__ == '__main__':
    win_width = 900
    win_height = 450
    root = Tk()
    root.geometry("600x500")
    root.title("yyx的画图软件")
    app = Application(root, win_width=win_width, win_height=win_height)
    root.mainloop()

相关文章:

  • MySQL主从服务器配置教程
  • 【C++】模板初阶和STL简介
  • Linux提权之metasploit 提权(五)
  • 登录-07.JWT令牌-登录后下发令牌
  • 编程题-连接两字母单词得到的最长回文串(中等)
  • 从网络基础到安全防护:网安运维小白的入门学习路线
  • python-静态方法和类方法
  • 蓝桥杯训练题目(一)—— 难度:简单(除了最后一题哈)
  • 《Python实战进阶》专栏 No 4:高效、简洁、强大之使用 FastAPI 构建高性能异步API
  • 请简述一下Prefab(预制体)的本质是什么?
  • 大白话TypeScript第三章高级特性学习
  • 使用Socket编写超牛的http服务器和客户端(一)
  • L1-6 剪切粘贴(字符串增删查改)
  • INTJ人格的本质
  • 【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter21-错误处理与调试
  • Linux 命令大全完整版(12)
  • apply的用法
  • 自动驾驶中planning为什么要把横纵向分开优化?
  • AI知识架构之RAG
  • 如何在VMware虚拟机的window10系统中安装网易mumu模拟器
  • 经济日报金观平:充分发挥增量政策的经济牵引力
  • 男子称喝中药治肺结节三个月后反变大增多,自贡卫健委回应
  • 特朗普签署行政命令推动深海采矿,被指无视国际规则,引发环境担忧
  • AI翻译技术已走向大规模商用,应用场景覆盖多个关键领域
  • 人民论坛:是民生小事,也是融合大势
  • 拍片无小事,牙齿也有故事