My problem
I’m using the wxWidgets library in Python (through the wxPython wrapper). I’m trying to implement a class, PaddedStaticText, that behaves identically to the WX StaticText class, but which has a user-configurable padding on each side. Ideally PaddedStaticText would be a drop-in replacement for StaticText. That kind of screams “subclass”, right? Unfortunately, I think that if I were to subclass StaticText I’d have to basically reimplement all of its drawing routines, and I don’t have the WX expertise to do that.
Solution one
The solution I came up with was to implement a class that had a StaticText attribute that would be placed within a couple of Sizers to effect the padding:
class PaddedStaticText(wx.Window):
def __init__(self, parent, label='', padding=(0, 0, 0, 0), **kwargs):
wx.Window.__init__(self, parent)
self.text = wx.StaticText(self, label=label, **kwargs)
inner_sizer = wx.BoxSizer(wx.VERTICAL)
inner_sizer.Add((0, padding[0]))
inner_sizer.Add(self.text, proportion=1, flag=wx.EXPAND)
inner_sizer.Add((0, padding[2]))
outer_sizer = wx.BoxSizer(wx.HORIZONTAL)
outer_sizer.Add((padding[3], 0))
outer_sizer.Add(inner_sizer, proportion=1, flag=wx.EXPAND)
outer_sizer.Add((padding[1], 0))
self.SetSizer(outer_sizer)
This class inherits from wx.Window, so it can be used in many of the same contexts in which I could have used a StaticText—adding it to a Sizer, for instance. Since a PaddedStaticText is not actually a StaticText, though, I couldn’t just call SetFont() on one of the instances. I had to make my own proxy method:
def SetFont(self, *args, **kwargs):
self.text.SetFont(*args, **kwargs)
Of course, I had to write such a stub for each of the methods of StaticText that I wanted to be available. This approach works, but it didn’t feel elegant at all. I figured that in Python there must be a better way to do something like this.
Solution two
Based on this SO answer I wrote this simple Proxy class:
class Proxy(object):
def __init__(self, underlying_object):
self._underlying_object = underlying_object
def __getattribute__(self, name):
obj = object.__getattribute__(self, '_underlying_object')
return object.__getattribute__(obj, name)
Then I could express PaddedStaticText as a subclass of Proxy with the underlying_object
set to a StaticText instance. Unfortunately, WX balked when I tried to add my new PaddedStaticText instances to a Sizer. Since it’s a wrapper around a C++ library I guess WX doesn’t really respect Python’s duck typing: even though a PaddedStaticText can respond appropriately whenever a StaticText method is called on it, all wxPython cared about was the fact that it inherited from Proxy and not from wx.Window.
From there I briefly played around with making PaddedStaticText inherit from both Proxy and from wx.Window, but that didn’t work (and it’s an example of an inheritance diamond anyway). How can I implement my PaddedStaticText class in a way that Just Works? I’d appreciate suggestions on either of my solution ideas, or something completely different that solves the problem.