GUI编程,Tkinter库和布局


date: 2017-12-12 22:00:00
status: public
title: 'GUI编程,Tkinter库和布局'
tags: Python, Tkinter,GUI,layout


GUI编程

最近看了下Python的GUI编程,官方Wiki地址是TkInter - Python Wiki,上面有很多文档,如果要深入这块,这些文档会很有帮助的。

这里简单说下Tkinter。Tkinter是Python的标准GUI(Graphical User Interface)包,它是基于Tcl/Tk的面向对象层。这里可以简单理解为Tkinter提供了一些函数,通过简单地import tkinter模块,你就可以使用这些函数构建图形窗口。虽然Tkinter不是Python唯一的GUI库,但是是最常用的。就我而言,我只要在之后做爬虫时能做一些简单的界面就可以,所以Tkinter能满足我的需求。

布局Layout

我以为布局其实是整个开发过程中最重要的部分,Tkinter提供了两种布局的方法:packgrid布局管理器,除此之外还提供了用于合并插件(widget)的Frame和用于微调插件位置的place布局管理器。

一般来说,如果你的界面设计包含:

  • 一个插件占满整个窗口或Frame(或其他容器)
  • 插件是一个接着一个排列的(上下或左右并列)

这两种情况推荐使用pack布局管理器,pack管理器实现起来很简单,也正因为如此,它在实现具有复杂结构的界面时操作起来就很复杂了(不是不能实现)。

另一个grid布局管理器会在逻辑上把整个界面分割成网格状,就像一个二维数组一样,从左上角(row=0, column=0)开始,向下向右延伸。实际过程中,我们在设计界面时,总是会有意无意的将插件分类,将一些有关的插件放入一个容器,然后再将容器进行排列。这条思路其实和grid的布局管理器的思路不谋而合,所以说,我个人十分推荐使用grid布局管理器。

place布局管理器通常是和pack或者grid管理器一起使用的(pack和grid管理器不能混合使用)。它可以通过指定(x=x0, y=y0)的方式直接将一个插件放置在一个容器如Frame、LabelFrame或者窗口中。

在整个界面设计过程中,所有的坐标轴都是以左上角作为原点,x轴向右延伸,y轴向下延伸,也就是说右下角代表了整个坐标轴的最大值。

到这里,我总结一下我的GUI开发的思路:

  1. 将你的要实现的程序拆分成若干个功能块,想好每个功能块要用到什么插件;
  2. 将这些功能块以网格状的形式排列起来(这里某个功能块是可以占据多行或者多列的,也就是说功能块A可以是grid(row=0, column=0)+grid(row=0, column=1)合起来,这是逻辑上的表达,实际代码用columnspan实现),排列好后,给每个功能块确定一下索引值,大概估算一下每个功能块的大小。
  3. 功能块对应到代码就是Frame或LabelFrame,然后我们针对每个块编写代码向功能块里添加插件(widgets),同时通过place管理器来调整每个插件相对于功能块的位置。

Tkinter库

剩下的就是具体实现的问题了,比如说怎么实现功能块?怎么往里面添加widgets?怎么调整widgets的外观?怎么关联两个widget事件?等等等等。

我不打算把这widgets啊,事件啊,属性啊,一一列在这里,完全没有必要。因为这些widgets的配置项其实大同小异,我们要做到的仅仅是了解:(1)widgets都有哪些;(2)具体的参数去哪里查。

针对(1),我准备了一个程序在下面,它实现了几乎所有的插件,并且把它们排列在一个窗口里,代码包含了几乎所有的常用配置项,如果有什么额外需求,比如说调整边框大小之类的,可以通过查阅文档自行修改代码即可。

针对(2), 我推荐两个文档,一个是上文所提的官方wiki,第二是一个外国小伙子写的An Introduction to Tkinter,他只写完了TK部分,不过已经足够用了。

一个例子

本来我是打算写个计算器作为GUI编程的入门程序,后来我在一个一个试widget代码时,突然想到不如直接试着把所有的widgets放到一个窗口里,排列起来,因此有了如下程序。

另一个值得一提的一点是,我也在网上看到了一段写GUI的代码,它是用类的方式实现的,这个类继承于Application父类,然后在这个类的__init__方法中实现整个GUI代码。我不确定哪一种才是标准写法,但只要你熟悉这些代码,改一下应该都不难。

效果图

GUI编程,Tkinter库和布局_第1张图片
Widgets效果图.jpg

完整代码

import tkinter as tk
import tkinter.messagebox

root = tk.Tk()
root.title('Widgets')

width = 960
height = 800
screenwidth = root.winfo_screenwidth()  
screenheight = root.winfo_screenheight()  
size = '%dx%d+%d+%d' % (width, height, (screenwidth - width)/2, (screenheight - height)/2)
root.geometry(size) # width*height + pos_x + pos_y

# label
# 最上方的那个黄条,后面的插件如果有事件都会修改这个黄条的值
myStr = tk.StringVar()
myStr.set('Label Default')
isHit = False

label = tk.Label(root,
    textvariable = myStr,
    bg = 'yellow', # =background
    font = ('Microsoft YaHei', 20),
    width = 60,  # 本例这里两个值单位是字节,不是像素
    height = 1
    )

label.grid(row=0, column=0,columnspan=6)

# button
# 每种插件都会包含在一个LabelFrame里
lf_button = tk.LabelFrame(root, width=96, height=96, text='Button')  
lf_button.grid(row=1, column=0, sticky='w',padx=10)

def button_click():
    global isHit
    if isHit == False:
        isHit = True
        myStr.set('Label #1')
    else:
        isHit = False
        myStr.set('Label #2')

button = tk.Button(lf_button,
    text='Mod',
    width=10,
    height=3,
    command=button_click
    )
button.place(x=6,y=1)

# Scale
lf_Scale = tk.LabelFrame(root, width=460, height=120, text='Scale - HORIZONTAL')  
lf_Scale.grid(row=2, column=0, sticky='w',padx=10,  columnspan=4)

def scale_click(v):
    myStr.set('You have selected '+ v)

scale = tk.Scale(lf_Scale,
    label='Scroll Me',
    from_ = 0,
    to = 100,
    orient = tk.HORIZONTAL,
    length = 440,
    showvalue = 1,
    tickinterval = 10,
    resolution = 1,
    command = scale_click
    )
scale.place(x=1,y=1)

# entry and text
lf_entry_text = tk.LabelFrame(root, width=160, height=240, text='Entry and Text')  
lf_entry_text.grid(row=1, column=4, rowspan=2, padx = 6,  sticky='w')

entry = tk.Entry(lf_entry_text, relief='solid')
entry.place(x=6, y=1)

def button2_click():
    var = entry.get()
    textbox.insert('insert',var)


def button3_click():
    var = entry.get()
    textbox.insert('end',var)

button2 = tk.Button(lf_entry_text,
    text='inset curr',
    command=button2_click
    )
button2.place(x=6,y=32)

button3 = tk.Button(lf_entry_text,
    text='inset end',
    command=button3_click
    )
button3.place(x=80,y=32)

textbox = tk.Text(lf_entry_text, 
    width=20,
    height=10,
    relief='solid'
    )
textbox.place(x=6,y=72)

# Listbox
lf_Listbox = tk.LabelFrame(root, width=260, height=240, text='Listbox')  
lf_Listbox.grid(row=1, column=5, rowspan=2,  sticky='w')

myList = tk.StringVar()
myList.set(['default0','default1','default2'])
listbox = tk.Listbox(lf_Listbox, 
    listvariable=myList,
    height=11
    )

# 为Listbox关联scrollbar
scrollbar = tk.Scrollbar(lf_Listbox,width=30, orient="vertical",command=listbox.yview)
listbox.configure(yscrollcommand=scrollbar.set)
scrollbar.place(x=120, y=1, height=201) 

for item in range(100):
    listbox.insert(0,item)

listbox.delete(0)

def button4_click():
    val = listbox.get(listbox.curselection())
    myStr.set(val)

button4 = tk.Button(lf_Listbox,
    text='display',
    width=10,
    height=2,
    command=button4_click
    )

listbox.place(x=6, y=1)
button4.place(x=164, y=1)


# Radio Button
lf_Radio = tk.LabelFrame(root, width=96, height=96, text='Radio')  
lf_Radio.grid(row=1, column=2, sticky='w')

varRadio = tk.StringVar()
def radiobutton_click():
    myStr.set('You have selected ' + varRadio.get())

radio1 = tk.Radiobutton(lf_Radio,
    text='A',
    value='A',
    variable = varRadio,
    command=radiobutton_click
)

radio2 = tk.Radiobutton(lf_Radio,
    text='B',
    value='B',
    variable = varRadio,
    command=radiobutton_click
)



radio1.place(x=6, y=0)
radio2.place(x=6, y=30)


# Check Button
lf_Check = tk.LabelFrame(root, width=96, height=96, text='Check')  
lf_Check.grid(row=1, column=1, sticky='w')

varCheck1 = tk.IntVar()
varCheck2 = tk.IntVar()

def checkbutton_click():
    if (varCheck1.get() == 1) & (varCheck2.get() == 1):
        myStr.set("Apple and Banana")
    elif (varCheck1.get() == 1) & (varCheck2.get() == 0):
        myStr.set("Apple")
    elif (varCheck1.get() == 0) & (varCheck2.get() == 1):
        myStr.set("Banana")
    else:
        myStr.set("Nothing")

checkbutton1 = tk.Checkbutton(lf_Check,
    text = 'Apple',
    variable = varCheck1,
    onvalue = 1,
    offvalue = 0,
    command = checkbutton_click
    )

checkbutton2 = tk.Checkbutton(lf_Check,
    text = 'Banana',
    variable = varCheck2,
    onvalue = 1,
    offvalue = 0,
    command = checkbutton_click
    )

checkbutton1.place(x=6, y=0)
checkbutton2.place(x=6, y=30)

# Menubar
menubar = tk.Menu(root)

filemenu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='File', menu=filemenu)

def dojob():
    pass

filemenu.add_command(label='New', command=dojob)
filemenu.add_command(label='Open', command=dojob)
filemenu.add_command(label='Save', command=dojob)
filemenu.add_separator()
filemenu.add_command(label='Exit', command=root.quit)

editmenu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='Edit', menu=editmenu)
editmenu.add_command(label='Cut', command=dojob)
editmenu.add_command(label='Copy', command=dojob)
editmenu.add_command(label='Paste', command=dojob)

submenu = tk.Menu(editmenu)
editmenu.add_cascade(label='Submenu', menu=submenu, underline=0)
submenu.add_command(label="Submenu1", command=dojob)
root.config(menu=menubar)

# Message Box
lf_MessageBox = tk.LabelFrame(root, width=96, height=96, text='MessageBox')  
lf_MessageBox.grid(row=1, column=3, sticky='w')
def show_msg():
    tk.messagebox.showinfo(title='HI', message='Yahaha')

button4 = tk.Button(lf_MessageBox, 
    text='Bing', 
    width=10,
    height=3,
    command=show_msg
    )
button4.place(x=6,y=2)

# Menu Button
lf_MenuButton = tk.LabelFrame(root, width=96, height=96, text='MenuButton') 
lf_MenuButton.grid(row=3, column=0, padx=10,sticky='w')

def menubutton_click1():
    myStr.set('You selected Apple.')

def menubutton_click2():
    myStr.set('You selected Banana.')

def menubutton_click3():
    myStr.set('You selected Coconut.')

menubutton = tk.Menubutton(lf_MenuButton, 
    text="Fruits", 
    relief='raised'
    )

menubutton.grid()
menubutton.menu = tk.Menu(menubutton,  tearoff=0)
menubutton["menu"] = menubutton.menu
menubutton.menu.add_command(label='Apple', command=menubutton_click1)
menubutton.menu.add_command(label='Banana', command=menubutton_click2)
menubutton.menu.add_command(label='Coconut', command=menubutton_click3)

menubutton.place(x=6,y=2)

# Option Menu
lf_OptionMenu = tk.LabelFrame(root, width=96, height=96, text='OptionMenu') 
lf_OptionMenu.grid(row=3, column=1, sticky='w')

Options = ['Apple', 'Banana', 'Coconut']
op_var = tk.StringVar()
op_var.set(Options[0])
optionmenu = tk.OptionMenu(lf_OptionMenu,
    op_var,
    *Options
    )

def change_dropdown(*args):
    myStr.set("You have selected " + op_var.get())

op_var.trace('w', change_dropdown)

optionmenu.place(x=6,y=2)

# Spin Box
lf_SpinBox = tk.LabelFrame(root, width=96, height=96, text='SpinBox') 
lf_SpinBox.grid(row=3, column=2, sticky='w')

spinbox = tk.Spinbox(lf_SpinBox, 
    from_=0, 
    to=10,
    width=8
    )
spinbox.place(x=6,y=2)

# Paned Window
lf_PanedWindow = tk.LabelFrame(root, width=570, height=96, text='PanedWindow') 
lf_PanedWindow.grid(row=3, column=3, columnspan=3, sticky='w')

panedwindow = tk.PanedWindow(lf_PanedWindow, width=560)

pw_left = tk.Label(panedwindow, text="left pane")
panedwindow.add(pw_left)
pw_mid = tk.Label(panedwindow, text="mid pane")
panedwindow.add(pw_mid)
pw_right = tk.Label(panedwindow, text="right pane")
panedwindow.add(pw_right)

panedwindow.place(x=6,y=2)

# Canvas
lf_Canvas = tk.LabelFrame(root, width=935, height=410, text='Canvas') 
lf_Canvas.grid(row=4, column=0, columnspan=6, padx=10, sticky='w')

canvas = tk.Canvas(lf_Canvas,
    bg = 'grey',
    heigh = 380,
    width = 900
    )
x0, y0, x1, y1 = 0,30,0,300
line = canvas.create_line(x0, y0, x1, y1)
oval = canvas.create_oval(x0+50, y0+50, x1+150, y1+50, fill='black')
arc  = canvas.create_arc(x0+160, y0, x1+310, y1, start = 30, extent = 150, fill = 'black')
rect = canvas.create_rectangle(560, 30, 870, 250)
poly = canvas.create_polygon(560, 220, 220, 310, 230, 190)
canvas.place(x=6,y=2)

root.mainloop()

相关链接

  1. Tkinter GUI教程系列

  2. Python Tkinter Grid布局管理器详解

  3. Python: tkinter窗口屏幕居中,设置窗口最大,最小尺寸

  4. python Tkinter LabelFrame+grid+place 综合应用

  5. An Introduction to Tkinter (查阅向,推荐)

  6. TkInter - Python Wiki

你可能感兴趣的:(GUI编程,Tkinter库和布局)