使用wxpython开发跨平台桌面应用,常用窗体布局BoxSizer,FlexGridSizer,GridBagSizer的介绍处理

我们在开发桌面应用的时候,不管是之前C#开发Winform的时候,还是现在使用wxpython来开发跨平台应用的时候,都需要了解布局的处理,wxpython的常用布局Sizer类,包括BoxSizer,FlexGridSizer,GridBagSizer都是我们需要经常打交道的,因此有必要对它们进行一些了解,这样开发界面起来才能得心应手。本篇随笔介绍一下这几种布局Sizer的不同以及对它们进行测试和封装使用。

1、BoxSizer,FlexGridSizer,GridBagSizer的布局介绍和差异

在 wxPython 中,布局管理是通过 Sizer 类来实现的。常用的 Sizer 类包括 BoxSizerFlexGridSizerGridBagSizer。下面是这些 Sizer 的介绍及其之间的差异:

  1. BoxSizer
  • 描述 : BoxSizer 是最简单的 Sizer 类型,允许你将控件沿一个方向(水平或垂直)排列。
  • 用法 : 适用于简单的线性布局。你可以指定方向(wx.HORIZONTALwx.VERTICAL)并控制每个控件的边距和比例。
  • 特点 :
    • 所有子控件按顺序排列。
    • 可以设置比例,控制控件的伸缩行为。
    • 较适合创建简单的、单一方向的布局。
  1. FlexGridSizer
  • 描述 : FlexGridSizer 是一个可以在行和列中进行灵活布局的 Sizer。它会自动调整每个单元格的大小,以适应控件的内容。
  • 用法: 适用于需要均匀分配空间的网格布局。
  • 特点 :
    • 行和列的大小可以根据内容自动调整。
    • 所有控件都放置在独立的单元格中,不能跨越多个单元格。
    • 适合需要规则网格的情况,比如表单或简单的网格布局。

3. GridBagSizer

  • 描述 : GridBagSizer 是一种更复杂的布局管理器,它允许你在一个网格中放置控件,并支持控件的大小、位置以及跨越多个行和列。
  • 用法: 适用于需要高度自定义布局的情况,比如复杂的用户界面。
  • 特点 :
    • 支持控件在网格中的精确控制。
    • 可以设置控件的对齐方式和边距。
    • 功能强大,适合复杂布局。

总结

  • BoxSizer: 最简单,适合线性布局。
  • FlexGridSizer: 灵活的网格,适合不规则网格布局。
  • GridBagSizer: 功能最强,支持复杂的自定义布局。

选择合适的 Sizer 取决于你的具体布局需求。对于简单场景,使用 BoxSizer 就足够了;而对于更复杂的布局,FlexGridSizerGridBagSizer 提供了更多的灵活性和控制能力。

2、使用布局Sizer控件创建不同的界面案例

为了直观的了解集中Sizer布局控件的不同,我们使用几个例子来测试它们的界面效果和区别。

1)BoxSizer的界面及代码

它是使用BoxeSizer来垂直放置几个部分的内容的,其中底部的两个按钮又是创建一个新的Panel进行维护,如下代码所示。

复制代码
class MyForm(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的方向处理。

该布局例子的代码如下所示。

复制代码
class Frame(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.GridBagSizerwx.GridBagSizer 是一个更灵活的布局管理器,允许控件在网格中跨越多个单元格。

3)GridBagSizer的界面及代码

这是一个简单的案例,主要来介绍 GridBagSizer 的缩放效果及其跨行的实现的。

它的代码如下所示

复制代码
class MyFrame(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 中,posspan 参数用于控制控件在网格中的位置和跨越的行列数。

  1. pos 参数
  • 描述 : pos 用于指定控件在网格中的位置。它是一个二元组,格式为 (行索引, 列索引)
  • 示例 : pos=(1, 0) 表示将控件放置在第 2 行(索引从 0 开始)第 1 列。
  1. span 参数
  • 描述 : span 用于指定控件跨越的行数和列数。它也是一个二元组,格式为 (行数, 列数)
  • 示例 : span=(1, 2) 表示该控件在垂直方向上跨越 1 行,在水平方向上跨越 2 列。

结合使用:当将 posspan 一起使用时,可以创建复杂的布局。例如:

复制代码
sizer.Add(wx.StaticText(self, label="占用两列的控件"), pos=(1, 0), span=(1, 2), flag=wx.EXPAND)

在这个示例中,控件的位置是 (1, 0),意味着它放置在第二行第一列;而 span=(1, 2) 意味着它占用整整 1 行和 2 列的空间。这使得该控件可以在横向上扩展到第二列,形成一个跨越的效果。

wx.GridBagSizer 是 wxPython 中一个非常灵活和强大的布局管理器,适用于需要复杂布局的用户界面。对于动态添加或删除控件的情况,GridBagSizer 能够很好地处理控件的重新排列和调整。

复杂布局的简化

  • 组合使用 : GridBagSizer 可以与其他 Sizer(如 BoxSizerFlexGridSizer)组合使用,以满足更复杂的布局需求。
  • 层次结构 : 可以在 GridBagSizer 中嵌套其他 Sizer,从而实现多层次的布局管理。

wx.GridBagSizer 提供了强大的功能和灵活性,使得开发者可以创建复杂和响应式的用户界面。通过合理使用 posspan、边距设置及可扩展性选项,可以有效提升应用程序的用户体验。
如下面界面效果,就是基于该布局创建的。

相关推荐
伍华聪3 天前
使用wxpython开发跨平台桌面应用,设计系统的登录界面
python开发
伍华聪10 天前
使用wxpython开发跨平台桌面应用,对WebAPI调用接口的封装
python开发
伍华聪12 天前
使用wxpython开发跨平台桌面应用,基类列表窗体的抽象封装处理
python开发
伍华聪12 天前
使用wxpython开发跨平台桌面应用,动态工具的创建处理
python开发
伍华聪19 天前
使用wxpython开发跨平台桌面应用,基类对话框窗体的封装处理
python开发
伍华聪21 天前
使用wxpython开发跨平台桌面应用,对常用消息对话框的封装处理
python开发
伍华聪22 天前
使用wxpython开发跨平台桌面应用,实现程序托盘图标和界面最小化及恢复处理
python开发
伍华聪25 天前
基于wxpython的跨平台桌面应用系统开发
python开发
伍华聪1 个月前
在Ubuntu小设备上使用VSCode+SSH开发部署nicegui的Web应用,并设置系统开机自动启动应用
python开发