两阶段widget生成
两阶段widget生成是一种通过两个步骤来初始化widget和其UI的方法。这个方法被像XRC这样的类工厂应用,用于设置那些常规构造函数无法实现的样式标志(style flag)。这个部分将展示如何通过两阶段widget方法来生成一个框架(frame),起拥有特别的按钮样式。
注意:这个例子是Windows系统特殊的支持,其他平台并不支持这种ContextButton。
How to do it?
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, *args, **kwargs):
pre = wx.PreFrame()
pre.SetExtraStyle(wx.FRAME_EX_CONTEXTHELP)
pre.Create(parent, *args, **kwargs)
self.PostCreate(pre)
self.Center()
self.SetTitle("Two-Stage Frame")
class MyFrame2(wx.Frame):
def __init__(self, parent, id = wx.ID_ANY, title = "Normal Frame", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE):
super(MyFrame2, self).__init__(parent, id, title,
pos, size, style)
if __name__ == "__main__":
app = wx.PySimpleApp(False)
MyFrame(None).Show()
MyFrame2(None).Show()
app.MainLoop()
How it works?
在wxPython中,两阶段widget生成实际上是一个三步骤过程。首先,每个类其自身支持的PreClass座位工厂构造函数先预生成(pre-create)对象。在这步骤中,每个预生成的对象(pre object)都能被设置额外的样式标志(extra style flag)。下一个步骤是调用create方法。create方向像常规的构造函数那样创建空间的UI部分。最后一步是调用PostCreate方法,它所完成的就是把预创建的对象(pre object)转换成类中__init__方法返回的self。
理解继承的局限
wxPython是一个对wxWidget C++框架的封装。这意味着在大多数wxPython对象中都有一个C++对象。因为这个缘故,许多wxPython的类并不能像普通Python对象那样被重写。
为了证明这个特点,这个部分将展示如何去创建一个把子窗口(children window)自动加入布局管理器(Sizer)。这个将与未在Python层面公开其虚方法(virtual method)的类进行对比。
How to do it?
验证重写方法区别,我们创建两个类似的类,第一个继承自标准的Panel类(MyPanel),第二个继承自
PyPanel的类(
MyVirtualPanel)
import wx
class MyPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super(MyPanel, self).__init__(parent, *args, **kwargs)
sizer = wx.BoxSizer()
self.SetSizer(sizer)
def AddChild(self, child):
sizer = self.GetSizer()
sizer.Add(child, 0, wx.ALIGN_LEFT|wx.ALL, 8)
return super(MyPanel, self).AddChild(child)
class MyVirtualPanel(wx.PyPanel):
def __init__(self, parent, *args, **kwargs):
super(MyVirtualPanel, self).__init__(parent, *args, **kwargs)
sizer = wx.BoxSizer()
self.SetSizer(sizer)
def AddChild(self, child):
print "AddChild Trigger!\n"
sizer = self.GetSizer()
sizer.Add(child, 0, wx.ALIGN_LEFT|wx.ALL, 8)
return super(MyVirtualPanel, self).AddChild(child)
class MyFrame(wx.Frame):
def __init__(self, parent, *args, **kwargs):
super(MyFrame, self).__init__(parent, *args, **kwargs)
self.mypanel = MyPanel(self)
self.mypanel.SetBackgroundColour(wx.BLACK)
self.virtpanel = MyVirtualPanel(self)
self.virtpanel.SetBackgroundColour(wx.WHITE)
self.__DoLayout()
def __DoLayout(self):
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.mypanel, 1, wx.EXPAND)
sizer.Add(self.virtpanel, 1, wx.EXPAND)
self.SetSizer(sizer)
for x in range(3):
wx.Button(self.mypanel,
label = "MyPanel %d" % x)
wx.Button(self.virtpanel,
label = "VirtPanel %d" %x)
wx.FindWindowByLabel("MyPanel 0").Hide()
wx.FindWindowByLabel("MyPanel 1").Hide()
self.SetInitialSize(size = (300,200))
if __name__ == "__main__":
app = wx.PySimpleApp(False)
MyFrame(None).Show()
app.MainLoop()
How it works?
在两个版本的Panel类中,我们都重写了AddChild方法,当每次有新的子窗口(child window)生成时它就就应该被调用。由于AddChild是在类中C++部分来调用的,所以我们需要用特殊的版本才能来重写这个方法。在wxPython中以Py开头的类对多数方法都有公开的虚方法(virtual method)。所以它们就可以在子类中被重写,并在对象中被调用。
通过例子可以看出,上面的panel由于没有继承自PyPanel,所以它的三个按钮都重叠放在窗口的左上角
(译注:重叠放置的时候,只是显示第一个按钮,所以程序中将前两个隐藏(Hide)了,这样更为直观),这是由于重写AddChild方法从来未能被调用。而在继承自PyPanel的类中,重写的AddChild方法则被成功调用。
There's more
明确列举哪些方法是公开虚方法是比较困难的。这里有个小技巧来帮助你识别类中有哪些虚方法可用,请在Python的解释器里运行下面的代码:
import wx
for method in dir(wx.PySizer):
if method.startswith('base_'):
print method
dir()中的参数可以根据你想了解的类而更换。运行这段代码将打印出所有此类中的虚方法。以base_开头的方法是由SWIG(SWIG是一种简化脚本语言与C/C++接口的开发工具。简而言之,SWIG是一个通过包装和编译 C 语言程序来达到与脚本语言通讯目的的工具。)生成的,它是作为wxPython的一部分被绑定到wxWidget上,并且不能直接在你的代码中调用。与之相反,不是base_开头的方法则是可以被调用的。
(译注:通过查找,发现此种可以重写方法的类较少,PyWindow, PyPanel较多方法,下图就是PyPanel的结果)