美文网首页
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