wxpython图形用户界面编程
一、wxpython的基础
1.1 wxpython的基础
作为图形用户界面开发工具包 wxPython,主要提供了如下 GUI 内容:
- 窗口。
- 控件。
- 事件处理。
- 布局管理。
1.2 wxpython的类层次机构
1.3 wxpython的安装
- Windows 和 macOS 平台安装:
pip install -U wxPython
其中 install 是按照软件包,-U 是将指定软件包升级到最新版本。
- Linux 平台下使用 pip 安装有点麻烦,例如在 Ubuntu 16.04 安装,打开终端输入
如下指令:
pip install -U \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-16.04 \
wxPython
- 下载 wxPython 帮助文档和案例。
https://extras.wxpython.org/wxPython4/extras
1.4 第一个wxPython程序
import wx
# 创建应用程序对象
app = wx.App()
# 创建窗口对象
### size 是窗口的长和宽
### pos 使主窗口再电脑桌面显示的位置
### self.Center() 使窗口在电脑桌面显示居中位置,替代pos
frm = wx.Frame(None, title="第一个GUI程序!", size=(400, 300), pos=(100, 100))
frm.Show() # 显示窗口
app.MainLoop() # 进入主事件循环
1.5 窗口类MyFrame
# 自定义窗口类MyFrame
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="第一个GUI程序!", size=(400, 300), pos=(100, 100))
# TODO
class App(wx.App):
def OnInit(self):
# 创建窗口对象
frame = MyFrame()
frame.Show()
return True
def OnExit(self):
print('应用程序退出')
return 0
if __name__ == '__main__':
app = App()
app.MainLoop() # 进入主事件循环
1.6 使用面板(panel )
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="第一个GUI程序!", size=(600, 400), pos=(600, 200))
# 使用面板,面板指向父类MyFrame,也就是self
panel = wx.Panel(parent=self)
# 在面板上设置文本内容
statictext = wx.StaticText(parent=panel, label='Hello World!', pos=(10, 10))
class App(wx.App):
def OnInit(self):
# 创建窗口对象
frame = MyFrame()
frame.Show()
return True
def OnExit(self):
print('应用程序退出')
return 0
if __name__ == '__main__':
app = App()
app.MainLoop() # 进入主事件循环
Frame的内容面板图解结构,如下图
1.7 wxpython界面构建层次机构
注意:并不是指wxpython功能模块的类相关的继承层次机构
二、事件处理
在事件处理的过程中涉及4个要素:
- 事件。它是用户对界面的操作,在wxPython中事件被封装成为事件类wx.Event及其子类,例如按钮事件类是wx.CommandEvent,鼠标事件类是wx.Move Event。
- 事件类型。事件类型给出了事件的更多信息,它是一个整数。例如标事件wx.Move Event还可以有鼠标的右键按下(WX.EVT LEFT DOWN)和释放(wx.EVT左上)等。
- 事件源。它是事件发生的场所,就是各个控件,例如按钮事件的事件源是按钮。
- 事件处理者。 它是在wx.EvtHandler子类(事件处理类)中定义的一个方法。
绑定是通过事件处理类的 Bind()方法实现,Bind()方法语法如下:
Bind(self, event, handler,source=None, id=wx.ID_ANY, id2=Wx.ID_ANY)
- 其中参数event是事件类型,注意不是事件;
- handler是事件处理者,它对应到事件处理类中特定方法;
- source 是事件源;
- id是事件源的标识,可以省略source参数通过id绑定事件源:
- id2设置要绑定事件源id范围,当有多个事件源帮定到同一个事件处理者时可以使用此参数。
2.1 一对一事件处理
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="一对一事件处理", size=(600, 400))
self.Center() # 使主窗口在电脑桌面显示居中位置, pos可以不使用
# 使用面板,面板指向父类MyFrame,也就是self
panel = wx.Panel(parent=self)
# 在面板上设置文本内容
self.statictext = wx.StaticText(parent=panel, pos=(10, 10))
button = wx.Button(parent=panel, label='OK', pos=(100, 80))
# event是事件类型 handler是事件处理者,具体的自定义的执行方法 source是事件源,指定事件源的变量名字,也可以用id去指定事件源范围
self.Bind(event=wx.EVT_BUTTON, handler=self.on_click, source=button)
def on_click(self, event):
print(event)
self.statictext.SetLabelText('Hello Word')
class App(wx.App):
def OnInit(self):
# 创建窗口对象
frame = MyFrame()
frame.Show()
return True
def OnExit(self):
print('应用程序退出')
return 0
if __name__ == '__main__':
app = App()
app.MainLoop() # 进入主事件循环
代码执行效果图
2.2 一对多事件处理
多个事件源调用同一个方法进行处理
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="一对一事件处理", size=(600, 400))
self.Center() # 使主窗口在电脑桌面显示居中位置, pos可以不使用
# 使用面板,面板指向父类MyFrame,也就是self
panel = wx.Panel(parent=self)
# 在面板上设置文本内容
self.statictext = wx.StaticText(parent=panel, pos=(10, 10))
# 定义两个事件源,分别是button10, button11
button10 = wx.Button(parent=panel, id=10, label='button10', pos=(100, 80))
button11 = wx.Button(parent=panel, id=11, label='button11', pos=(140, 110))
# Bind是绑定不同类型的事件源去执行方法,这里的事件类型是button按钮
# event是事件类型 handler是事件处理者,具体的执行方法
# id是事件源起始id, id2是事件源的结束id,这是id范围是10到11,也就是说10到11的id事件源都会执行self.on_click方法
self.Bind(event=wx.EVT_BUTTON, handler=self.on_click, id=10, id2=11)
def on_click(self, event):
# 获取事件源的id
source_id = event.GetId()
# 根据事件源id执行不同的结果
if source_id == 10:
self.statictext.SetLabelText('button10')
else:
self.statictext.SetLabelText('button11')
class App(wx.App):
def OnInit(self):
# 创建窗口对象
frame = MyFrame()
frame.Show()
return True
def OnExit(self):
print('应用程序退出')
return 0
if __name__ == '__main__':
app = App()
app
三、布局管理
使用绝对布局会有如下问题:
- 子窗口(或控件)位置和大小不会随着父窗口的变化而变化。
- 在不同平台上显示效果可能差别很大。
- 在不同分辨率下显示效果可能差别很大。
- 字体的变化也会对显示效果有影响。
- 动态添加或删除子窗口(或控件)界面布局需要重新设计。
wxPython 提供了8个布局管理器类,如图19-10所示,包括:
wx.Sizer、wx.BoxSizer、wx.StaticBoxSizer , wx.WrapSizer, wx.StdDialogButtonSizer, wx.GridSizer 、 wx.FlexGridSizer和 wx.GridBagSizer .
3.1 Box 布局器
创建 wx.BoxSizer 对象时可以指定布局方向:
hbox = wx.BoxSizer(wx.HORIZONTAL) # 设置为水平方向布局
hbox = wx.BoxSizer() # 也是设置为水平方向布局,wx.HORIZONTAL是默认值可以省略
vhbox = wx.BoxSizer(wx.VERTICAL) # 设置为垂直方向布局
当需要添加子窗口(或控件)到父窗口时,需要调用 wx.BoxSizer 对象 Add()方法,
Add()方法是从父类 wx.Sizer 继承而来的,Add()方法语法说明如下:
Add(window, proportion=0, flag=0, border=0, userData=None) # 添加到父窗口
Add(sizer, proportion=0, flag=0, border=0, userData=None) # 添加到另外一个Sizer中,用于嵌套
Add(width, height, proportion=0, flag=0, border=0, userData=None) # 添加一个空白空间
注意: proportion=0 表示该组件不会随着窗口大小改变而改变大小
注意: proportion=1 表示该组件会随着窗口大小改变而改变大小
注意: 如果有多个组件都设置了proportion>0,则按比例分配空间
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="Box布局", size=(600, 400))
self.Center() # 使主窗口在电脑桌面显示居中位置, pos可以不使用
# 使用面板,面板指向父类MyFrame,也就是self
panel = wx.Panel(parent=self)
# 创建垂直方向box布局管理wx.VERTICAL
vbox = wx.BoxSizer(wx.VERTICAL)
# 设置静态文本模板,放在panel面板中
self.statictext = wx.StaticText(parent=panel, label='Button1单机')
# #添加静态文本到vbox布局管理器中,指定文本框在整个面板中所占权重为2,标志为固定大小填充,顶部有边框,水平居中,边框宽度为10
vbox.Add(self.statictext, proportion=2, flag=wx.FIXED_MINSIZE | wx.TOP | wx.CENTER, border=10)
button10 = wx.Button(parent=panel, id=10, label='button10')
button11 = wx.Button(parent=panel, id=11, label='button11')
self.Bind(event=wx.EVT_BUTTON, handler=self.on_click, id=10, id2=20)
# 创建水平方向box布局管理器
hbox = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(button10, proportion=0, flag=wx.EXPAND | wx.BOTTOM | wx.RIGHT, border=70)
hbox.Add(button11, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=70)
# 将水平box添加到垂直box
vbox.Add(hbox, proportion=1, flag=wx.CENTER)
# 设置面板的布局管理器vbox
panel.SetSizer(vbox)
'''将两个button放到一个水平方向布局管理器,然后将水平方向布局管理器放到垂直方向布局管理器中
添加控件到其父容器是通过parent属性,这里button和statictext的父容器都是面板,与布局管理器的添加是没有关系的
布局管理器添加是通过Add()方法添加,这个添加只是说将某个控件纳入布局管理器管理
不是添加到容器中,注意布局管理器不是一个容器'''
def on_click(self, event):
# 获取事件源的id
source_id = event.GetId()
# 根据事件源id执行不同的结果
if source_id == 10:
print('button10')
else:
print('button11')
class App(wx.App):
def OnInit(self):
# 创建窗口对象
frame = MyFrame()
frame.Show()
return True
def OnExit(self):
print('应用程序退出')
return 0
if __name__ == '__main__':
app = App()
app.MainLoop() # 进入主事件循环
运行效果:
3.2 StaticBox布局
wx.StaticBoxSizer构造方法如下:
-
1.wx.StaticBoxSizer(box,orient=HORIZONTAL)
box参数:wx.StaticBox(静态框)对象
orient参数:布局方向 -
2.wx.StaticBox(orient,parent,label=“”)
orient参数:布局方向
parent参数:设置所在的父窗口
label参数:设置边框的静态文本
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="StaticBox布局", size=(300, 200))
self.Center() # 使主窗口在电脑桌面显示居中位置, pos可以不使用
# 使用面板,面板指向父类MyFrame,也就是self
panel = wx.Panel(parent=self)
# 创建垂直方向box布局管理wx.VERTICAL
vbox = wx.BoxSizer(wx.VERTICAL)
# 设置静态文本模板,放在panel面板中
self.statictext = wx.StaticText(parent=panel, label='Button1单机')
# 添加静态文本到vbox布局管理器中
vbox.Add(self.statictext, proportion=2, flag=wx.FIXED_MINSIZE | wx.TOP | wx.CENTER, border=50)
button10 = wx.Button(parent=panel, id=10, label='button10')
button11 = wx.Button(parent=panel, id=11, label='button11')
self.Bind(event=wx.EVT_BUTTON, handler=self.on_click, id=10, id2=20)
# 创建静态框
sb