美文网首页
python进阶-01-GUI

python进阶-01-GUI

作者: 西海岸虎皮猫大人 | 来源:发表于2020-03-15 12:18 被阅读0次

    1 概述

    tkinter:
    标准GUI库,适合小型程序
    https://docs.python.org/3.7/library/tk.html
    wxPython: 大型程序
    http://effbot.org/tkinterbook/

    第一行程序
    # coding=utf-8
    from tkinter import *
    from tkinter import messagebox
    
    # 窗口
    root = Tk()
    
    btn01 = Button(root)
    btn01["text"] = "点击"
    
    # 布局管理器,压缩窗口
    btn01.pack()
    
    # e封装事件对象
    def songhua(e):
        messagebox.showinfo("Message", "送你一朵花")
    
    # 鼠标左键单击绑定事件
    btn01.bind("<Button-1>", songhua)
    
    # 窗口显示,进入事件循环,监听用户操作
    root.mainloop()
    

    2 PEP8规范

    规范文档链接:
    https://www.python.org/dev/peps/pep-0008/
    alt+回车 可设置忽略警告
    注释#后加空格等

    tkinter主窗口大小位置
    # 设置标题
    root.title('第一个GUI程序')
    # 宽500高300 距左100 距上300
    root.geometry(''500*300+100+300)
    

    3 整体描述

    图形由组件组成
    组件继承关系
    button label canvas... ->Widget -> BaseWidget -> Misc
    Tk -> Wm(通信)
    Tk -> Misc

    代码中查看类结构

    如Button,ctrl + 单击查看源码
    右键->diagram->show diagram 查看类关系图,可以切换关系图布局

    常用组件汇总

    4 GUI程序经典写法

    继承Frame框架,可看做一个容器,用于放其他组件

    """测试经典GUI程序写法,使用面向对象的方式"""
    
    #coding=utf-8
    from tkinter import *
    from tkinter import messagebox
    
    # 继承Frame
    class Application(Frame):
        """一个经典的GUI程序"""
        def __init__(self, master=None):
            super().__init__(master)
            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朵玫瑰花")
    
    
    root = Tk()
    root.geometry("400x100+200+300")
    root.title("一个经典的GUI程序")
    # 主窗口对象传给构造函数
    app = Application(master=root)
    root.mainloop()
    

    5 Label标签

    所有组件占的区域都是矩形

     def createWidget(self):
            """创建组件"""
            # 文字 宽 高 背景色 前景色 字体
            self.Label01 = Label(self,text="DFUN",width=10,height=2,bg="black",fg="white",font=("黑体",30))
            self.Label01.pack()
            # 显示图像,如果是局部变量本方法执行完毕后图像销毁
            global photo
            photo = PhotoImage(file="imgs/logo.gif")
            self.Label02 = Label(self, image=photo)
            self.Label02.pack()
    
            # 显示多行文本 边界 右对齐
            self.label04 = Label(self, text="DFUN\nVincent\nWu", borderwidth=1,relief="solid",justify="right")
    

    6 选项 - Options

    设置Options - 三种方式

    1.创建组建时使用命名参数

    self.lable01 = Label(self,text="DFUN",width=10,height=2,bg="black",fg="white",font=("黑体",30))
    

    # 使用字典
    self.lable01 = Label(self,dict(text="DFUN",width=10,height=2,bg="black",fg="white",font=("黑体",30)))
    

    2.创建后使用字典索引方式

    lable01["text"] = "ccc" 
    

    底层是使用运算符重载
    3.创建后使用config()方法

    7 组件 - Button

       def createWidget(self):
            """创建组件"""
            # anchor定位,默认居中,N北S南W西E东SE东南... 
            self.btn01 = Button(root, text="登录", width=6, height=3, anchor=NE, command=self.login)
            self.btn01.pack()
    
            # 按钮设置背景图片
            global photo
            photo = PhotoImage(file="imgs/start.gif")
            self.btn02 = Button(root, image=photo, command=self.login)
            self.btn02.pack()
            # 禁用
            # self.btn02.config(state="disabled")
        def login(self):
            messagebox.showinfo("python学习","登录成功!")
    

    阅读文档体会,英文对于描述科技语言的优势,从句说明

    8 Entry | StringBar

    Entry 单行文本框

    """模拟登录功能,测试Entry"""
    # coding=utf-8
    from tkinter import *
    from tkinter import messagebox
    
    
    # 继承Frame
    class Application(Frame):
        """一个经典的GUI程序"""
        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.pack()
            # StringVar为Tkinter中的变量类型,此处用来关联Entry
            v1 = StringVar()
            self.entry01 = Entry(self, textvariable=v1)
            self.entry01.pack()
            v1.set("admin")
            print(v1.get())
            print(self.entry01.get())
    
            # 创建密码框
            self.label02 = Label(self, text="密码")
            self.label02.pack()
            v2 = StringVar()
            self.entry02 = Entry(self, textvariable=v2, show="*")
            self.entry02.pack()
    
            Button(self, text="登录", command=self.login).pack()
    
        def login(self):
            username = self.entry01.get()
            pwd = self.entry02.get()
            print("数据库密码校验")
            print("用户名:"+username)
            print("密码:" + pwd)
            messagebox.showinfo("python学习系统", "登录成功!")
    
    
    root = Tk()
    root.geometry("400x300+200+300")
    root.title("一个经典的GUI程序")
    # 主窗口对象传给构造函数
    app = Application(master=root)
    root.mainloop()
    

    9 多行文本框 - Text

    import webbrowser
    
    def createWidget(self):
        self.w1 = Text(root, width=40, height=12, bg="gray")
        self.w1.pack()
        # 第1行第0列插入内容
        self.w1.insert(1.0,"hello\nworld")
    
        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="添加标记", command=self.testTag).pack(side="left")
    
    
    def insertText(self):
        # 光标处插入
        self.w1.insert(INSERT, "Vincent")
        # 末尾插入
        self.w1.insert(INSERT, "Wu")
    
    def returnText(self):
        # 返回第1行第1列至第6列内容
        print(self.w1.get(1.2,  1.6))
        # 返回所有内容
        print(self.w1.get(1.0,  END))
    
    def addImage(self):
        self.photo = PhotoImage(file="imgs/logo.gif")
        self.w1.image_create(END, image=self.photo)
    
    def addWidget(self):
        b1 = Button(self, w1, text="按钮")
        self.w1.window_create(INSERT, window=b1)
    
    def testTag(self):
        # 删除所有
        self.w1.delete(1.0, END)
        # 光标处插入
        self.w1.insert(INSERT, "good good study\nVincent Wu")
        # 添加标记
        self.w1.tag_add("good", 1.0, 1.9)
        # 标记改变样式
        self.w1.tag_config("good", background="yellow", foreground="red")
        
        # 绑定事件
        self.w1.tag_add("baidu", 4.0, 4.2)
        self.w1.tag_config("baidu", underline=True)
        self.w1.tag_bind("baidu", "<Button-1>", self.webshow)
    
    def webshow(self, event):
        # 调用默认浏览器打开百度
        webbrowser.open("https://www.baidu.com")
    

    10 Radiobutton | Checkedbutton

    单选 - Radiobutton
    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="right")
    
        Button(self, text="确定", command=self.confirm).pack(side="left")
    
    def confirm(self):
        messagebox.showinfo("测试", "选择的性别:"+self.v.get())
    
    复选 - Checkedbutton
    def createWidget(self):
        self.codeHobby = IntVar()
        self.videoHobby = IntVar()
    
        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):
        messagebox.showinfo(self.c1.get() + ", " + self.c2.get())
    

    11 Canva画布

    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/logo.gif")
        self.canvas.create_image(150, 170, image=photo)
    

    12 布局管理器 - Grid

    三种布局方式

    pack 垂直水平排列
    grid 网格排列
    place 位置管理器

    Grid布局实现登录功能
    """通过grid布局实现登录页面"""
    # coding=utf-8
    from tkinter import *
    from tkinter import messagebox
    
    
    # 继承Frame
    class Application(Frame):
        """一个经典的GUI程序"""
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            """通过grid布局实现登录页面"""
            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)
    
        def login(self):
            username = self.entry01.get()
            pwd = self.entry02.get()
            print("数据库密码校验")
            print("用户名:"+username)
            print("密码:" + pwd)
            messagebox.showinfo("python学习系统", "登录成功!")
    
    
    
    root = Tk()
    root.geometry("400x300+200+300")
    root.title("一个经典的GUI程序")
    # 主窗口对象传给构造函数
    app = Application(master=root)
    root.mainloop()
    

    13 Grid布局 - 计算器

    # coding=utf-8
    """Grid布局实现计算器布局"""
    from tkinter import *
    from tkinter import messagebox
    
    
    # 继承Frame
    class Application(Frame):
        """一个经典的GUI程序"""
        def __init__(self, master=None):
            super().__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).grid(row=0, column=0, columnspan=4, pady=10)
            for rindex, r in enumerate(btnText):
                for cindex, c in enumerate(r):
                    if c == "=":
                        Button(self, text=c, width=2).grid(row=rindex+1, column=cindex, rowspan=2, sticky=NSEW)
                    elif c == 0:
                        Button(self, text=c, width=2).grid(row=rindex+1, column=cindex, columnspan=2, sticky=NSEW)
                    elif c == ".":
                        Button(self, text=c, width=2).grid(row=rindex+1, column=cindex+1, sticky=NSEW)
                    else:
                        Button(self, text=c, width=2).grid(row=rindex+1, column=cindex, sticky=NSEW)
    
    root = Tk()
    root.geometry("200x250+200+300")
    root.title("计算器")
    # 主窗口对象传给构造函数
    app = Application(master=root)
    root.mainloop()
    

    14 Pack布局

    竖直排列或水平排列

    """Pack布局实现琴键效果"""
    # coding=utf-8
    """测试pack布局管理"""
    
    from tkinter import *
    
    root = Tk()
    root.geometry("700x220")
    
    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()
    

    15 Place布局

    坐标布局,更加灵活

    # coding=utf-8
    from tkinter import *
    
    root = Tk()
    root.geometry("500x300")
    root.title("布局管理")
    root["bg"] = "white"
    
    f1 = Frame(root, width=200, height=200, bg="green")
    # 距左30 距上30
    f1.place(x=30, y=30)
    # relx 宽度root*0.2 与x同时存在则叠加 
    Button(root, text="DFUN").place(relx=0.2, x=100, y=20, relwidth=0.2, relheight=0.5)
    Button(f1, text="Vincent").place(relx=0.6, rely=0.7)
    Button(f1, text="Wu").place(relx=0.5, rely=0.2)
    root.mainloop()
    

    16 Place布局- 扑克牌效果

    # coding=utf-8
    """Place布局实现扑克布局"""
    from tkinter import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            self.photo = PhotoImage(file= "imgs/puke/puke1.gif")
            self.puke1 = Label(self.master, image=self.photo)
            self.puke1.place(x=10, y=50)
    
            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=10+i*40, y=50)
    
            # 为所有的pukes增加事件
            self.pukes[0].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() == 50):
                event.widget.place(y=30)
            else:
                event.widget.place(y=50)
    
    root = Tk()
    root.geometry("600x250+200+300")
    root.title("计算器")
    # 主窗口对象传给构造函数
    app = Application(master=root)
    root.mainloop()
    

    17 事件

    鼠标和键盘事件

    左键(滚轮\右键)按下
    左键释放
    按住左键移动
    双击
    进入某一区域
    离开某一区域
    按下a键
    ...(详见文档)

    event对象属性

    num 鼠标按键
    type 事件类型
    widget 引起事件的组件
    width,height 组件改变后大小
    x,y 鼠标当前位置,相对于父容器
    x_root, y_root 鼠标当前位置,相对于整个容器

    char 按键字符
    keycode 按键编码
    keysym 按键名称

    # coding=utf-8
    """测试鼠标、键盘事件"""
    from tkinter import *
    
    root = Tk()
    root.geometry("500x300")
    root.title("布局管理")
    root["bg"] = "white"
    
    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 press_a_test(event):
        print("press a")
    
    def keyboardTest(event):
        print(event.keycode, event.char, event.keysym)
    
    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)
    root.bind("<KeyRelease-a>", release_a_test)
    
    root.mainloop()
    

    18 lambda表达式

    lambda表达式定义一个匿名函数
    格式:
    lambda 参数值列表: 表达式
    适合最简单的处理情况
    不复用,没必要起名

    add3args = lambda x,y,z:x+y+z
    print(add3args(10,20,30))
    # 任意多个参数求和
    lambda *args:sum(args)
    
    # coding=utf-8
    """lambda表达式事件传参"""
    from tkinter import *
    
    root = Tk()
    root.geometry("500x300")
    root.title("lambda事件绑定测试")
    
    def mouseTest1():
        print("command方式,简单情况:不涉及获取event对象")
    
    def mouseTest2(a, b):
        print(a, b)
    
    
    Button(root, text="测试command1", command=mouseTest1).pack(side="left")
    Button(root, text="测试command2", command=lambda: mouseTest2(10, 20)).pack(side="left")
    
    root.mainloop()
    

    19 事件绑定方式汇总

    组件对象

    通过command属性
    通过bind()方法

    组件类绑定

    w.bind_class("Widget", "event", eventhandler)

    20 选择项 - OptionMenu

    下拉选项 - OptionMenu
    v  = StringVar(root)
    om = OptionMenu(root, v, "Vincent", "Wu", "DFUN")
    om["width"] = 10
    om.pack()
    
    # 取值
    v.get()
    
    移动滑块 - Scale
    # 10到50 宽200 每5标记一次
    s1 = Scale(root, from=10, to=50, length=200, tickIntegerval=5, orient=HORIZONTAL, command=test1) 
    
    # 滑块的值会传给value
    def test1(value):
        print(value)
    

    21 颜色选择框 | 文件选择框

    颜色选择框 - askcolor
    def test1():
        s1 = askcolor(color="red", title="选择背景色")
        print(s1)
        root.config(bg=s1[1])
    
    Button(root, text="选择背景色", command=test1).pack()
    
    文件对话框
    def test2():
        # 默认打开目录 文件类型
        f = askopenfilename(title="上传文件", initaldir="f:", filetypes=["视频文件", ".mp4"])
        # 打印文件路径
        print(f)
    Button(root, text="请选择文件", command=test2).pack()
    

    22 简单输入对话框 | 通用消息框 | ttk子模块

    简单输入对话框
    from tkinter.simpledialog import *
    def test01:
        a = askinteger(title="输入年龄",prompt="请输入年龄
    ",initialvalue=18,minvalue=1,maxvalue=150)
        show["text"]=a
    

    此外还有,float\String,详见文档

    通用消息框
    from tkinter.messagebox import *
    a1 = showinfo(title="DFUN",message="Python400")
    print(a1)
    

    此外,还有是\否,重试\取消,错误消息,警告消息,详见文档

    ttk 子模块

    tkinter 模块下的组件,整体风格老旧,为弥补推出了ttk组件,更加美观功能强大,新增了LabeledScale(带标签的
    Scale)、Notebook(多文档窗口)、Progressbar(进度条)、Treeview(树)等组件
    详见文档:
    https://docs.python.org/3.7/library/tkinter.ttk.html

    项目确实需要用到复杂的界面,推荐使用
    wxpython 或者pyQt.

    23 菜单

    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_seperator()
        menuFile .add_command(label="推出", accelerator="ctrl+q", command=self.test)
    
        root["menu"] = menubar
    
        # 文本编辑区
        self.textpad = Text(root, width=50, height=20)
        self.textpad.pack()
    
        # 上下文菜单
        self.contextMenu = Menu(root)
        self.contextMenu.add_command(label="背景颜色", command=self.test) 
    
        root.bind("<Button-3>", self.createContextMenu)
    
    # 单击处生成右键菜单
    def createContextMenu(self, event):
        self.contextMenu.post(event.x_root, event.y_root)
    

    24 记事本项目 - 打开 | 保存 | 退出

    # coding=utf-8
    """练习:实现记事本"""
    from tkinter import *
    from tkinter.filedialog import *
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            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.open_file)
            menuFile.add_command(label="保存", accelerator="ctrl+s", command=self.save_file)
            # 添加分割线
            menuFile.add_separator()
            menuFile.add_command(label="退出", accelerator="ctrl+q", command=self.quit)
    
            root["menu"] = menubar
    
            # 文本编辑区
            self.textpad = Text(root, width=50, height=20)
            self.textpad.pack()
    
            # 上下文菜单a
            self.contextMenu = Menu(root)
            self.contextMenu.add_command(label="背景颜色", command=self.test)
    
            root.bind("<Button-3>", self.createContextMenu)
    
            # 单击处生成右键菜单
    
        def createContextMenu(self, event):
            self.contextMenu.post(event.x_root, event.y_root)
    
        def test(self):
            print("背景色")
    
        def open_file(self):
            self.textpad.delete(1.0, END)
            with askopenfile(title="打开文本文件") as f:
                self.textpad.insert(INSERT, f.read())
                self.filename = f.name
    
        def save_file(self):
            with open(self.filename, "w") as f:
                c = self.textpad.get(1.0, END)
                f.write(c)
    
        def exit(self):
            root.quit()
    
    root = Tk()
    root.geometry("200x250+200+300")
    root.title("记事本")
    app = Application(master=root)
    root.mainloop()
    

    遗留问题:
    中文报错

    绑定快捷键
    root.bind("<Control-n>", lambda event:self.new_file())
    root.bind("<Control-o>", lambda event: self.open_file())
    root.bind("<Control-s>", lambda event: self.save_file())
    root.bind("<Control-q>", lambda event: self.exit())
    
    修改背景色
        def change_bg(self):
            s1 = askcolor(color="red", title="选择背景色")
            self.textpad.config(bg=s1[1])
    
    发布exe文件

    1.安装PyInstaller模块
    setting->Project Interpreter->点+号->搜索PylInstaller
    需要翻墙...
    2.终端执行
    pyinstaller -F l_notepad.py

    相关文章

      网友评论

          本文标题:python进阶-01-GUI

          本文链接:https://www.haomeiwen.com/subject/qjchehtx.html