我们在开发桌面应用的时候,不管是之前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)并控制每个控件的边距和比例。
  • 特点:
    • 所有子控件按顺序排列。
    • 可以设置比例,控制控件的伸缩行为。
    • 较适合创建简单的、单一方向的布局。

2. 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 列。

2. 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、边距设置及可扩展性选项,可以有效提升应用程序的用户体验。

 如下面界面效果,就是基于该布局创建的。

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

  1. Electron+Vue开发跨平台桌面应用

    Electron+Vue开发跨平台桌面应用 xiangzhihong发布于 2019-12-23 虽然B/S是目前开发的主流,但是C/S仍然有很大的市场需求.受限于浏览器的沙盒限制,网页应用无法满足某 ...

  2. 使用XUL开发跨平台桌面应用

    先上图: 现在使用html,css,js开发桌面的优势越来越明显了,硬件性能的不断提升,人力成本越发昂贵,用户对界面要求越来越高,全球化下企业间的竞争越发激烈. 桌面软件50%+的工作量都在界面开发这 ...

  3. Electron开发跨平台桌面程序入门教程

    最近一直在学习 Electron 开发桌面应用程序,在尝试了 java swing 和 FXjava 后,感叹还是 Electron 开发桌面应用上手最快.我会在这一篇文章中实现一个HelloWord ...

  4. 快速了解Electron:新一代基于Web的跨平台桌面技术

    本文引用了作者“ ConardLi”的<用JS开发跨平台桌面应用,从原理到实践>一文部分内容,原文链接:segmentfault.com/a/1190000019426512,感谢原作者的 ...

  5. nodegui 使用react开发跨平台应用试用

    nodegui官方团队提供了基于react 应用开发方式,同时我们集成官方的packer 进行快速的应用打包 项目说明 项目使用了官方的计算机应用,我使用官方的react starter,同时添加了p ...

  6. 使用JavaScript开发跨平台的桌面应用

    任何可以使用JavaScript来编写的应用,最终会由JavaScript编写.--Atwood定律 Atwood's Law是Jeff Atwood在2007年提出的:"any appli ...

  7. 用HTML5+JS开发跨平台的桌面应用

    通过Node.js和WebKit技术的融合,开发者可以用HTML5技术编写UI,同时又能利用Node.js平台上众多library访问本地OS的能力,最终达到用Web技术就可以编写桌面应用的目的. 选 ...

  8. 使用nodegui 开发高性能的跨平台桌面端应用

    nodegui 是基于qt + nodejs 的跨平台桌面开发方案,官方同时也提供了很不错的文档 简单使用 使用官方的starter clone 代码 git clone https://github ...

  9. electron-vue:Vue.js 开发 Electron 桌面应用

    相信很多同学都知道 Electron 可以帮助开发人员使用前端技术开发桌面客户端应用,今天介绍的 electron-vue 框架是一套基于 Vue.js 开发 Electron 桌面应用的脚手架,该项 ...

  10. 带你从零学ReactNative开发跨平台App开发(七)

    ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...

随机推荐

  1. .NET 7 + Vue 权限管理系统 小白快速上手

    前言 今天给大家推荐一个超实用的开源项目<.NET 7 + Vue 权限管理系统 小白快速上手>,DncZeus的愿景就是做一个.NET 领域小白也能上手的简易.通用的后台权限管理模板系统 ...

  2. 1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到

    今天刷面试题看到一个有意思的面试题, 1000T的文件怎么能以最快速度从南京传到北京? 网络传输 首先我们考虑通过网络传输,需要多长时间. 我特地咨询了在运营商工作的同学,目前带宽: 家庭宽带下行最大 ...

  3. 使用 preloadRouteComponents 提升 Nuxt 应用的性能

    title: 使用 preloadRouteComponents 提升 Nuxt 应用的性能 date: 2024/8/19 updated: 2024/8/19 author: cmdragon e ...

  4. 微信小程序 BLE 基础业务接口封装

    写在前面:本文所述未必符合当前最新情形(包括蓝牙技术发展.微信小程序接口迭代等). 微信小程序为蓝牙操作提供了很多接口,但在实际开发过程中,会发现隐藏了不少坑.目前主流蓝牙应用都是基于低功耗蓝牙(BL ...

  5. chezmoi 使用

    chezmoi(发音 /ʃeɪ mwa/ (shay-moi)):在多台不同的机器上安全地管理你的 dotfiles. 安装 macOS: brew install chezmoi Ubuntu: s ...

  6. 在 Docker 中启动 Jupyter

    参考:Jupyter Docker Stacks documentation 容器地址在 quay.io/jupyter/scipy-notebook 如果你直接运行命令: docker run -p ...

  7. Docker 构建多平台镜像

    构建多平台镜像的方法分为两种:一种是在不同平台的机器上分别构建并推送对应平台的镜像,然后通过 Docker Manifest 将两个镜像标签合并为一个.另一种是通过 Docker buildx 在一台 ...

  8. 使用Pandas和NumPy实现数据获取

    公众号本文地址:https://mp.weixin.qq.com/s/Uc4sUwhjLTpOo85ubj0-QA 以某城市地铁数据为例,通过提取每个站三个月15分钟粒度的上下客量数据,展示Panda ...

  9. Dell R920 服务器iDrac口默认账号密码和IP

    Dell服务器iDrac口默认账号密码和IP   账号:root 密码:calvin IP:192.168.0.120/24

  10. C++ 性能反向优化——用哈希表unordered_map消除if else导致性能降低。

    从代码整洁的角度考虑,对于不同的值将调用相同参数的不同函数,我们通常可以通过建立从值到对应函数指针的哈希表,从而将if else消除.但实际可能使性能更低,以下是测试例子. 原因在于,if else分 ...