您的位置:首页 > 编程语言 > Python开发

Python GUI Cookbook —— 布局管理

2017-12-04 20:06 549 查看
原文链接:Python GUI Cookbook —— 布局管理

在标签框架中排列多个标签

LabelFrame
可以让我们有组织的的设计 GUI。

[...]
# Create a container to hold labels
buttons_frame = ttk.LabelFrame(window, text=' Label in a Frame')
buttons_frame.grid(column=0, row=7)
# buttons_frame.grid(column=1, row=6)

# Place labels into the container element
ttk.Label(buttons_frame, text='Label1').grid(column=0, row=0, sticky=tk.W)
ttk.Label(buttons_frame, text='Lable2').grid(column=1, row=0, sticky=tk.W)
ttk.Label(buttons_frame, text='Label3').grid(column=2, row=0, sticky=tk.W)

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()




也可以垂直摆放

# Create a container to hold labels
buttons_frame = ttk.LabelFrame(window, text=' Label in a Frame')
buttons_frame.grid(column=0, row=7)
# buttons_frame.grid(column=1, row=6)

# Place labels into the container element
ttk.Label(buttons_frame, text='Label1').grid(column=0, row=0, sticky=tk.W)
ttk.Label(buttons_frame, text='Lable2').grid(column=0, row=1, sticky=tk.W)
ttk.Label(buttons_frame, text='Label3').grid(column=0, row=2, sticky=tk.W)

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()




使用填充增加 widget 周围的空间

tkinter 因 GUI 丑陋而闻名,然而这种情况在版本号 8.5 后发生了巨大的变化。Python 3.6 带有 tkinter 8.6。

给之前的代码添加
padx
pady


[...]
buttons_frame.grid(column=0, row=7, padx=20, pady=40)
[...]




LabelFrame
包含的标签添加一些空间

[...]
ttk.Label(buttons_frame, text='Lable2').grid(column=0, row=1)
ttk.Label(buttons_frame, text='Label3').grid(column=0, row=2)

for child in buttons_frame.winfo_children():
child.grid_configure(padx=8, pady=4)

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()




grid_configure()
可以让我们能够在主循环显示 UI 之前修改它们。所以不必在首次创建 widget 时硬编码这些值。

winfo_children()
返回 buttons_frame 的子 widgets。

还不知道 labels 右边的空间的大小

[...]
# Place labels into the container element
ttk.Label(buttons_frame, text='Label1 -- sooooooo much looooooooonger...').grid(column=0, row=0)
ttk.Label(buttons_frame, text='Lable2').grid(column=0, row=1)
ttk.Label(buttons_frame, text='Label3').grid(column=0, row=2)
[...]




可以移除
LabelFrme
的名字。看看 padx 的效果

[...]
buttons_frame = ttk.LabelFrame(window, text='')
[...]




widgets 动态扩展 GUI

你可能已经看到了 widget 能够自动扩展所需空间以便直观地显示文本。

对于网格管理器,其列的宽度取决于该列中最长的名字或 widget。

由于显示
LaqbelFrame
标题的文本属性比上面的
Enter a name:
label 和 textbox entry 都长,所以这两个 widget 都依据新宽度,动态地中心对齐到 column 0。

column 0 的
Checkbutton
Radiobutton
没有中心对齐,因为使用了
sticky=tk.W
属性。

通过在框架中放入框架来组织 GUI widgets

这里我们将创建一个顶层框架来包含其他 widget

添加下面的代码到我们的 Python 模块

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext

# Create instance
window = tk.Tk()

# Add a title
window.title("My First Python GUI")

# We are creating a container frame to hold all other widgets
mighty = ttk.LabelFrame(window, text=' Mighty Python ')
mighty.grid(column=0, row=0, padx=8, pady=4)

[...]


接下来将
mighty
作为父
widget
,替换
window
。如下所示:

[...]
# Modify adding a Label using mighty as the parent instead of window
a_label = ttk.Label(mighty, text = 'Enter a number:')
a_label.grid(column=0, row=0)
[...]


创建菜单栏

首先需要从 tkinter 中导入 Menu 类

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
from tkinter import Menu

# Create instance
window = tk.Tk()

[...]


添加菜单项

[...]

# Creating a Menu Bar
menu_bar = Menu(window)
window.config(menu=menu_bar)

# Creating menu and add menu items
file_menu = Menu(menu_bar) # create File menu
file_menu.add_command(label='New') # add file menu item
menu_bar.add_cascade(label='File', menu=file_menu) # add File menu to menu bar

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()




如果你对这段代码有一点困惑的话,请不要担心,这仅仅是 tkinter 创建 menubar 的语法,并不很 Pythonic。

添加第二个菜单项

[...]

# Creating a Menu Bar
menu_bar = Menu(window)
window.config(menu=menu_bar)

# Creating menu and add menu items
file_menu = Menu(menu_bar) # create File menu
file_menu.add_command(label='New') # add file menu item
file_menu.add_command(label='Exit')
menu_bar.add_cascade(label='File', menu=file_menu) # add File menu to menu bar

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()




在两个菜单项中间添加分隔线

通过传
tearoff
属性到菜单的构造器,移除第一个 dashed line

通过下面代码添加分隔线

[...]

# Creating menu and add menu items
file_menu = Menu(menu_bar, tearoff=0) # create File menu
file_menu.add_comm
105f5
and(label='New') # add file menu item
file_menu.add_separator()
file_menu.add_command(label='Exit')
menu_bar.add_cascade(label='File', menu=file_menu) # add File menu to menu bar

name_entered.focus() # Place cursor into name Entry
# Start GUI
window.mainloop()




接下来添加水平的第二个菜单

[...]
# Adding another Menu to the Menu Bar and an item
help_menu = Menu(menu_bar, tearoff=0)
help_menu.add_command(label='About')
menu_bar.add_cascade(label='Help', menu=help_menu)
[...]




现在,我们的菜单项点击过后还没有反应,因此我们需要添加一些 commands 到菜单项

[...]

# Exit GUI cleanly
def _quit():
window.quit()
window.destroy()
exit()

# Creating a Menu Bar
menu_bar = Menu(window)
window.config(menu=menu_bar)

# Creating menu and add menu items
file_menu = Menu(menu_bar, tearoff=0) # create File menu
file_menu.add_command(label='New') # add file menu item
file_menu.add_separator()
file_menu.add_command(label='Exit', command=_quit)
menu_bar.add_cascade(label='File', menu=file_menu) # add File menu to menu bar

[...]


现在当我们点击 Exit,程序就会立刻退出。

创建选项卡式 widgets

创建一个新的 Python 模块

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

window = tk.Tk() # Create instance
window.title('Python GUI') # Add a title
tabControl = ttk.Notebook(window) # Create Tab Control
tab1 = ttk.Frame(tabControl) # Create a Tab
tabControl.add(tab1, text='Tab 1') # Add the Tab
tabControl.pack(expand=1, fill='both') # Pack to make visible

window.mainloop()




添加第二个 Tab

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

window = tk.Tk() # Create instance
window.title('Python GUI') # Add a title
tabControl = ttk.Notebook(window) # Create Tab Control
tab1 = ttk.Frame(tabControl) # Create a Tab
tabControl.add(tab1, text='Tab 1') # Add the Tab
tab2 = ttk.Frame(tabControl) # Create second Tab
tabControl.add(tab2, text='Tab 2') # Add second Tab
tabControl.pack(expand=1, fill='both') # Pack to make visible

window.mainloop()




在 Tab 1 中写一些内容

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

window = tk.Tk() # Create instance
window.title('Python GUI') # Add a title
tabControl = ttk.Notebook(window) # Create Tab Control
tab1 = ttk.Frame(tabControl) # Create a Tab
tabControl.add(tab1, text='Tab 1') # Add the Tab
tab2 = ttk.Frame(tabControl) # Create second Tab
tabControl.add(tab2, text='Tab 2') # Add second Tab
tabControl.pack(expand=1, fill='both') # Pack to make visible

# LabelFrame using tab1 as the parent
mighty = ttk.LabelFrame(tab1, text=' Mighty Python ')
mighty.grid(column=0, row=0, padx=8, pady=4)

# Label using mighty as the parent
a_label = ttk.Label(mighty, text=' Enter a number: ')
a_label.grid(column=0, row=0, sticky='W')

window.mainloop()




再加一些内容,好看清标题

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext

window = tk.Tk() # Create instance
window.title('Python GUI') # Add a title
tabControl = ttk.Notebook(window) # Create Tab Control
tab1 = ttk.Frame(tabControl) # Create a Tab
tabControl.add(tab1, text='Tab 1') # Add the Tab
tab2 = ttk.Frame(tabControl) # Create second Tab
tabControl.add(tab2, text='Tab 2') # Add second Tab
tabControl.pack(expand=1, fill='both') # Pack to make visible

# LabelFrame using tab1 as the parent
mighty = ttk.LabelFrame(tab1, text=' Mighty Python ')
mighty.grid(column=0, row=0, padx=8, pady=4)

# Label using mighty as the parent
a_label = ttk.Label(mighty, text=' Enter a number: ')
a_label.grid(column=0, row=0, sticky='W')

# Modified Button click Event Function
def click_me():
action.configure(text='Hello ' + name.get() + ' ' + number_chosen.get())

# Adding a Textbox Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(mighty, width=12, textvariable=name)
name_entered.grid(column=0, row=1, sticky='W') # column 0

# Adding a Button
action = ttk.Button(mighty, text="Click Me!", command=click_me)
action.grid(column=2, row=1, sticky='W') # change column to 2

ttk.Label(mighty, text='Choose a number:').grid(column=1, row=0, sticky='W')

number = tk.StringVar()
number_chosen = ttk.Combobox(mighty, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1, sticky='W') # Combobox in column 1
number_chosen.current(4)

# Using a scrlled text control
scrol_w = 30
scrol_h = 3
scr = scrolledtext.ScrolledText(mighty, width=scrol_w, height=scrol_h, wrap=tk.WORD)
scr.grid(column=0, sticky='WE', columnspan=3)

window.mainloop()




给 Tab 2 也加上内容

[...]

# Creating three checkbuttons
chVarDis = tk.IntVar()
check1 = tk.Checkbutton(snake, text='Disabled', variable=chVarDis, state='disabled')
check1.select()
check1.grid(column=0, row=0, sticky='W')

chVarUn = tk.IntVar()
check2 = tk.Checkbutton(snake, text='UnChecked', variable=chVarUn)
check2.deselect()
check2.grid(column=1, row=0, sticky='W')

chVarEn = tk.IntVar()
check3 = tk.Checkbutton(snake, text='Enabled', variable=chVarEn)
check3.deselect()
check3.grid(column=2, row=0, sticky='W')

# GUI callback function
def checkCallback(*ignoredArgs):
if chVarUn.get():
check3.configure(state='disabled')
else:
check3.configure(state='normal')
if chVarEn.get():
check2.configure(state='disabled')
else:
check2.configure(state='normal')

# trace the state of the two checkbutton
chVarUn.trace('w', lambda unused0, unused1, unused2 : checkCallback())
chVarEn.trace('w', lambda unused0, unused1, unused2 : checkCallback())

# First, we change our Radiobutton globals variables into a list
colors = ['Blue', 'Gold', 'Red']

# We have also changed the callback function to be zero-based, using list
# instead of module-level global variables
# Radiobutton Callback
def radCall():
radSel = radVar.get()
if radSel == 0:
snake.configure(background=colors[0]) # now zero-based and using list
elif radSel == 1:
snake.configure(background=colors[1])
elif radSel == 2:
snake.configure(background=colors[2])

# Create three Radiobuttons using one variable
radVar = tk.IntVar()

# Next we are selecting a non-existing index value for radVar
radVar.set(99)

# Now we are creating all three Radiobutton widgets within one loop
for col in range(3):
curRad = tk.Radiobutton(snake, text=colors[col], variable=radVar,
value=col, command=radCall)
curRad.grid(column=col, row=1, sticky=tk.W)

# Create a container to hold labels
buttons_frame = ttk.LabelFrame(snake, text=' Labels in a Frame ')
buttons_frame.grid(column=0, row=2)
# buttons_frame.grid(column=1, row=6)

# Place labels into the container element
ttk.Label(buttons_frame, text='Label1').grid(column=0, row=0)
ttk.Label(buttons_frame, text='Lable2').grid(column=0, row=1)
ttk.Label(buttons_frame, text='Label3').grid(column=0, row=2)

for child in buttons_frame.winfo_children():
child.grid_configure(padx=8, pady=4)

window.mainloop()




点击
Radiobutton
发现不起作用了,那么我们改变它们的功能

[...]
# Radiobutton Callback
def radCall():
radSel = radVar.get()
if radSel == 0:
snake.configure(text='Blue') # now zero-based and using list
elif radSel == 1:
snake.configure(text='Gold')
elif radSel == 2:
snake.configure(text='Red')
[...]




使用网格布局管理器

如果我们不指明 row 的值,tkinter 会自动完成加 1 操作。

参考文献

Python GUI Programming Cookbook - Second Edition by Burkhard A. Meier
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python gui 布局 管理