wxPython: Can a wx.PyControl contain a wx.Sizer?

2019-07-20 03:55发布

问题:

Can a wx.PyControl contain a wx.Sizer?

Note that what I am ultimately trying to do here (spinner with float values) is already answered in another question. I am particularly interested in layouting widgets within a wx.PyControl, a skill which might prove useful if I come across a need to make my own custom widgets. I already read through CreatingCustomControls, but it didn't use sizers within the wx.PyControl subclass.

Using my code below, my CustomWidget just doesn't look right. I'm not yet doing a DoGetBestSize because I think that applies to a wx.Sizer acting on a widget. I am actually having a wx.Sizer doing its thing inside a CustomWidget.

Here is my code (without the event bindings between sub-widgets):
EDIT: Here is my corrected class code, thanks to Steven Sproat:

import wx

class CustomWidget(wx.PyControl):
  def __init__(self, parent):
    wx.PyControl.__init__(self, parent=parent, style=wx.NO_BORDER) # Style added.
    text = wx.TextCtrl(parent=self)
    spin = wx.SpinButton(parent=self, style=wx.SP_VERTICAL)
    sizer = wx.GridBagSizer()
    self.layout(text, spin, sizer)
    self.OnInit(text, sizer)

  def OnInit(self, text, sizer):
    text.SetValue(u"0.000")

  def layout(self, text, spin, sizer):
    self.SetSizer(sizer)
    sizer.Add(text, pos=(0, 0), flag=wx.ALIGN_CENTER)
    sizer.Add(spin, pos=(0, 1), flag=wx.ALIGN_CENTER)
    self.Fit()
    self.Layout() # This is what I lacked. I needed to call .Layout()
    self.CenterOnParent()

回答1:

Yes, it can. You just need to call Layout() to tell the sizer to recalculate/layout its children.

import wx

class Frame(wx.Frame):
  def __init__(self):
    wx.Frame.__init__(self, None)
    blah  = CustomWidget(self)
    self.Show(True)

class CustomWidget(wx.PyControl):
  def __init__(self, parent):
    wx.PyControl.__init__(self, parent=parent)
    text = wx.TextCtrl(parent=self)
    spin = wx.SpinButton(parent=self, style=wx.SP_VERTICAL)
    sizer = wx.GridBagSizer()
    self.layout(text, spin, sizer)
    self.OnInit(text, sizer)

  def OnInit(self, text, sizer):
    text.SetValue(u"0.000")

  def layout(self, text, spin, sizer):
    self.SetSizer(sizer)
    sizer.Add(text, pos=(0, 0), flag=wx.ALIGN_CENTER)
    sizer.Add(spin, pos=(0, 1), flag=wx.ALIGN_CENTER)
    self.Fit()
    self.Layout()
    self.CenterOnParent()

app = wx.App()
f = Frame()
app.MainLoop()

By the way, it would be nice in the future if you could attach a runnable sample as above :)



回答2:

Since classes derived from wxControl are not normally used for containing other widgets the auto-layout code is not present and so it will not call Layout() when it gets the EVT_SIZE event. You can easily add that ability to your class by Bind()ing a handler for EVT_SIZE and calling self.Layout() from there. Then it will act just like a panel does when it gets the size event and has children and a sizer.