使用Sizer放置小部件
立即解锁
发布时间: 2025-08-17 02:04:03 阅读量: 1 订阅数: 15 


wxPython in Action:GUI开发的利器
### 使用Sizer放置小部件
#### 1. UI布局难题与Sizer的引入
在UI编程中,管理窗口内小部件的物理布局一直是个令人头疼的问题。最初采用的绝对定位方法,需要程序员明确设置每个小部件在屏幕上的大小和位置,这不仅繁琐,而且在窗口大小改变或小部件数量变化时,还需要手动调整每个小部件的大小和位置,以确保它们不会重叠或超出窗口边缘。如果要改变界面外观,还得重新进行整个布局过程。
为了解决这些问题,推荐使用Sizer。Sizer是一种自动算法,用于布局一组小部件。它附着在容器(通常是框架或面板)上,容器内创建的子小部件需要单独添加到Sizer中。当Sizer附着到容器后,它会管理容器内子小部件的布局。
使用Sizer有很多优点,例如当容器大小改变时,Sizer会自动重新计算子小部件的布局;如果某个子小部件大小改变,Sizer也能自动刷新布局。而且,在需要改变布局时,Sizer很容易管理。不过,Sizer强制实施的布局可能会有一定的限制,但最灵活的网格袋和盒子Sizer几乎能满足各种需求。
#### 2. 什么是Sizer?
wxPython中的Sizer是一个对象,其唯一目的是管理容器内一组小部件的布局。Sizer本身不是容器或小部件,它只是屏幕布局算法的一种表示。所有Sizer都是抽象类wx.Sizer的子类实例。wxPython提供了五种Sizer,如下表所示:
| Sizer类型 | 描述 |
| --- | --- |
| Grid | 非常基本的网格布局,最适合放置大小完全相同且能整齐排列成规则网格的小部件。 |
| Flex grid | 与网格Sizer略有不同,在小部件大小不同时能获得更好的效果。 |
| Grid bag | 网格Sizer家族中最灵活的成员,允许在网格中更随意地放置小部件,适用于可看作不规则网格的布局,可能有些项目会占据多个网格方块。 |
| Box | 水平或垂直的盒子,小部件排成一行。在控制小部件调整大小时的行为非常灵活,通常以嵌套方式使用,适用于几乎任何类型的布局,但确定如何嵌套盒子可能比较棘手。 |
| Static box | 带有线条和标题的标准盒子Sizer。 |
所有Sizer都知道每个子小部件的最小尺寸,通常还允许设置有关布局的额外信息,如小部件之间的间距、小部件可增大的尺寸以填充空间的程度,以及小部件小于分配空间时的对齐方式。Sizer根据这些信息使用布局算法确定每个子小部件的大小和位置。不同类型的Sizer会对同一组子小部件产生不同的最终布局。
使用Sizer有三个基本步骤:
1. **将Sizer添加到容器**:使用wx.Window的SetSizer(sizer)方法将Sizer连接到它要管理子小部件的容器。由于这是wx.Window的方法,意味着任何wxPython小部件都可以有Sizer,但Sizer仅对作为容器的小部件有意义。
2. **将每个子小部件添加到Sizer**:所有子小部件都需要单独添加到Sizer中,仅仅在容器中创建子小部件是不够的。添加小部件到Sizer的主要方法是Add(),该方法有几种不同的签名。
3. **(可选)启用Sizer计算其大小**:通过调用父窗口对象的wx.Window方法Fit()或Sizer的Fit(window)方法,告诉Sizer根据其子小部件计算其大小。这两种方法都会让Sizer根据对其子小部件的了解计算其大小,并调整父小部件的大小以适应。还有一个相关方法FitInside(),它不会改变父小部件的显示大小,但会改变其虚拟大小,即如果小部件在滚动面板内,wxPython会重新计算是否需要滚动条。
#### 3. 基本的网格Sizer
为了演示Sizer的工作原理,下面的例子使用了一个简单的小部件。以下是该小部件的代码:
```python
import wx
class BlockWindow(wx.Panel):
def __init__(self, parent, ID=-1, label="",
pos=wx.DefaultPosition, size=(100, 25)):
wx.Panel.__init__(self, parent, ID, pos, size,
wx.RAISED_BORDER, label)
self.label = label
self.SetBackgroundColour("white")
self.SetMinSize(size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
sz = self.GetClientSize()
dc = wx.PaintDC(self)
w, h = dc.GetTextExtent(self.label)
dc.SetFont(self.GetFont())
dc.DrawText(self.label, (sz.width - w) / 2, (sz.height - h) / 2)
```
接下来将使用不同的Sizer在框架中放置多个这样的块小部件,首先从网格Sizer开始。
#### 4. 什么是网格Sizer?
wxPython提供的最简单的Sizer是网格Sizer。顾名思义,网格Sizer将其子小部件放置在二维网格中。Sizer子列表中的第一个小部件放在网格的左上角,其余小部件从左到右、从上到下排列,直到最后一个小部件放在网格的右下角。
当调整网格Sizer的大小时,每个插槽会变大,但默认情况下,小部件的大小保持不变,并保持附着在其分配插槽的左上角。
以下是生成网格布局的代码:
```python
import wx
from blockwindow import BlockWindow
labels = "one two three four five six seven eight nine".split()
class GridSizerFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Basic Grid Sizer")
sizer = wx.GridSizer(rows=3, cols=3, hgap=5, vgap=5)
for label in labels:
bw = BlockWindow(self, label=label)
sizer.Add(bw, 0, 0)
self.SetSizer(sizer)
self.Fit()
app = wx.PySimpleApp()
GridSizerFrame().Show()
app.MainLoop()
```
从代码中可以看出,网格Sizer是wx.GridSizer类的实例。其构造函数显式设置了四个独特的属性:
```python
wx.GridSizer(rows, cols, vgap, hgap)
```
其中,rows和cols是整数,指定网格的大小,即水平和垂直放置的小部件数量。如果其中一个设置为0,其值将根据Sizer中的子小部件数量推断。例如,如果使用构造函数wx.GridSizer(2, 0, 0, 0)创建的Sizer有八个子小部件,它将需要四列才能将子小部件放在两行中。
vgap和hgap参数允许在小部件之间设置垂直和水平间距。vgap是相邻列之间的像素数,hgap是相邻行之间的像素数。这些像素是除了小部件指定的边框像素之外的。rows、cols、vgap和hgap属性都有相应的获取和设置方法。
网格Sizer的大小和放置算法很简单。当第一次调用Fit()时,它会创建初始的网格布局。如果需要,会根据列表中的元素数量计算行数和列数。网格中的每个空间大小相同,即使每个小部件的大小不同,也会使用最大尺寸来计算空间大小。因此,网格Sizer最适合子小部件自然大小相同的布局,如计算器键盘。如果小部件大小差异很大,使用网格Sizer可能会看起来有点奇怪,此时可以考虑使用弹性网格Sizer或网格袋Sizer。
#### 5. 如何向Sizer添加或移除子小部件?
子小部件添加到Sizer的顺序非常重要,这与将子小部件添加到父小部件的一般情况不同。Sizer的典型布局算法会逐个处理每个子小部件,以确定其在显示中的位置。下一个项目的放置取决于前一个项目的位置。例如,网格Sizer根据小部件的顺序从左到右、从上到下排列。在大多数情况下,在父小部件构造函数中创建Sizer时,可以按正确的顺序添加项目。但在某些情况下,特别是在运行时动态更改布局时,可能需要更多的灵活性。
##### 5.1 使用Add()方法
向Sizer添加小部件最常用的方法是Add(),它将新小部件追加到Sizer子列表的末尾。列表末尾的具体含义取决于S
0
0
复制全文
相关推荐









