【wxPython】wxPython之窗口操作

时间:2021-01-16 07:10:52

主题:

  • 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.TextCtrlwx.ComboBox.

所有的可见元素(wxWindow对象和它们的子类) 都能够容纳子元素。例如,一个wx.Frame可以容纳若干个wx.Panel对象,而这些wx.Panel又可以容纳若干wx.Button, wx.StaticTextwx.TextCtrl对象,就像这样:

【wxPython】wxPython之窗口操作

请注意,这仅仅是描述可见元素的相关性,而不是描述应该怎样布局它们。如果要处理元素的布局,有以下几种选择:

  • 您可以通过在父窗口中指定其精确的像素坐标来手动定位每个元素。由于平台之间的字体大小等差异,通常不建议使用此选项。
  • 您可以使用wx.LayoutConstraints,尽管这些使用相当复杂。
  • 您可以使用类似DelphiLayoutAnchors,这使得更容易使用wx.LayoutConstraints
  • 您可以使用其中一个wxSizer子类。

Sizers(布局管理)
作为wx.Sizer的子类,Sizer能够被用来在framewindow中布置可见元素。它的作用包括:

  • 为每个可见元素计算合适的尺寸
  • 参照一定的尺度为元素定位
  • frame的尺寸变化时,动态的对元素的尺寸和(或)位置做出调整

一些常见的Sizer包括:

  • wx.BoxSizer, 基于水平线或垂直线布置可见元素
  • wx.GridSizer, 按照网格结构来布置元素
  • wx.FlexGridSizer, 与wx.GridSizer类似,但更加灵活

通过调用sizer.Add(window, options...)或者sizer.AddMany(...)来给出一个wx.Window对象的列表,sizer就能够布置它们. Sizer还能够嵌套,你可以把1sizer放进另1sizer里面,例如把2个按水平线布置按钮的wx.BoxSizer放进另1个按垂直线布置元素的wx.BoxSizer里面,就像这样:

【wxPython】wxPython之窗口操作
注意: 在上面的例子中,6个按钮并不是按照23列来做阵列式布局的,如果要那样做,你必须使用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 (语法)创建按钮的时候就需要指定framewindow作为按钮的父窗口,而不是指定sizer来当父窗口。

一旦你完成可见元素的设置,并把它们加入到sizer(或者嵌套的sizer),下一步就是告诉framewindow来使用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()方法。