大家在编写python tkinter 界面时,经常被小部件的布局困扰,我再写了几个小工具后,对布局有了一些心得,现分享给大家,也希望大家能编写出更漂亮的界面。

第一步,选择布局方式
tkinter 提供了三种布局方式:
1、pack() 排列方式,如果小部件按横向或竖向对齐排列的,可选用这种方式,类似网页中的层概念,强烈建议先用;
2、grid() 网格方式,如果小部按网格规律排列的,则可选这种方式,类似网页中的表格概念;
3、place() 精准方式,会按坐标值进行布局,虽然能很精准地布局,但需要不停地调整位置花费大量时间,且在不同的运行环境可能导致小部件在容器中显示不全,不建议使用。

前两种方式在同一容器内(父小部件)不可混用,后者则可以强加到前两者的容器中。

前两方式控制小部件之间的间隔距离,在布局方法中加入属性 padx、pady 来实现,单位是像素值,一般设2左右就可以了。小部件的宽、高度则在小部件主体中用 width、height 来控制,注意这里的单位是按字符来定义(1个字符约为7个像素点),而 place 则是按像素来计算,即如小部件中定义了 width=20,后面用 pack 或 grid 来布局时,则表示小部件的宽度为20个字符,若用 place 来定位布局,则表示小部件的宽度为 20 个像素宽。还有一点要注意,button、label 小部件在使用image 显示图片后,宽高度会强制转为像素单位。

前两种方式在布局时需根据小部件的排列进行合理分组,使用 pack 布局时调用它的顺序还会导致不同的效果;grid 调用的顺序倒不讲究,全由 row 、 column 来决定位置;place 则更是随意。

总之,如果小部件布置成横向排列(不成网格状,也是最常见的),这时请用 pack 布局;如果成网格状排列(中间可以存在跨行、跨列情况)则使用 grid 布局;若小部件位置分散就建议用 place 了。

第二步,划分区域

这步很关键,也是最难控制的一环,若想象能力不强的建议画草图辅助,分区的方式每次只能按横向或纵向进行,复杂的界面需要多次嵌套分区。我先用我写过的小工具界面做为例子进行分区。

python 控件名显示 python 控件布局_python


根据上图小部件的分布情况,先划分第一层分区,现按红色框按上下结构分区(图中蓝色框为第二层分区,橙色框为第三层,棕色框为第四层),关键代码(为了方便理解,把不同层做成缩进格式,实现代码不能缩进):

f1=tkinter.Frame(root)
f1.pack(side=tkinter.TOP,pady=2,fill=tkinter.X) #横向占满空间
    tkinter.Button(f1,width=3).pack(side=tkinter.LEFT,padx=2)#以 f1 做为容器
    tkinter.Button(f1,width=3).pack(side=tkinter.LEFT,padx=2)#以 f1 做为容器

f2=tkinter.Frame(root)
f2.pack(side=tkinter.TOP,pady=2,fill=tkinter.X)

    f4=tkinter.Frame(f2) #以 f2 做为容器
    f4.pack(side=tkinter.TOP,fill=tkinter.X)

        f8=tkinter.Frame(f4) #以 f4 做为容器
        f8.pack(side=tkinter.LEFT,fill=tkinter.Y) #纵向占满空间
        tkinter.Label(text="……\n……").pack(padx=2)#控制前后间隔,不设宽度会根据文本长度自行调整

        f9=tkinter.Frame(f4)
        f9.pack(side=tkinter.LEFT,fill=tkinter.Y)

            f12=tkinter.Frame(f9) #以 f9 做为容器
            f12.pack(side=tkinter.TOP,pady=2,fill=tkinter.X)
                tkinter.Radiobutton(f12,text="……").pack(side=tkinter.LEFT,padx=2)#以 f12 做为容器
                ttk.Combobox(f12,width=20).pack(side=tkinter.LEFT,padx=2) #用 width 控制长度
                ttk.Combobox(f12).pack(side=tkinter.LEFT,padx=2,fill=tkinter.X) #可先其中一小部件充满剩余空间,使界面好看些

        f13=tkinter.Frame(f9) #以 f9 做为容器
        f13.pack(side=tkinter.TOP,pady=2,fill=tkinter.X)
        	#……
            tkinter.Entry(f13).pack(side=tkinter.LEFT,fill=tkinter.X)#以 f13 做为容器

    f5=tkinter.Frame(f2) #以 f2 做为容器
    f5.pack(side=tkinter.TOP,fill=tkinter.X)
    	#……

f3=tkinter.Frame(root)
f3.pack(side=tkinter.TOP,pady=2,fill=tkinter.BOTH,expand=True) #充满窗体剩余空间

    f6=tkinter.LabelFrame(f3,text="书目结构") #以 f3 做为容器
    f6.pack(side=tkinter.LEFT,fill=tkinter.Y)#纵向占满空间

        f10=tkinter.Frame(f6) #以 f6 做为容器
        f10.pack(side=tkinter.TOP,fill=tkinter.X)

        f11=tkinter.Frame(f6) #以 f6 做为容器
        f11.pack(side=tkinter.TOP,fill=tkinter.BOTH,expand=True)#占满剩余空间

    f7=tkinter.LabelFrame(f3,text="内容预览") #以 f3 做为容器
    f7.pack(side=tkinter.LEFT,fill=tkinter.BOTH,expand=True)#纵向占满空间
    
        tkinter.Scrollbar(f7,orient=tkinter.VERTICAL).pack(side=tkinter.RIGHT,fill=tkinter.Y)#先布局,避免有时被挤掉
        tkinter.Text(f7).pack(side=tkinter.LEFT,fill=tkinter.BOTH,expand=True)#占满剩余空间

    
tkinter.Text(height=2).pack(side=tkinter.TOP,fill=tkinter.X,pady=2) #仅1个小部件,所以省掉 Frame 直接使用小部件

每个顶层的小部件基本都会按横向对齐排列,因此均用 pack(side=tkinter.LEFT,padx=2) 排列即可 ,为了界面更加好看,每一行中选其中一个小部件自动拉长充满剩余空间(关键代码: fill=tkinter.X,有时需要再加上 expand=True )

分好区(层)后,若某一区小部件为网格分布,则该层的小部件可换用 grid()来布局,如下图(橙色线为网格划分):

python 控件名显示 python 控件布局_ide_02


直接上关键代码:

f1=tkinter.Frame(root)
f1.pack(side=tkinter.LEFT)

    tkinter.Label(f1,text=" 书籍名称 ").grid(row=0,column=0,pady=2) #文本前后加空间来实现间隔
    tkinter.Entry(f1,width=20).grid(row=0,column=1,padx=2,pady=2) # row=行控制, column 列控制
    tkinter.Label(f1,text=" 作者 ").grid(row=0,column=2,pady=2)
    tkinter.Entry(f1,width=20).grid(row=0,column=3,padx=2,pady=2) # row=行控制, column 列控制

    tkinter.Label(f1,text=" 出 版 社 ").grid(row=1,column=0,pady=2)
    tkinter.Entry(f1,width=20).grid(row=1,column=1,padx=2,pady=2)
    tkinter.Label(f1,text=" 日期 ").grid(row=1,column=2,pady=2)
    tkinter.Entry(f1,width=20).grid(row=1,column=3,padx=2,pady=2)

    tkinter.Label(f1,text=" 封面图片 ").grid(row=2,column=0,pady=2)
    tkinter.Entry(f1,width=20).grid(row=2,column=1,padx=2,pady=2)
    f3=tkinter.Frame(f1).grid(row=2,column=2,columnspan=2,pady=2) #横跨2列

        tkinter.Button(f3,text="浏览").pack(side=tkinter.LEFT,padx=2) #该层中所有小部件均用 pack 布局
        #……

f2=tkinter.Frame(root,width=30) #容器也可以定义尺寸,不定义则以最大包含量来算
f2.pack(side=tkinter.LEFT)

    tkinter.Label(f2).pack(fill=tkinter.BOTH,expand=True)

第三步,层内小部件的排列顺序
一般小部件都会放在最上层排列,可用 pack 按上下左右四个方向来布局(同一层中最好只按横向或纵向,比如 前面用 side=tkinter.LEFT 放置后,后面小部件又用 tkinter.TOP 来放置,虽然不会报错,但效果不可控。但可以再用 tkinter.RIGHT 来放置)。

pack 方法有先来后到之分,调用时要注意顺序,在添加自动填充空间的小部件时,后面添加的小部件可能会被挤掉,这时可以把后面部件调到前面先放置,再放置自动填充空间的小部件,如:

f1=tkinter.Frame(root)
f1.pack(side=tkinter.TOP,fill=tkinter.BOTH,expand=True)

tkinter.Text(f1).pack(side=tkinter.LEFT,fill=tkinter.BOTH,expand=True)
tkinter.Scrollbar(f1,orient=tkinter.VERTICAL).pack(side=tkinter.LEFT,fill=tkinter.Y)

带垂直滚动条的文本框,按上面顺序放置,可能滚动条会被挤掉不显示,因此可以把放置顺序改下就可以避免这种情况:

f1=tkinter.Frame(root)
f1.pack(side=tkinter.TOP,fill=tkinter.BOTH,expand=True)

tkinter.Scrollbar(f1,orient=tkinter.VERTICAL).pack(side=tkinter.RIGHT,fill=tkinter.Y)#先放置滚动条,右对齐
tkinter.Text(f1).pack(side=tkinter.LEFT,fill=tkinter.BOTH,expand=True)#后放置的文本框左对齐

小技巧

看到这里,相信大家应该能做出复杂的界面了,但中间的调试是免不了的,调试又是最耗时的,因此大家在划区分层时,最好先把 Frame 换成 LabelFrame,这个容器会显示边框,能让大家更直观地看到分区效果。或者给 Frame 加上不同的背景色,也能减少很多调试时间!

最后,愿大家能做出更美的程序界面!!