主题:
- Frames(框架)
- Windows(窗口)
- Controls/Widgets(控件/工具)
- Sizers(布局管理)
- Validators(验证器)
- 列表内容
在这个章节,我们将会讲解wxPython处理窗口和窗口内容的方法,包括创建输入组件,使用各种工具和控件widgets/controls。 我们将会创建一个计算股票价格的小程序。如果你已经是个有经验的GUI开发者,这部分的内容对你来说太简单了,你可以直接阅读下文的Boa-Constructor章节。
概述
可见元素的布局
在frame里面,你可以使用若干个wxWindow 子类来充实frame的内容,常用的元素有以下几种:
wx.MenuBar
,在frame
的顶部填加菜单栏wx.StatusBar
,在frame
的底部填加状态栏,显示状态信息wx.ToolBar
, 在frame
中添加工具栏wx.Control
的子类,它们代表用户接口的widgets
(例如显示数据and/or
处理用户输入的可见元素). 常见的wx.Control
对象包括wx.Button
,wx.StaticText
,wx.TextCtrl
和wx.ComboBox
.
所有的可见元素(wxWindow对象和它们的子类) 都能够容纳子元素。例如,一个wx.Frame可以容纳若干个wx.Panel对象,而这些wx.Panel又可以容纳若干wx.Button, wx.StaticText和wx.TextCtrl对象,就像这样:
请注意,这仅仅是描述可见元素的相关性,而不是描述应该怎样布局它们。如果要处理元素的布局,有以下几种选择:
- 您可以通过在父窗口中指定其精确的像素坐标来手动定位每个元素。由于平台之间的字体大小等差异,通常不建议使用此选项。
- 您可以使用
wx.LayoutConstraints
,尽管这些使用相当复杂。 - 您可以使用类似
Delphi
的LayoutAnchors
,这使得更容易使用wx.LayoutConstraints
。 - 您可以使用其中一个
wxSizer
子类。
Sizers(布局管理)
作为wx.Sizer
的子类,Sizer
能够被用来在frame
或window
中布置可见元素。它的作用包括:
- 为每个可见元素计算合适的尺寸
- 参照一定的尺度为元素定位
- 当
frame
的尺寸变化时,动态的对元素的尺寸和(或)位置做出调整
一些常见的Sizer包括:
wx.BoxSizer
, 基于水平线或垂直线布置可见元素wx.GridSizer
, 按照网格结构来布置元素wx.FlexGridSizer
, 与wx.GridSizer
类似,但更加灵活
通过调用sizer.Add(window, options...)
或者sizer.AddMany(...)
来给出一个wx.Window
对象的列表,sizer
就能够布置它们. Sizer
还能够嵌套,你可以把1个sizer
放进另1个sizer里面,例如把2个按水平线布置按钮的wx.BoxSizer放进另1个按垂直线布置元素的wx.BoxSizer里面,就像这样:
wx.GridSizer
接下来,我们给我们的文本编辑器增加2个嵌套的sizer,把1个水平布局的sizer嵌入到1个垂直布局的sizer里面:
# -*- coding: utf-8 -*-
import wx
import os
class MainWindow(wx.Frame):
def __init__(self,parent,title):
self.dirname = ''
# "-1"这个尺寸参数意味着通知wxWidget使用默认的尺寸
# 在这个例子中,我们使用200像素的宽度,和默认的高度
wx.Frame.__init__(self,parent,title=title,size=(200,-1))
self.contrl = wx.TextCtrl(self,style=wx.TE_MULTILINE)
self.CreateStatusBar()# 创建位于窗口的底部的状态栏
#设置菜单
filemenu = wx.Menu()
#wx.ID_OPEN 和 wx.ID_EXIT是wxWidgets提供的标准ID
menuOpen = filemenu.Append(wx.ID_OPEN,"&Open","Open a file")
menuAbout = filemenu.Append(wx.ID_ABOUT,"&About","Information about this program") #(ID, 项目名称, 状态栏信息)
filemenu.AppendSeparator() #分割线
menuExit = filemenu.Append(wx.ID_EXIT,"&Exit","Terminate the program") # (ID, 项目名称, 状态栏信息)
#创建菜单栏
menuBar = wx.MenuBar()
menuBar.Append(filemenu, "&File") #在菜单栏中添加filemenu菜单
self.SetMenuBar(menuBar) #在frame中添加菜单栏
#设置 events
self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
self.Bind(wx.EVT_MENU, self.OnAbout,menuAbout)
self.Bind(wx.EVT_MENU, self.OnExit,menuExit)
#设置 sizer
self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.buttons = []
for i in range(0,7):
self.buttons.append(wx.Button(self, -1, "Button &" + str(i)))
self.sizer2.Add(self.buttons[i], 1, wx.SHAPED)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.contrl,1,wx.EXPAND)
self.sizer.Add(self.sizer2,0,wx.GROW)
#激活sizer
self.SetSizer(self.sizer)
self.SetAutoLayout(True)
self.sizer.Fit(self)
self.Show(True)
#self.Show(True)
def OnAbout(self,e):
#创建一个带"OK"按钮的对话框。wx.OK是wxWidgets提供的标准ID
dlg = wx.MessageDialog(self,"A Small Editor.","About Sample Editor",wx.OK) #语法是(self, 内容, 标题, ID)
dlg.ShowModal() #显示对话框
dlg.Destroy() #当结束之后关闭对话框
def OnExit(self,e):
self.Close(True) #关闭整个frame
def OnOpen(self,e):
#wx.FileDialog语法:(self, parent, message, defaultDir, defaultFile,wildcard, style, pos)
dlg = wx.FileDialog(self,"Choose a file",self.dirname,"","*·*",wx.FD_OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
f = open(os.path.join(self.dirname,self.filename),'r')#暂时只读
self.contrl.SetValue(f.read())
f.colse()
dlg.Destroy()
app = wx.App(False)
frame = MainWindow(None,title="Small Editor")
app.MainLoop()
sizer.Add
方法有3个参数:
第1个参数把1个控件添加到sizer
里面
第2个参数是proportion权重因子,代表着这个控件相对于其它控件所占有的空间比例。例如,如果你有3个编辑控件,你希望它们的空间比例是3:2:1,那么在把它们加到sizer里面的时候,就按照这个比例数值来指定权重因子。如果权重因子为0,意味着这个控件或者sizer,在它的父sizer的布局方向上的尺寸,不会随着frame的增大(缩小)而增大(缩小)。在上面的例子中,第3个参数flag通常用wx.GROW
或者wx.EXPAND
,它们的作用是一样的,这意味着控件可以调整自己的尺寸以适应frame尺寸的变化。如果使用wx.SHAPED
来充当第3个参数,那么控件的尺寸虽然可以变化,但是形状会保持不变。
在上面的例子中,self.sizer.Add(self.sizer2, 0, wx.GROW)
权重因子是0,所以我们可以看到无论frame
的形状怎么变,self.sizer2
的高度是一直不变的,因为它的父sizer self.sizer
是按照垂直线来布置元素的。而self.sizer2
的宽度可以变,因为第3个参数是wx.GROW
。
另外,self.sizer2.Add(self.buttons[i], 1, wx.SHAPED)
第3个参数是wx.SHAPED
,所以无论frame的形状和尺寸怎样变化,按钮的形状都不会变,长度和宽度一直保持着相同的比率。
flag参数也可以使用wx.ALIGN_CENTER_HORIZONTAL
,wx.ALIGN_CENTER_VERTICAL
, 或wx.ALIGN_CENTER (for both)
来设置元素的居中方式,还可以使用wx.ALIGN_LEFT
, wx.ALIGN_TOP
, wx.ALIGN_RIGHT
, wx.ALIGN_BOTTOM
中的1个或2个组合,来设置元素的对齐方式。默认的对齐方式是wx.ALIGN_LEFT | wx.ALIGN_TOP
.
wx.Sizer
和它的子类有一个可能会让人感到困惑的地方,就是sizer
和父窗口之间的区别。当你把一个对象添加到sizer
里面时,不需要指定这个对象的父窗口。sizer
只是对窗口布局的方式,它本身并不是窗口。但是在创建对象的时候就需要指定父窗口。在上面的例子中,使用wx.Button
(语法)创建按钮的时候就需要指定frame
或window
作为按钮的父窗口,而不是指定sizer
来当父窗口。
一旦你完成可见元素的设置,并把它们加入到sizer
(或者嵌套的sizer
),下一步就是告诉frame
或window
来使用sizer
。用以下3个必要的步骤来完成这项工作:
window.SetSizer(sizer)
window.SetAutoLayout(True)
sizer.Fit(window)
SetSizer()
告诉你的window (or frame)
应该使用哪个sizer
。 SetAutoLayout()
告诉你的window
使用sizer
来布局组件 sizer.Fit()
告诉sizer
计算它所容纳的元素的初始化尺寸和位置
菜单Menus
我们已经在上文讲解过菜单的设置和使用方法,不再累述。
验证器Validators
当你创建一个对话框或者输入控件的时候,可以使用wx.Validator
来简化控件加载数据的进程,对输入的数据进行验证,或从中摘录数据。wx.Validator
还可以被用来截取控件域内发生的一些事件,例如敲击键盘的动作。要使用验证器,你必须先定义一个wx.Validator
的子类 (既不是wx.TextValidator
也不是wx.GenericValidator
),然后再调用myInputField.SetValidator(myValidator)
把它关联到你的控件域。
注意: 你定义的wx.Validator
子类必须覆盖wxValidator.Clone()
方法。