Python 基础总结(七)(Tkinter 的 GUI 的程序设计)

2,402 阅读10分钟

Snip20161104_1.png

英文不好的朋友也没有关系,下面会给出一些学习的例子:

首先我们来看一下常用的TKinter提供的核心小构件类

小构件类 描述
Button 按钮
Canvas 结构化图形,用于绘制图形,创建图形编辑器以及实现自定义小构件类
Checkbutton 单击复选按钮在值之间切换
Entry 文本域或称文本框
Frame 容器(可包含其他的小构件)
Label 显示文本或图像
Menu 显示下拉菜单和弹出菜单的菜单栏
Menubutton 下拉菜单的菜单按钮
Message 类似于标签显示文本,但能自动将文本放在给定宽高内
Radiobutton 单选按钮
Text 格式化的文本显示,支持内嵌图片和文本,允许用不同风格和属性显示和编辑文本

实例一

  • 我们来编写一个这样的例子,使用到了文本输入、复选按钮、单选按钮、输入框。可以学到如何绑定事件到小构件上。

Snip20161104_2.png

具体实现代码如下:

from tkinter import *

class WidgetsDemo:
    def __init__(self):    
    window = Tk()    
    window.title("组建demo")
    #添加一个多选按钮和单选按钮到frame1
    frame1 = Frame(window)
    frame1.pack()  #看下面的解释(包管理器)
    self.v1 = IntVar()
    cbtBold = Checkbutton(frame1, text = "粗体", variable = self.v1, command = self.processCheckbutton)
    self.v2 = IntVar()
    rbRed = Radiobutton(frame1, text = "红色", bg = "red", variable = self.v2, value = 1, command = self.processRaidobutton)
    rbYellow = Radiobutton(frame1, text="黄色", bg="yellow", variable=self.v2, value=2, command=self.processRaidobutton)
    cbtBold.grid(row = 1, column = 1)  #将cbtBold排列在frame1的网格第一行第一列(网格管理器也会在下面有解释)
    rbRed.grid(row=1, column=2)
    rbYellow.grid(row=1, column=3)
    #添加一个label、entry、button和message到frame2
    frame2 = Frame(window)frame2.pack()
    label = Label(frame2, text = "请输入名字")
    self.name = StringVar()
    entryName = Entry(frame2, textvariable = self.name)
    btGetName = Button(frame2, text = "获取名字", command = self.processButton)
    message = Message(frame2, text = "组建demo")
    label.grid(row = 1, column = 1)
    entryName.grid(row = 1, column = 2)
    btGetName.grid(row = 1, column = 3)
    message.grid(row = 1, column = 4)
    #添加一个texttext = Text(window)
    text.pack()
    text.insert(END, "Tip\n最好的学习TKinter方式是读")  #END表示插入到当前文本最后
    text.insert(END, "这是一些很好的学习例子使用他们")
    text.insert(END, "创建你自己的应用吧")window.mainloop()

def processCheckbutton(self):
    print("复选框按钮是" + ("被选中" if self.v1.get() == 1 else "未选中"))    
def processRaidobutton(self):        
    print("红色是" + ("被选中" if self.v2.get() == 1 else "未选中"))    
def processButton(self):        
    print("你的名字是:" + self.name.get())
WidgetsDemo()

代码中的一些解释:

包管理器

  • frame1.pack():这句话是指使用包管理器将frame1放到容器中。

包管理器可以管理在相同容器中的所有小构件的展示方式,我们可以使用 fill, expand, side来设置它们(小构件)的展示方式。官方给出一个例子来解释这个包管理器的实现原理,可以想象一些有弹性的材料和一个很小的矩形洞,然后按照小构件的打包顺序依次放进这个洞中,默认是从顶部开始往里放直到全部放置完毕,最后计算出容纳所有的小构件需要多大的边界并将它们全部移到主容器中。

如果你需要实现更复杂的布局,通常不得不使用额外的Frame构件,也能替换使用网格管理器来实现。

Note:不要在同一个窗口中混合使用网格管理器(grid)和包管理器(pack)。

下面样式(patterns)举例:

  1. 装满父容器
#一个小列表占满父容器
from Tkinter import *
root = Tk()
listbox = Listbox(root)
listbox.pack()
for i in range(20): 
    listbox.insert(END, str(i))
 mainloop()

Snip20161104_3.png

这个列表默认只会展示10行(超过的部分可滚动显示),当用户拖拽放大窗口的时候,Tkinter将会增加包裹列表的内边距,而不会放大列表本身,如下图:


Snip20161104_4.png

如果我们希望这个列表可以装满父容器,并且用户可以手动拖拽容器来达到放大列表的目的,可以使用fillexpand选项来实现。

#一个小列表占满父容器
from Tkinter import *
root = Tk()
listbox = Listbox(root)
listbox.pack(fill=BOTH, expand=1)
for i in range(20): 
    listbox.insert(END, str(i))
 mainloop()

Snip20161104_5.png

这个fill选项告诉包管理器这个小构件(widget)想填充这整个空间,并指定填充方式,BOTH:指定这个构件在水平和垂直两个方向伸展,X在水平方向伸展,Y在竖直方向伸展。
expand:当值非0时,是告诉包管理器分配额外的空间给构件,当父容器足够包裹所有的子构件时,多于的扩展空间就会填充在构件之间

2, 放置一定数量的小构件

当不使用任何参数的时候,构件就会按照一列排序

from tkinter import *

root = Tk()

w = Label(root, text="Red", bg="red", fg="white")
w.pack()
w = Label(root, text="Green", bg="green", fg="black")
w.pack()
w = Label(root, text="Blue", bg="blue", fg="white")
w.pack()

mainloop()

Snip20161104_6.png

当添加了fill=X后,就可以使构件和父容器一样宽

from tkinter import *

root = Tk()

w = Label(root, text="Red", bg="red", fg="white")
w.pack(fill=X)
w = Label(root, text="Green", bg="green", fg="black")
w.packfill=X()
w = Label(root, text="Blue", bg="blue", fg="white")
w.pack(fill=X)

mainloop()

Snip20161104_7.png

设置使构件并肩排列,并设置高度等于父容器

from tkinter import *

root = Tk()

w = Label(root, text="Red", bg="red", fg="white")
w.pack(fill=Y,side=LEFT)
w = Label(root, text="Green", bg="green", fg="black")
w.pack(fill=Y,side=LEFT)
w = Label(root, text="Blue", bg="blue", fg="white")
w.pack(fill=Y,side=LEFT)

mainloop()

Snip20161104_8.png

关于包管理器更多点击查看

网格管理器

  • 被誉为Tkinter中最灵活的几何管理器,也是最需要学习的一种管理器

    网格管理器把小部件放在一个二维的表里面,父部件(就是包涵其它部件的主部件)被分成很多行和列,并在行和列所组成的每个单元格里面可以容纳一个小部件。在设计对话框的时候,只用网格管理器是非常便捷的,当然也可以使用包管理器实现,但是使用包管理器需要大量额外的frame部件。

  • 样式(patterns)举例:

    使用网格管理器很容易,只需要告诉小部件在网格管理器中的第几行第几列,也不用预先指定网格管理器的大小,它会根据内部的小部件来设定自己的大小。

例1:

from tkinter import *
master = Tk()
Label(master, text="First").grid(row=0)
Label(master, text="Second").grid(row=1)

e1 = Entry(master)
e2 = Entry(master)

e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
mainloop()

当没有给出列数的时候就默认是0列.
运行上面的代码得到下面的窗口:


Snip20161106_1.png

注意每个小部件都呆在他们自己元件的中间,你可以使用sticky选项来修改这个设置,它有四个值如下:

含义
E 东(右)
S 南(下)
W 西(左)
N 北(上)

例如:

from tkinter import *
master = Tk()
Label(master, text="First").grid(row=0, sticky = W)
Label(master, text="Second").grid(row=1, sticky = W)

e1 = Entry(master)
e2 = Entry(master)

e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
mainloop()

Snip20161106_2.png

你可以设置让一个部件占用多个行或列。columnspan指定列数,rowspan指定行数。

from tkinter import *
master = Tk()
Label(master, text="First").grid(row=0, sticky = W)
Label(master, text="Second").grid(row=1, sticky = W)

e1 = Entry(master)
e2 = Entry(master)

e1.grid(row=0, column=1)
e2.grid(row=1, column=1)

button = Button(master,text = "button")
button.grid(row = 0, column = 2, columnspan = 2, rowspan = 2, padx = 5,
            pady = 5, sticky = W + E + N + S)
mainloop()

Snip20161106_3.png

学习更多关于网格管理器

位置管理器

可以使用位置管理器直接指定小部件在窗口中的x和y值来设置位置,但是它不能兼容所有的计算机,在不同分辨率的窗口上排列的位置可能会不同,所以我们尽量避免使用它。更多关于位置管理器

实例二

可以根据单选按钮来切换文字的颜色,并根据输入改变文字的内容:

from tkinter import *

class ChangeLabelDemo:
    def __init__(self):

        window = Tk()
        window.title = "改变labeldemo"

        frame1= Frame(window)
        frame1.pack()
        self.lb1 = Label(frame1, text = "Programming is fun")
        self.lb1.pack()

        frame2 = Frame(window)
        frame2.pack()
        label = Label(frame2, text = "输入")
        self.msg = StringVar()
        entry = Entry(frame2, textvariable = self.msg)
        btChangeText = Button(frame2, text = "改变text", command = self.processButton)
        self.v1 = StringVar()
        rbRed = Radiobutton(frame2, text = "Red", bg="red",variable=self.v1,value="R",command = self.processRadiobutton)
        rbYellow = Radiobutton(frame2, text="Yellow", bg="yellow", variable=self.v1, value="Y", command=self.processRadiobutton)

        label.grid(row = 1, column = 1)
        entry.grid(row = 1, column = 2)
        btChangeText.grid(row = 1, column = 3)
        rbRed.grid(row = 1, column = 4)
        rbYellow.grid(row = 1, column = 5)

        window.mainloop()

    def processButton(self):
        self.lb1["text"] = self.msg.get()
    def processRadiobutton(self):
        if self.v1.get() == "R":
            self.lb1["fg"] = "red"
        elif self.v1.get() == "Y":
            self.lb1["fg"] = "yellow"
ChangeLabelDemo()

Snip20161106_1.png

实例三

画图

from tkinter import *

class CanvasDemo:
    def __init__(self):
        window = Tk()
        window.title("CanvasDemo")

        self.canvas = Canvas(window, width = 200, height = 100, bg = "White")
        self.canvas.pack()

        frame = Frame(window)
        frame.pack()

        btRectangle = Button(frame, text = "长方形", command = self.displayRect)
        btOval = Button(frame, text="椭 圆", command=self.displayOval)
        btArc = Button(frame, text = "圆 弧", command = self.displayArc)
        btPolygon = Button(frame, text="多边形", command=self.displayPolygon)
        btLine = Button(frame, text=" 线 ", command=self.displayLine)
        btString = Button(frame, text="文 字", command=self.displayString)
        btClear = Button(frame, text="清 空", command=self.clearCanvas)

        btRectangle.grid(row = 1, column = 1)
        btOval.grid(row=1, column=2)
        btArc.grid(row=1, column=3)
        btPolygon.grid(row=1, column=4)
        btLine.grid(row=1, column=5)
        btString.grid(row=1, column=6)
        btClear.grid(row=1, column=7)

        window.mainloop()

    def displayRect(self):
        self.canvas.create_rectangle(10, 10, 190, 90, tags = "rect")
    def displayOval(self):
        self.canvas.create_oval(10, 10, 190, 90, tags = "oval", fill = "red")
    def displayArc(self):
        self.canvas.create_arc(10, 10, 190, 90, start = 0, extent = 90, width = 8, fill = "red", tags = "arc")
    def displayPolygon(self):
        self.canvas.create_polygon(10, 10, 190, 90, 30, 50, tags = "polygon")
    def displayLine(self):
        self.canvas.create_line(10, 10, 190, 90, fill = 'red', tags = "line")
        self.canvas.create_line(10, 90, 190, 10, width = 9, arrow = "last", activefill = "blue", tags = "line")
    def displayString(self):
        self.canvas.create_text(60, 40, text = "Hi,i am a string", font = "Tine 10 bold underline", tags = "string")
    def clearCanvas(self):
        self.canvas.delete("rect", "oval", "arc", "polygon", "line", "string")

CanvasDemo()

Snip20161106_2.png

实例四

菜单
可以使用Menu创建菜单,使用add_command给菜单添加条目

from tkinter import *

class MenuDemo:
    def hello(self):
        print("hello!")

    def __init__(self):


        window = Tk()
        window.title("Menu demo")


        menubar = Menu(window,bg="red")
        window.config(menu = menubar)

        #创建下拉菜单,并添加到菜单条
        operationMenu = Menu(menubar, tearoff = 0) 
        menubar.add_cascade(label = "操作", menu = operationMenu)
        operationMenu.add_command(label = "加", command = self.add)
        operationMenu.add_command(label="减", command=self.subtract)
        operationMenu.add_separator()
        operationMenu.add_command(label = "乘", command = self.multiply)
        operationMenu.add_command(label="除", command=self.divide)

        exitMenu = Menu(menubar, tearoff = 0)
        menubar.add_cascade(label = "退出", menu = exitMenu)
        exitMenu.add_command(label = "退出", command = window.quit)

        mainloop()

    def add(self):
        print("相加")
    def subtract(self):
        print("相减")
    def multiply(self):
        print("相乘")
    def divide(self):
        print("相除")

MenuDemo()

注意:在mac上菜单上在电脑屏幕的最上方的。
tearoff = 0表示菜单不能移除窗口,add_cascade添加菜单标签,add_command添加条目到菜单。


Snip20161106_3.png

实例五 弹出窗口和绑定鼠标事件

from tkinter import *

class MenuDemo:


    def __init__(self):
        root = Tk()

        # create a popup menu
        self.menu = Menu(root, tearoff=0)
        self.menu.add_command(label="Undo", command=self.hello)
        self.menu.add_command(label="Redo", command=self.hello)

        # create a canvas
        self.frame = Frame(root, width=512, height=512)
        self.frame.pack()

        # attach popup to canvas
        self.frame.bind("<Button-1>", self.popup)
        root.mainloop()

    def popup(self, event):
        self.menu.post(event.x_root, event.y_root)

    def hello(self):
        print("hello!")
MenuDemo()

Snip20161106_5.png

当点击鼠标左键就会出现一个小的菜单栏。可以使用bind方法来绑定事件。

widget.bind(event, handler)

Python中处理的事件和事件属性如下表:
事件:

事件 描述
<Bi-Motion> 当拖拽小部件的时候
<Button-i> Button-1、Button-2、Button-3表示左键、中间键、右键点击
<ButtonReleased-i> 当释放鼠标的时候
<Double-Button-i> 当双击鼠标的时候
<Enter> 当鼠标光圈进入小部件的时候
<Key> 当敲击一个键时候
<Leave> 当鼠标光圈离开小部件的时候
<Return> 当敲击“Enter”键时候,可以将键盘任意一个键和一个事件绑定
<Shift+A> 当敲击Shif+A的时候,可以和Alt、Control等组和
<Triple-Button-i> 点击鼠标3次的时候

事件属性:

事件属性 描述
char 从键盘输入的字符
keycode 从键盘输入的键代码
keysym 从键盘输入的键符号
num 鼠标键的数字(1、2、3)
widget 出发这个事件的小部件的对象
x和y 当前鼠标在小部件的坐标(像素)
x_root和y_root 当前鼠标相对于屏幕左上角的坐标(像素)

其他:

  • 显示图像:
    可以用如下PhotoImage类创建图像:
    photo = PhotoImage(file = imagefilename)
    可以使用标签、按钮、复选框、单选框的image属性添加图片,也可以使用Canvas的create_image创建图片。

  • 动画

    Canvas类可以用来开发动画,使用move(tags,dx,dy)来实现移动图片或文字等部件。
    canvas.after(times) 暂停
    canvas.update()重新显示画布

  • 滚动条
    可以使用在Text、Canvas、Listbox部件里面。

from tkinter import *

master = Tk()

scrollbar = Scrollbar(master)
scrollbar.pack(side=RIGHT, fill=Y)

listbox = Listbox(master, yscrollcommand=scrollbar.set)
for i in range(1000):
    listbox.insert(END, str(i))
listbox.pack(side=LEFT, fill=BOTH)

scrollbar.config(command=listbox.yview)

mainloop()

上面的方法设置部件yscrollcommand的滚动回调set,并设置scrollbar部件的方法yview。如果是在水平方向上,可以设置xscrollcommand,并使用xview方法。


Snip20161106_6.png
  • 标准对话框
import tkinter.messagebox
import tkinter.simpledialog
import tkinter.colorchooser

tkinter.messagebox.showinfo("showinfo", "this is an info msg")
tkinter.messagebox.showwarning("showinfo", "this is an info msg")
tkinter.messagebox.showerror("showinfo", "this is an info msg")

isYes = tkinter.messagebox.askyesno("askyesno", "continue")
print(isYes)
isOK = tkinter.messagebox.askokcancel("askobcancel","OK?")
print(isOK)

isYesNoCancel = tkinter.messagebox.askyesnocancel("sfs","OK?")
print(isYesNoCancel)

name = tkinter.simpledialog.askstring("ssf", "sdfsdf ")
print(name)

age = tkinter.simpledialog.askinteger("df","sdf")
print(age)