使用wxpython开发跨平台桌面应用,常用窗体布局BoxSizer,FlexGridSizer,GridBagSizer的介绍处理
我们在开发桌面应用的时候,不管是之前C#开发Winform的时候,还是现在使用wxpython来开发跨平台应用的时候,都需要了解布局的处理,wxpython的常用布局Sizer类,包括BoxSizer,FlexGridSizer,GridBagSizer都是我们需要经常打交道的,因此有必要对它们进行一些了解,这样开发界面起来才能得心应手。本篇随笔介绍一下这几种布局Sizer的不同以及对它们进行测试和封装使用。
1、BoxSizer,FlexGridSizer,GridBagSizer的布局介绍和差异
在 wxPython 中,布局管理是通过 Sizer 类来实现的。常用的 Sizer 类包括
BoxSizer
、
FlexGridSizer
和
GridBagSizer
。下面是这些 Sizer 的介绍及其之间的差异:
1. BoxSizer
- 描述
:
BoxSizer
是最简单的 Sizer 类型,允许你将控件沿一个方向(水平或垂直)排列。 - 用法
: 适用于简单的线性布局。你可以指定方向(
wx.HORIZONTAL
或
wx.VERTICAL
)并控制每个控件的边距和比例。 - 特点
:
- 所有子控件按顺序排列。
- 可以设置比例,控制控件的伸缩行为。
- 较适合创建简单的、单一方向的布局。
2. FlexGridSizer
- 描述
:
FlexGridSizer
是一个可以在行和列中进行灵活布局的 Sizer。它会自动调整每个单元格的大小,以适应控件的内容。 - 用法
: 适用于需要均匀分配空间的网格布局。 - 特点
:
- 行和列的大小可以根据内容自动调整。
- 所有控件都放置在独立的单元格中,不能跨越多个单元格。
- 适合需要规则网格的情况,比如表单或简单的网格布局。
3. GridBagSizer
- 描述
:
GridBagSizer
是一种更复杂的布局管理器,它允许你在一个网格中放置控件,并支持控件的大小、位置以及跨越多个行和列。 - 用法
: 适用于需要高度自定义布局的情况,比如复杂的用户界面。 - 特点
:
- 支持控件在网格中的精确控制。
- 可以设置控件的对齐方式和边距。
- 功能强大,适合复杂布局。
总结
- BoxSizer
: 最简单,适合线性布局。 - FlexGridSizer
: 灵活的网格,适合不规则网格布局。 - GridBagSizer
: 功能最强,支持复杂的自定义布局。
选择合适的 Sizer 取决于你的具体布局需求。对于简单场景,使用
BoxSizer
就足够了;而对于更复杂的布局,
FlexGridSizer
和
GridBagSizer
提供了更多的灵活性和控制能力。
2、使用布局Sizer控件创建不同的界面案例
为了直观的了解集中Sizer布局控件的不同,我们使用几个例子来测试它们的界面效果和区别。
1)BoxSizer的界面及代码
它是使用BoxeSizer来垂直放置几个部分的内容的,其中底部的两个按钮又是创建一个新的Panel进行维护,如下代码所示。
classMyForm(wx.Frame):def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, title='Boxesizer 测试')#Add a panel so it looks correct on all platforms self.panel =wx.Panel(self, wx.ID_ANY)#使用内置图标 bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_OTHER, (48, 48))
titleIco=wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
title= wx.StaticText(self.panel, wx.ID_ANY, '测试内容')#设置标题的字体大小 font =title.GetFont()
font.SetPointSize(28)
title.SetFont(font)#使用内置图标 bmp = wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_OTHER, (16, 16))
inputOneIco=wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
labelOne= wx.StaticText(self.panel, wx.ID_ANY, 'Input 1')
inputTxtOne= wx.TextCtrl(self.panel, wx.ID_ANY, '')
inputTwoIco=wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
labelTwo= wx.StaticText(self.panel, wx.ID_ANY, 'Input 2')
inputTxtTwo= wx.TextCtrl(self.panel, wx.ID_ANY,'')
inputThreeIco=wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
labelThree= wx.StaticText(self.panel, wx.ID_ANY, 'Input 3')
inputTxtThree= wx.TextCtrl(self.panel, wx.ID_ANY, '')
okBtn= wx.Button(self.panel, wx.ID_ANY, '确定')
cancelBtn= wx.Button(self.panel, wx.ID_ANY, '取消')
self.Bind(wx.EVT_BUTTON, self.onOK, okBtn)
self.Bind(wx.EVT_BUTTON, self.onCancel, cancelBtn)
topSizer=wx.BoxSizer(wx.VERTICAL)
titleSizer=wx.BoxSizer(wx.HORIZONTAL)
inputOneSizer=wx.BoxSizer(wx.HORIZONTAL)
inputTwoSizer=wx.BoxSizer(wx.HORIZONTAL)
inputThreeSizer=wx.BoxSizer(wx.HORIZONTAL)
btnSizer=wx.BoxSizer(wx.HORIZONTAL)
titleSizer.Add(titleIco, 0, wx.ALL,5)
titleSizer.Add(title, 0, wx.ALL,5)
inputOneSizer.Add(inputOneIco, 0, wx.ALL,5)
inputOneSizer.Add(labelOne, 0, wx.ALL,5)
inputOneSizer.Add(inputTxtOne,1, wx.ALL|wx.EXPAND, 5)
inputTwoSizer.Add(inputTwoIco, 0, wx.ALL,5)
inputTwoSizer.Add(labelTwo, 0, wx.ALL,5)
inputTwoSizer.Add(inputTxtTwo,1, wx.ALL|wx.EXPAND, 5)
inputThreeSizer.Add(inputThreeIco, 0, wx.ALL,5)
inputThreeSizer.Add(labelThree, 0, wx.ALL,5)
inputThreeSizer.Add(inputTxtThree,1, wx.ALL|wx.EXPAND, 5)
btnSizer.Add(okBtn, 0, wx.ALL,5)
btnSizer.Add(cancelBtn, 0, wx.ALL,5)
topSizer.Add(titleSizer, 0, wx.CENTER)
topSizer.Add(wx.StaticLine(self.panel,), 0, wx.ALL|wx.EXPAND, 5)
topSizer.Add(inputOneSizer, 0, wx.ALL|wx.EXPAND, 5)
topSizer.Add(inputTwoSizer, 0, wx.ALL|wx.EXPAND, 5)
topSizer.Add(inputThreeSizer, 0, wx.ALL|wx.EXPAND, 5)
topSizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 5)
topSizer.Add(btnSizer, 0, wx.ALL|wx.ALIGN_RIGHT, 5)
self.panel.SetSizer(topSizer)
topSizer.Fit(self)
上面可以看到,BoxSizer比较僵硬,它只能垂直或者水平放置内容,如果具有水平和垂直方向的处理,就需要分别创建多个不同的BoxSizer进行管理,因此代码会相对多一些。
2)FlexGridSizer的界面及代码
同样使用FlexGridSizer也可以做到很好的控制,通过设置指定行或者列的拉伸效果,可以很好的实现自动拉伸功能,类似Winform里面的Dock的方向处理。
该布局例子的代码如下所示。
classFrame(wx.Frame):def __init__(self):
super().__init__(None, title = '测试FlexGridSizer', size=(600, 400))#self.SetBackgroundColour("#4f5049") self.SetMinSize((300, 350))
main_sizer= wx.BoxSizer( wx.VERTICAL ) #主面板布局使用垂直方向 sizer= wx.FlexGridSizer(2, (10,20))
sizer.Add(wx.StaticText(self, label='昵称'))
sizer.Add(wx.TextCtrl(self), flag=wx.EXPAND)
sizer.Add(wx.StaticText(self, label='留言'))
sizer.Add(wx.TextCtrl(self, style=wx.TE_MULTILINE), flag=wx.EXPAND, proportion=1)
sizer.Add(wx.StaticText(self, label='测试'))
sizer.Add(wx.TextCtrl(self), flag=wx.EXPAND)
sizer.Add(wx.StaticText(self, label='测试其他'))
sizer.Add(wx.TextCtrl(self), flag=wx.EXPAND)#添加一行,使其占用两列 sizer.AddSpacer(0) #占位符,保持布局 sizer.Add(wx.TextCtrl(self, value='测试占用两列'), flag=wx.EXPAND, proportion=1)
self.sampleList= ['friends', 'advertising', 'web search', 'Yellow Pages']
self.edithear= wx.ComboBox(self, size=(95, -1), style=wx.CB_DROPDOWN)
self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
self.edithear.AppendItems(self.sampleList)
sizer.Add(wx.StaticText(self, label='下拉列表', size=(100, -1)))
sizer.Add(self.edithear, flag=wx.EXPAND)
sizer.AddGrowableRow(1)
sizer.AddGrowableCol(1)
main_sizer.Add(sizer, flag=wx.EXPAND|wx.ALL, proportion=1, border=10)
self.SetSizer(main_sizer)
okBtn= wx.Button(self, wx.ID_ANY, "确定")
cancelBtn= wx.Button(self, wx.ID_ANY, '取消')
btnSizer=wx.BoxSizer(wx.HORIZONTAL)
btnSizer.Add(okBtn, 0, wx.ALL,5)
btnSizer.Add(cancelBtn, 0, wx.ALL,5)
main_sizer.Add(btnSizer, flag=wx.ALIGN_RIGHT|wx.ALL, border=10)
前面我们提到了,该布局控件的
所有控件都放置在独立的单元格中,不能跨越多个单元格
。
如果您需要实现控件跨越多行或多列的布局,应该使用
wx.GridBagSizer
。
wx.GridBagSizer
是一个更灵活的布局管理器,允许控件在网格中跨越多个单元格。
3)GridBagSizer的界面及代码
这是一个简单的案例,主要来介绍
GridBagSizer 的缩放效果及其跨行的实现的。
它的代码如下所示
classMyFrame(wx.Frame):def __init__(self):
super().__init__(None, title="GridBagSizer 示例")#创建一个 BoxSizer 作为外层 sizer outer_sizer =wx.BoxSizer(wx.VERTICAL)#创建一个 GridBagSizer grid_sizer = wx.GridBagSizer(5, 5) #行间距和列间距为 5 #添加控件到 sizer grid_sizer.Add(wx.StaticText(self, label="姓名"), pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL, border=10)
grid_sizer.Add(wx.TextCtrl(self, size= (100, -1)), pos=(0, 1), flag=wx.EXPAND|wx.ALL, border=10)#添加一个控件,占用 1 行 2 列 grid_sizer.Add(wx.StaticText(self, label="占用两列的控件,测试内容很长很长很长很长"), pos=(1, 0), span=(1, 2), flag=wx.EXPAND)#添加更多控件 grid_sizer.Add(wx.StaticText(self, label="介绍内容"), pos=(2, 0), flag=wx.ALIGN_CENTER_VERTICAL, border=10)
grid_sizer.Add(wx.TextCtrl(self, style=wx.TE_MULTILINE), pos=(2, 1), flag=wx.EXPAND|wx.ALL, border=10)#让控件跟随窗口拉伸 grid_sizer.AddGrowableCol(1) #允许第二列拉伸 grid_sizer.AddGrowableRow(2) #允许第三行拉伸 #将 grid_sizer 添加到 outer_sizer,并设置顶部边距 outer_sizer.Add(grid_sizer, flag=wx.EXPAND | wx.ALL, border=10) #设置顶部、底部、左侧和右侧边距 #设置外层 sizer self.SetSizer(outer_sizer)
self.SetSize((400, 300)) #设置初始大小 outer_sizer.Fit(self)#调整窗口大小以适应控件 self.Layout()
注意,我们如果要使得控件能够拉伸,通过设置指定布局的行或者列可以拉伸即可,如上面代码介绍。
#让控件跟随窗口拉伸 grid_sizer.AddGrowableCol(1) #允许第二列拉伸 grid_sizer.AddGrowableRow(2) #允许第三行拉伸
注意上面的代码中的位置,以及跨行的设置代码
pos=(1, 0), span=(1, 2)
在
wx.GridBagSizer
中,
pos
和
span
参数用于控制控件在网格中的位置和跨越的行列数。
1. pos 参数
- 描述
:
pos
用于指定控件在网格中的位置。它是一个二元组,格式为
(行索引, 列索引)
。 - 示例
:
pos=(1, 0)
表示将控件放置在第 2 行(索引从 0 开始)第 1 列。
2. span 参数
- 描述
:
span
用于指定控件跨越的行数和列数。它也是一个二元组,格式为
(行数, 列数)
。 - 示例
:
span=(1, 2)
表示该控件在垂直方向上跨越 1 行,在水平方向上跨越 2 列。
结合使用:
当将
pos
和
span
一起使用时,可以创建复杂的布局。例如:
在这个示例中,控件的位置是
(1, 0)
,意味着它放置在第二行第一列;而
span=(1, 2)
意味着它占用整整 1 行和 2 列的空间。这使得该控件可以在横向上扩展到第二列,形成一个跨越的效果。
wx.GridBagSizer
是 wxPython 中一个非常灵活和强大的布局管理器,适用于需要复杂布局的用户界面。对于动态添加或删除控件的情况,
GridBagSizer
能够很好地处理控件的重新排列和调整。
复杂布局的简化
- 组合使用
:
GridBagSizer
可以与其他 Sizer(如
BoxSizer
和
FlexGridSizer
)组合使用,以满足更复杂的布局需求。 - 层次结构
: 可以在
GridBagSizer
中嵌套其他 Sizer,从而实现多层次的布局管理。