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


原文更丰富


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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 虽然这是tk的教程,但是我个人还是比较推荐使用pyqt来写窗口,实际上pyqt配合qtdesigner写的窗口不仅...
    遗步看风景阅读 39,866评论 3 39
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,658评论 22 664
  • 上午,有一姑娘来办事。初见人也是青春美貌,同行的还有她的一个女性长辈。 入办公室,那个女性长辈先问办事是不是在此地...
    粥粥摄影阅读 309评论 0 0
  • 大学生活就是这样,一到期末,忙的是忙的要死,闲的是闲的要死。我们宿舍就是后者,室友们大多拿着手机各种刷,躺在舒适...
    红衣衣衣阅读 227评论 0 0
  • 发胖的原因不在于吃的啥,而在于摄入的热量的多寡。摄入热量远大于消耗热量的话,发胖则是必然。 生面粉和大米的热量几乎...
    朵小阳阅读 494评论 0 1