Custom red X control and wx.window styles documentation

Hi,
I needed the capability to cross out ( draw a big red X) a
wx.lib.masked.TextCtrl in my app.

First try

···

==========
My first thought was to simply derive a custom control from
wx.lib.masked.TextCtrl and override the OnPaint() event to draw the X after
the control finished its own painting. Unfortunately, when I called
SetFont() on my custom control it gets into infinite loop. SetFont()
internally calls __repr__. That goes to BaseMaskedTextCtrl.__repr__:

    def __repr__(self):
        return "<BaseMaskedTextCtrl: %s>" % self.GetValue()

As you can see it calls self.GetValue(). That goes all the way to
TextCtrl.GetValue():

    def GetValue(*args, **kwargs):
        """GetValue(self) -> String"""
        return _controls_.TextCtrl_GetValue(*args, **kwargs)

Now, _controls_.TextCtrl_GetValue calls BaseMaskedTextCtrl.__repr__ again
and there is your infinite loop.

I noticed the multiple inheritence with a mixin of BaseMaskedTextCtrl, so I
checked the C++ wxWidgets docs and the web and noticed that when they
subclass an existing control they use some macro, so I gave up on the idea
(I don't really like inheritence anyway).

Second try

My second thought was to use composition. I can create a simple X control
that just draws a red X and place it on top of any control I wish. This is
simpler and more generic (I can cross out any control not just
wx.lib.masked.TextCtrl). It turned out to be not so simple. My first
problem, was that my control was resized to fill the entire area of its
parent window (client area) after the Show() command. Here is a short
snippet that demonstrates it:

# --------------------------------------------------
import wx

class XCtrl(wx.Window):
    def __init__(self, parent, id, pos, size, style = 0, name = 'XCtrl'):
        wx.Window.__init__(self, parent, id, pos, size, style, name)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        dc.SetPen(wx.RED_PEN)
        size = self.GetSize()

        dc.DrawLine(0, 0, size[0], size[1])
        dc.DrawLine(size[0], 0, 0, size[1])

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Test XCtrl', pos=(150, 150),
size=(400,400))
        self.x = XCtrl(self, -1, pos=(10,10), size=(50, 50))
        assert self.x.GetSize() == (50, 50)
        self.Show()
        assert self.x.GetSize() == self.GetClientSize()

app = wx.PySimpleApp()
frame = MainFrame()
app.MainLoop()
# --------------------------------------------------

I could fix it by calling SetSize() after the call to Show() but it is a
hack and in my real program the code that calls Show() knows nothing about
the XCtrl down in the guts of the UI. I let this problem go because I
planned to make my masked.TextCtrl the parent of XCtrl and I wanted the
XCtrl to cover the entire masked.TextCtrl.

This is where the next problem kicked in. Every control gets two events to
paint itself: EVT_ERASE_BACKGROUND and EVT_PAINT. The default handling of
EVT_ERASE_BACKGROUND is to paint the entire control area with the background
color. However, since my XCtrl covers the masked.TextCtrl it erases the text
I want to display. So, I bound the EVT_ERASE_BACKGROUND to an empty handler
and thought I'm done. Not so fast. It looks like wxPython/wxWidgets
implements a celver optimization. If a control is hidden under control then
the hidden control never gets painted. The result was that I could see the
desktop through my app window. I tried all kind of hacks to no avail.

Finally, I looked in the windows styles section and found the solution. The
wx.TRANSPARENT_WINDOW style does exactly what I need it doesn't erase the
background, it draws the masked.TextCtrl underneath and then it calls the my
EVT_PAINT handler to draw the red X. Unfortunately, the documentation of
wx.TRANSPARENT_WINDOW is incorrect:

wx.TRANSPARENT_WINDOW The window is transparent, that is, it will not
receive paint events. Windows only.

I hope you enjoyed the story and that someone out there will fix the docs.

Thanks, Gigi

Cursors. Check out the Using Images->Cursor demo in the wxPython demo.

- Josiah

···

"Gigi" <gigi-s@bigfoot.com> wrote:

Hi,
I needed the capability to cross out ( draw a big red X) a
wx.lib.masked.TextCtrl in my app.