Python GUI:Tkinter——2


续上文: Python GUI:Tkinter——1

加入 Padding

所谓 Padding,就是棉片,哦不,空间的意思。通过控件的 .grid_configure接口,可以设置控件在 x,y轴方向上的间隔

大家可以接着Python GUI:Tkinter——1,在下面添加代码

aLabelFrame.grid_configure(padx=10,pady=40)    #.grid_configure 接口可以设置 grid 排版格式
for child in aLabelFrame.winfo_children():    #遍历 Frame 下的所有控件
	child.grid_configure(padx=8,pady=4)

效果如下,可以看到,aLabel 中的(子)控件的间隔变宽了,从而使得整个界面的排版变得小清新起来。
在这里插入图片描述

Frames within win

上一篇文章最后讨论了,若某个控件占用的长度过长,则会导致一整列都变长,进而导致界面排版混乱。如何解决呢?就是把控件的 Master,从 win 中改为某个 Frame。 注意,Frame 可以是 LabelFrame 也可以直接是 Frame。前者有一个 Label 作为“标题”,后者没有。

用 Frame 取代 win

用 Frame 取代 win 有什么作用呢?

  • 便于遍历控制 Frame 中的所有控件
  • 有利于改善 GUI 界面的整体视觉效果

大家可以接着Python GUI:Tkinter——1,在下面添加代码

"""在 win 下面加入下面的代码"""
bLabelFrame = ttk.LabelFrame(win,text='Python GUI')
bLabelFrame.grid(column=0,row=0)

把所有的控件的 Master 改为这个 Frame,即 bLabelFrame

这样做有什么好处呢?一是可以通过 .winfo_children() 来遍历 bLabelFrame 下面的控件,然后只要修改子控件就可以修改每个控件的间隔,比如:

for child in bLabelFrame.winfo_children():
    child.grid_configure(sticky='W')

其中 sticky=‘W’ 等价于 tk.w,要记住,sticky 的英文是粘的意思,不是对其。所以,如果令 sticky='WSNE',则让控件占满整个空间。

改善 GUI 界面的整体视觉效果

如果某个控件占用了很多空间,那么与之同列的控件就会收到影响。因此,不能像上面那样,把所有控件放到一个Frame里。如果一个控件占用的空间太大,可以把他装到另一个 Frame 里,然后再把这个Frame 放入到 win 上。于是,就变成了 win 中有两个 Frames,每个 Frame 分配不同的空间。于是,某一个 Frame 的列宽度或列数,就不会影响另一个 Frame 的宽度和列数。

添加 Menus

效果图:

在这里插入图片描述

实现代码

大家可以接着Python GUI:Tkinter——1,在下面添加代码。

from tkinter import Menu
menuBar = Menu(win)    #示例话一个 Menu,Master 为 win。
"""思考一下,Master可以为 Frame 吗?评论区评论一下试试?"""
win.configure(menu=menuBar)    #注意是 menu 参数

fileMenu = Menu(menuBar,tearoff=0)    # tearoff 参数如果不设置,就会发现多出一条横线
"""提问: 你觉得这里的 menuBar 是父类,还是某个参数?在评论区回答一下吧/(ㄒoㄒ)/~~"""
fileMenu.add_command(label='New')    #add_command 用来添加 menu 的“项”
"""嵌套"""
subFileMenu = Menu(fileMenu,tearoff=0)    # 这个是 Menu 中的 Menu 了,可以一直这样嵌套下去
subFileMenu.add_command(label='Save')
subFileMenu.add_command(label='Save as')
fileMenu.add_cascade(label='Save as',menu=subFileMenu)
"""嵌套结束"""
fileMenu.add_separator()    #给菜单“项”加一条横线

from sys import exit
def _quit():    #设置一个 menu 菜单项的激活函数
    win.quit()    #退出
    win.destroy()   #销毁
    exit()   #结束程序

fileMenu.add_command(label='Exit',command=_quit)    #command参数设置激活函数,即退出。激活函数的定义同样要放在
#前面,这是因为 tk 控件是 callback 的缘故
menuBar.add_cascade(label='File',menu=fileMenu)    #将fileMenu 添加进 menuBar,这样才会显示出来

helpMenu = Menu(menuBar,tearoff=0)
helpMenu.add_command(label='About')
menuBar.add_cascade(label='Help',menu=helpMenu)

助记总结

  • 所有的 Menu 类控件,都以 Menu 实例化开始
  • 除了 menuBar 以 win.configure(menu=menuBar)结束外,其他的 Menu 控件都以 .add_cascade(label='xxx' ,menu=menu名)结束。
  • 不同于其他空间,menu 无需 grid 就能够显示。换句话说,许多控件,包括 Frame 都需要 grid 或 pack 后才能排版在GUI 上。

NoteBook 与 Tab

Tab 能够将软件分开成两个部分来使用,就像书本翻页一样。这两页的排版可以没有任何关系,当然除了 Menus 是一样的之外。先看一下效果:
在这里插入图片描述
在这里插入图片描述

原理

其实,tab 相当于一个新的 Frame,其分页作用的,是 NoteBook 这个控件。

tabControl = ttk.Notebook(win)
tab1 = ttk.Frame(tabControl)
tab2 = ttk.Frame(tabControl)
tabControl.add(tab1,text='页面1')
tabControl.add(tab2,text='页面2')
tabControl.pack(fill='both',expand=1)

之后,只要把 tab1 但成 win 来用就行了,比如大家可以把 bLabelFrame 的 Master 设置成 tab1。

然后,再添加一个 cLabelFrame,再弄上几个控件,在把 cLabelFrame 的 Master 设置成 tab2 。完事。 当然,也可以把 空间的 Master 设置成 tab2

记住,此时 Frames within win 就应该改写为 Frames within tab了。当然,不是 Frame within tabcontrol,希望大家能明白我的意思。

整体代码:

可能有人被我的表述方式搞晕了,这是因为这门教程是从 01 开始看起的。所以中途跳转过来的同学,可能有点不知所云,那么没关系,我就把所有代码再展示以便。(这个代码可能与 01 有点不一样,因为是我重新敲的,并且加入了 02 的代码(即上面的所有骚操作))

先看看效果吧:
在这里插入图片描述
在这里插入图片描述
这是我为了强化记忆,再次码了一遍代码。其中

# -*- coding: utf-8 -*-
"""
Created on Thu May 28 21:01:29 2020

@author: Administrator
"""

import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as mBox
from tkinter import Menu
win = tk.Tk()
win.title('标题')
win.iconbitmap(r'../app.ico')
menuBar = Menu(win)
win.configure(menu=menuBar)
from sys import exit
def _quit():
    win.quit()
    win.destroy()
    exit()

fileMenu = Menu(menuBar,tearoff=0)
fileMenu.add_command(label='New')
fileMenu.add_separator()
subMenu = Menu(fileMenu,tearoff=0)
subMenu.add_command(label='Save')
subMenu.add_command(label='Save as')
fileMenu.add_cascade(label='Save',menu=subMenu)
fileMenu.add_command(label='Exit',command=_quit)
menuBar.add_cascade(label='File',menu=fileMenu)

helpMenu = Menu(menuBar,tearoff=0)
helpMenu.add_command(label='Help')
helpMenu.add_command(label='About')
menuBar.add_cascade(label='Help',menu=helpMenu)


tabControl = ttk.Notebook(win)
tab1 = ttk.LabelFrame(tabControl)
tab2 = ttk.LabelFrame(tabControl)
tabControl.add(tab1,text='页面1')
tabControl.add(tab2,text='页面2')
tabControl.pack(fill='both',expand=1)

zhuo = ttk.LabelFrame(tab1,text='zhuo\'s GUI')
zhuo.grid(column=0,row=0)

ttk.Label(zhuo,text='Please put a name').grid(column=0,row=0)

etyVar = tk.StringVar()
aEntry = ttk.Entry(zhuo,textvariable=etyVar)
aEntry.grid(column=0,row=1)

comVar = tk.StringVar()
aCombobox = ttk.Combobox(zhuo,textvariable=comVar,state='readonly')
aCombobox['value'] = (1,2,4,8)
aCombobox.grid(column=1,row=1)

def clickBut():
    aButton.configure(text='hello'+etyVar.get()+comVar.get())
aButton = ttk.Button(zhuo,text='click me',command=clickBut)
aButton.grid(column=2,row=1)

aCheck = tk.IntVar()
aCheckbutton = tk.Checkbutton(zhuo,variable=aCheck,text='Disabled',state='disabled')
aCheckbutton.select()
aCheckbutton.grid(column=0,row=2)

bCheck = tk.IntVar()
bCheckbutton = tk.Checkbutton(zhuo,variable=bCheck,text='Unchecked')
bCheckbutton.grid(column=1,row=2)

cCheck = tk.IntVar()
cCheckbutton = tk.Checkbutton(zhuo,variable=cCheck,text='Checked')
cCheckbutton.select()
cCheckbutton.grid(column=2,row=2)

radVar = tk.IntVar()
COLORs = ['Green','Gold','Red']
def clickRad():
    flag = radVar.get()
    if flag==0:
        zhuo.configure(text=COLORs[0])
        mBox.showinfo('HH','w ai feiyueyin')
    elif flag==1:
        zhuo.configure(text=COLORs[1])
        mBox.showinfo('HH','w ai feiyueyin')
    elif flag==2:
        zhuo.configure(text=COLORs[2])
        mBox.showinfo('HH','w ai feiyueyin')
        
for col in range(3):
    rad = 'rad'+str(col)
    rad = tk.Radiobutton(zhuo,variable=radVar,value=col,text=COLORs[col],command=clickRad)
    rad.grid(column=col,row=3)
from tkinter import scrolledtext
scrW = 30
scrH = 3
aScrTxt = scrolledtext.ScrolledText(zhuo,height=scrH,width=scrW,wrap=tk.WORD)
aScrTxt.grid(row=4,column=0,columnspan=3)

aLabelFrame = ttk.LabelFrame(tab1,text='a label frame')
aLabelFrame.grid(column=0,row=1,sticky='W')

ttk.Label(aLabelFrame,text='a label with sooooooo much long').grid(column=0,row=0)
ttk.Label(aLabelFrame,text='a label').grid(column=0,row=1)
ttk.Label(aLabelFrame,text='a label').grid(column=0,row=2)

"""这个控件叫 Spinbox,用于上下调整的那种"""
from tkinter import Spinbox
aSpinbox = Spinbox(aLabelFrame,from_=0,to=10)
# aSpinbox = Spinbox(aLabelFrame,values=(1,2,5,10))    #也可以用 value 参数来设置
aSpinbox.grid(column=0,row=3)

for child in aLabelFrame.winfo_children():
    child.grid_configure(sticky='W')
    
for child in zhuo.winfo_children():
    child.grid_configure(sticky='W')
    
"""开发页面2"""
muniao = ttk.LabelFrame(tab2,text='我是frame')
muniao.grid(column=0,row=0)
"""之后,所有的空间的 Master 就是 frame muniao了"""
dFlag = tk.IntVar()
eFlag = tk.IntVar()
fFlag = tk.IntVar()
dCheck = tk.Checkbutton(muniao,variable=dFlag,text='A',state='disabled')
eCheck = tk.Checkbutton(muniao,variable=eFlag,text='B')
eCheck.select()
fCheck = tk.Checkbutton(muniao,variable=fFlag,text='C')

i = 0
for child in muniao.winfo_children():
    child.grid(column=i,row=0,sticky='W')
    i+=1

radVar2 = tk.IntVar()
for i in range(3):
    rad = 'rad'+str(i+3)
    rad = tk.Radiobutton(muniao,variable=radVar2,value=i,text='radbutton'+str(i))
    rad.grid(column=i,row=1,sticky='W')
    
win.mainloop()

多文件开发

引子

首先要知道 Python 是顺序执行的,因此如果不是 def 里面的东西,Python 都会执行下去。那么 if name == “main”: 是干什么用的呢?他是用来测试函数的。因此,一般开发中,不会在 if name == “main” 以外的地方写除了 def 以外的代码,当然,一般情况下(别喷我,我的确很菜)。比如下面的代码,运行一下会是?

"""script1.py"""
A = 'zhuo'
def fun():
    print(A)
# import script2
if __name__ ==  "__main__":
    fun()
    print(A)

结果为两个 zhuo。

若有另一个代码文件:

script2.py
A = 'muniao'
def fun():
    print('My name is zhuo mu niao')
    
if __name__ == '__main__':
    fun()

然后返回 script1.py,把# import script2取消注释,再次运行,你猜会发现什么?输出还是两个 zhuo,也就是说,每个脚本的环境都不是一样的。因此,不能将 import 看成简单的拷贝。于是,在不同脚本中,应该可以使用同一个变量名而不产生歧义

双脚本开发

对于上面的很长的、两个 tab 的代码。我们是否可以把 tab2 单独写在另一个脚本文件中呢?这样,在命名变量的时候,就不需要记住** “咦?我排序到哪了?应该是 e 吧” **这样的傻瓜问题了。因此,试一下把?

把 tab2 开发代码放到一个叫 prac2.py 的文件中:
在这里插入图片描述
答案是不行,因为 prac2 中的 tab2 not defined!如果在 prac2 用 from prac1 import tab2呢?感觉可以,但实际上会导致弹出两个窗口。难道我们就真的要屈服吗?

这进一步说明了双文本开发 GUI,可能还需要技巧?这个技巧是什么呢?高手们,回答一下呗,评论区欢迎你!!

总结

经过上面的处理,相信我们的 gui 界面已经比较完善了。但是,内在的东西还是有所匮乏。比如,咱们把讨厌的羽毛LOGO 给弄掉?如何弹窗?有没有可能实现令一个鼠标停留之后就会跳出提示框的功能?完善再完善,是我们的目的。如何做呢?请看下一章:Python GUI:Tkinter——03

学习手记

我重新翘了一遍代码,犯了某些错误:

  • Combobox 里的参数是 textvariable
  • Checkbutton 而不是 Checkbox
  • from tkinter import scrolledtext,库名总是小写的。而类名中是大写开头,之后全小写。对象名是驼峰标记发。所以,在实例化的时候,是 scrolledtext.Scrolledtext 而不是 xxx.ScrolledText
  • 在该页面名的时候,不应该是 tab1 = ttk.Frame(tabControl, text=‘页面1’),而是 tabControl.add(tab1, text='页面1')

再说一次:当然,整个 GUI 经过上述完善之后,虽然有所改观,但仍旧有进步的空间。比如怎么弹窗?怎么久置后弹出提示?怎么修改 ICON,也即 LOGO?等等,都需要我们更进一步地改进。如果读者们想要有进一步地提高,请看下一篇吧:Python GUI:Tkinter——03

猜你喜欢

转载自blog.csdn.net/weixin_42141390/article/details/106402919