2.3 常用控件介绍
尽管wxPython的核心模块和扩展模块提供了数以百计的各式控件和组件,但真正常用且必不可少的控件只有为数不多的几个:
wx.Frame - 窗口
wx.Panel - 面板
wx.StaticText - 静态文本
StaticBitmap - 静态图片
wx.TextCtrl - 单行或多行文本输入框
wx.Button - 按钮
wx.RadioButton - 单选按钮
wx.CheckBox - 复选按钮
wx.Choice - 下拉选择框
所有的wxPython控件都有一个不可或缺的parent参数和若干关键字参数,通常,关键字参数都有缺省默认值。
parent - 父级对象
id - 控件的唯一标识符,缺省或-1表示自动生成
pos - 控件左上角在其父级对象上的绝对位置
size - 控件的宽和高
name - 用户定义的控件名
style - 控件风格
wxPython的控件在使用风格上保持着高度的一致性,一方面因为它们从一个共同的基类派生而来,更重要的一点,wxPython不像PyQt那样充斥着随处可见的重载函数。比如,PyQt的菜单栏QMenuBar增加菜单,就有addMenu(QMenu)、addMenu(str)和addMenu(QIcon, str)等三种不同的重载形式。方法重载固然带来了很多便利,但也会增加使用难度,让用户无所适从。
下面的代码演示了上述常用控件的使用方法。
import wx
class MainFrame(wx.Frame):
"""从wx.Frame派生主窗口类"""
def __init__(self, parent):
"""构造函数"""
# 调用父类的构造函数,从默认风格中去除改变窗口大小
wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
self.SetTitle('wxPython控件演示')
self.SetIcon(wx.Icon('res/wx.ico'))
self.SetSize((860, 450))
self.Center
# 创建一个面板,用于放置控件
panel = wx.Panel(self, -1)
# 在x=20,y=20的位置,创建静态文本控件
st = wx.StaticText(panel, -1, '我是静态文本控件', pos=(20, 20))
# 在x=300,y=20的位置,创建静态图片
bmp = wx.Bitmap('res/forever.png')
sb = wx.StaticBitmap(panel, -1, bmp, pos=(280, 10))
# 在x=20, y=50的位置,创建文本输入框,指定输入框的宽度为260像素,高度默认
tc1 = wx.TextCtrl(panel, -1, value='我是文本输入框', pos=(20, 50), size=(260, -1))
# 在x=20, y=90的位置,创建文本输入框,指定样式为密码
tc2 = wx.TextCtrl(panel, -1, value='我是密码', pos=(20, 90), style=wx.TE_PASSWORD)
# 在x=20, y=130的位置,创建单选按钮,成组的单选按钮,第一个需要指定样式wx.RB_GROUP
rb1 = wx.RadioButton(panel, -1, '单选按钮1', pos=(20, 130), style=wx.RB_GROUP, name='rb1')
# 在x=100, y=130的位置,创建单选按钮,不再需要指定样式wx.RB_GROUP
rb2 = wx.RadioButton(panel, -1, '单选按钮2', pos=(100, 130), name='rb2')
# 在x=180, y=130的位置,创建单选按钮,不再需要指定样式wx.RB_GROUP
rb3 = wx.RadioButton(panel, -1, '单选按钮3', pos=(180, 130), name='rb3')
# 在x=20, y=160的位置,创建复选按钮
cb1 = wx.CheckBox(panel, -1, '复选按钮', pos=(20, 160))
# 在x=100, y=160的位置,创建复选按钮,指定其样式为wx.ALIGN_RIGHT
cb2 = wx.CheckBox(panel, -1, '文字在左侧的复选按钮', pos=(100, 160), style=wx.ALIGN_RIGHT)
# 在x=20,y=190的位置,创建按钮
ch = wx.Choice(panel, -1, choices=['wxPython', 'PyQt', 'Tkinter'], pos=(20, 190), size=(100, -1))
ch.SetSelection(0)
# 在x=120,y=190的位置,创建按钮
btn = wx.Button(panel, -1, '按钮', pos=(150, 190))
# 在x=20,y=230的位置,创建文本框,指定大小为260*150,并指定其样式为多行和只读
tc3 = wx.TextCtrl(panel, -1, value='我是多行文本输入框', pos=(20, 230), size=(260, 150), style=wx.TE_MULTILINE | wx.CB_READONLY)
if __name__ == '__main__':
app = wx.App # 创建一个应用程序
frame = MainFrame(None) # 创建主窗口
frame.Show # 显示窗主口
app.MainLoop # 应用程序进入事件处理主循环
代码运行界面如下图所示。
控件布局
3.1. 分区布局
上面的例子里,输入框、按钮等控件的位置由其pos参数确定,即绝对定位。绝对定位这种布局方式非常直观,但不能自动适应窗口的大小变化。更普遍的方式是使用被称为布局管理器的wx.Sizer来实现分区布局。所谓分区布局,就是将一个矩形区域沿水平或垂直方向分割成多个矩形区域,并可嵌套分区布局管理器wx.Sizer的派生类有很多种,最常用到是wx.BoxSizer和wx.StaticBoxSizer。
和一般的控件不同,布局管理器就像是一个魔法口袋:它是无形的,但可以装进不限数量的任意种类的控件——包括其他的布局管理器。当然,魔法口袋也不是万能的,它有一个限制条件:装到里面的东西,要么是水平排列的,要么是垂直排列的,不能排成方阵。好在程序员可以不受限制地使用魔法口袋,当我们需要排成方阵时,可以先每一行使用一个魔法口袋,然后再把所有的行装到一个魔法口袋中。
创建一个魔法口袋,装进几样东西,然后在窗口中显示的伪代码是这样的:
魔法口袋 = wx.BoxSizer # 默认是水平的,想要垂直放东西,需要加上 wx.VERTICAL 这个参数
魔法口袋.add(确认按钮, 0, wx.ALL, 0) # 装入确认按钮
魔法口袋.add(取消按钮, 0, wx.ALL, 0) # 装入取消按钮
窗口.SetSizer(魔法口袋) # 把魔法口袋放到窗口上
窗口.Layout # 窗口重新布局
魔法口袋的 add 方法总共有4个参数:第1个参数很容易理解,就是要装进口袋的物品;第2个参数和所有 add 方法的第2个参数之和的比,表示装进口袋的物品占用空间的比例,0表示物品多大就占多大地儿,不额外占用空间;第3个参数相对复杂些,除了约定装进口袋的物品在其占用的空间里面水平垂直方向的对齐方式外,还可以指定上下左右四个方向中的一个或多个方向的留白(padding);第4个参数就是留白像素数。
下面是一个完整的例子。
import wx
class MainFrame(wx.Frame):
"""从wx.Frame派生主窗口类"""
def __init__(self, parent):
"""构造函数"""
wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE)
self.SetTitle('分区布局')
self.SetIcon(wx.Icon('res/wx.ico'))
self.SetSize((640, 320)) # 设置窗口大小
self._init_ui # 初始化界面
self.Center # 窗口在屏幕上居中
def _init_ui(self):
"""初始化界面"""
# 创建容器面板
panel = wx.Panel(self, -1)
# 生成黑色背景的预览面板
view = wx.Panel(panel, -1, style=wx.SUNKEN_BORDER)
view.SetBackgroundColour(wx.Colour(0, 0, 0))
# 生成按钮和多行文本控件
btn_capture = wx.Button(panel, -1, '拍照', size=(100, -1))
btn_up = wx.Button(panel, -1, '↑', size=(30, 30))
btn_down = wx.Button(panel, -1, '↓', size=(30, 30))
btn_left = wx.Button(panel, -1, '←', size=(30, 30))
btn_right = wx.Button(panel, -1, '→', size=(30, 30))
tc = wx.TextCtrl(panel, -1, '', style=wx.TE_MULTILINE)
# 左右按钮装入一个水平布局管理器
sizer_arrow_mid = wx.BoxSizer
sizer_arrow_mid.Add(btn_left, 0, wx.RIGHT, 16)
sizer_arrow_mid.Add(btn_right, 0, wx.LEFT, 16)
# 生成带标签的垂直布局管理器
sizer_arrow = wx.StaticBoxSizer(wx.StaticBox(panel, -1, '方向键'), wx.VERTICAL)
sizer_arrow.Add(btn_up, 0, wx.ALIGN_CENTER|wx.ALL, 0) # 装入上按钮
sizer_arrow.Add(sizer_arrow_mid, 0, wx.TOP|wx.BOTTOM, 1) # 装入左右按钮
sizer_arrow.Add(btn_down, 0, wx.ALIGN_CENTER|wx.ALL, 0) # 装入下按钮
# 生成垂直布局管理器
sizer_right = wx.BoxSizer(wx.VERTICAL)
sizer_right.Add(btn_capture, 0, wx.ALL, 20) # 装入拍照按钮
sizer_right.Add(sizer_arrow, 0, wx.ALIGN_CENTER|wx.ALL, 0) # 装入方向键
sizer_right.Add(tc, 1, wx.ALL, 10) # 装入多行文本控件
# 生成水平布局管理器
sizer_max = wx.BoxSizer
sizer_max.Add(view, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5) # 装入左侧的预览面板
sizer_max.Add(sizer_right, 0, wx.EXPAND|wx.ALL, 0) # 装入右侧的操作区
# 为容器面板指定布局管理器,并调用布局方法完成界面布局
panel.SetSizer(sizer_max)
panel.Layout
if __name__ == '__main__':
app = wx.App
frame = MainFrame(None)
frame.Show
app.MainLoop
代码运行界面如下图所示。
3.2. 栅格布局
顾名思义,栅格布局就是将布局空间划分成网格,将控件放置到不同的网格内。栅格布局比较简单,用起来非常方便。栅格布局布局管理器也有很多种,GridBagSizer是最常用的一种。下面是一个使用GridBagSizer实现栅格布局的例子。
import wx
class MainFrame(wx.Frame):
"""从wx.Frame派生主窗口类"""
def __init__(self, parent):
"""构造函数"""
wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE)
self.SetTitle('栅格布局')
self.SetIcon(wx.Icon('res/wx.ico'))
self.SetSize((800, 440)) # 设置窗口大小
self._init_ui # 初始化界面
self.Center # 窗口在屏幕上居中
def _init_ui(self):
"""初始化界面"""
panel = wx.Panel(self, -1) # 创建容器面板
sizer = wx.GridBagSizer(10, 10)# 每个控件之间横纵间隔10像素
st = wx.StaticText(panel, -1, "用户名")
sizer.Add(st, (0, 0), flag=wx.TOP | wx.ALIGN_RIGHT, border=20) # 在第0行0列,距离上边缘20像素,右对齐
userName = wx.TextCtrl(panel, -1)
sizer.Add(userName, (0, 1), (1, 3), flag=wx.EXPAND | wx.TOP, border=20) # 在第0行1列,跨3列,距离上边缘20像素
sb = wx.StaticBitmap(panel, -1, wx.Bitmap('res/python.jpg'))
sizer.Add(sb, (0, 5), (7, 1), flag=wx.TOP | wx.RIGHT, border=20) # 在第0行4列,跨7行,距离上右边缘20像素
st = wx.StaticText(panel, -1, "密码")
sizer.Add(st, (1, 0), flag=wx.ALIGN_RIGHT) # 在第1行0列,右对齐
password = wx.TextCtrl(panel, -1, style=wx.TE_PASSWORD)
sizer.Add(password, (1, 1), (1, 3), flag=wx.EXPAND) # 在第1行1列,跨3列
st = wx.StaticText(panel, -1, "学历")
sizer.Add(st, (2, 0), flag=wx.ALIGN_RIGHT) # 在第2行0列,右对齐
level1 = wx.RadioButton(panel, -1, "专科")
sizer.Add(level1, (2, 1)) # 在第2行1列
level2 = wx.RadioButton(panel, -1, "本科")
sizer.Add(level2, (2, 2)) # 在第2行1列
level3 = wx.RadioButton(panel, -1, "研究生及以上")
sizer.Add(level3, (2, 3)) # 在第2行1列
st = wx.StaticText(panel, -1, "职业")
sizer.Add(st, (3, 0), flag=wx.ALIGN_RIGHT) # 在第3行0列,右对齐
professional = wx.Choice(panel, -1, choices=["学生", "程序员", "软件工程师", "系统架构师"])
professional.SetSelection(0)
sizer.Add(professional, (3, 1), (1, 3), flag=wx.EXPAND) # 在第3行1列,跨3列
# 语言技能
st = wx.StaticText(panel, -1, "语言技能")
sizer.Add(st, (4, 0), flag=wx.ALIGN_RIGHT | wx.LEFT, border=20) # 在第4行0列,距离左边缘20像素,右对齐
choices = ["C", "C ", "Java", "Python", "Lua", "JavaScript", "TypeScript", "Go", "Rust"]
language = wx.ListBox(panel, -1, choices=choices, style=wx.LB_EXTENDED)
sizer.Add(language, (4, 1), (1, 3), flag=wx.EXPAND) # 在第4行1列,跨3列
isJoin = wx.CheckBox(panel, -1, "已加入QQ群", style=wx.ALIGN_RIGHT)
sizer.Add(isJoin, (5, 0), (1, 4), flag=wx.ALIGN_CENTER) # 在第5行0列,跨4列, 居中
btn = wx.Button(panel, -1, "提交")
sizer.Add(btn, (6, 0), (1, 4), flag=wx.ALIGN_CENTER | wx.BOTTOM, border=20) # 在第6行0列,跨4列, 居中
sizer.AddGrowableRow(4) # 设置第4行可增长
sizer.AddGrowableCol(3) # 设置第3列可增长
panel.SetSizer(sizer)
panel.Layout
if __name__ == '__main__':
app = wx.App
frame = MainFrame(None)
frame.Show
app.MainLoop
代码运行界面如下图所示。