catching events from children

I’m trying to make a Panel act like a button, following the example of wx.lib.buttons.GenButton. (I know this is clumsy, but the panels are potentially complex and I’m using sizers for layout.) This works for the panel itself, but clicking on any of the controls contained in the panel - just StaticText and StaticBitmap objects - does not propagate the event. I assume this is because the events I’m binding aren’t wx.CommandEvents, but I can’t intercept them directly either: I tried binding to the appropriate events with a text control as the source, but that doesn’t work. How can I get the click to “pass through” to the panel? Or is what I’m doing futile?

thanks,

Nat

sample code:

mport wx

class MyFrame (wx.Frame) :

def init (self) :

wx.Frame.init(self, None, -1, “Test frame”, style=wx.DEFAULT_FRAME_STYLE)

panel = MyPanel(self)

szr = wx.BoxSizer(wx.VERTICAL)

self.SetSizer(szr)

#self.Fit()

self.Layout()

class MyPanel (wx.Panel) :

def init (self, parent) :

wx.Panel.init(self, parent, -1)

self.is_active = False

self.SetBackgroundColour((220,220,220))

szr = wx.BoxSizer(wx.VERTICAL)

self.SetSizer(szr)

txt = wx.StaticText(self, -1, “This is some text. Click me!”)

szr.Add(txt, 0, wx.ALL|wx.EXPAND, 10)

szr.Layout()

szr.Fit(self)

self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)

self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)

self.Bind(wx.EVT_MOTION, self.OnMotion)

self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown, txt)

self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp, txt)

self.Bind(wx.EVT_MOTION, self.OnMotion, txt)

def OnLeftDown (self, event) :

self.CaptureMouse()

self.SetFocus()

self.set_active()

def OnLeftUp (self, event) :

if self.is_active :

if self.HasCapture() :

self.ReleaseMouse()

self.reset_active()

print “panel clicked”

def set_active (self) :

self.is_active = True

self.SetBackgroundColour((130,160,180))

self.Refresh()

def reset_active (self) :

self.SetBackgroundColour((220,220,220))

self.Refresh()

self.is_active = False

def OnMotion(self, event):

if not self.IsEnabled() or not self.HasCapture():

return

if event.LeftIsDown() and self.HasCapture():

x,y = event.GetPositionTuple()

w,h = self.GetClientSizeTuple()

if self.is_active and (x<0 or y<0 or x>=w or y>=h):

self.reset_active()

return

event.Skip()

if name == “main” :

a = wx.App(0)

frame = MyFrame()

frame.Show()

a.MainLoop()

There's maybe an interesting way to do this that I am not aware of, but
what about just binding left_up from each of the objects in your panel
to a common handler? Code below:

Che

···

On Wed, Apr 29, 2009 at 5:48 PM, Nathaniel Echols <nathaniel.echols@gmail.com> wrote:

I'm trying to make a Panel act like a button, following the example of
wx.lib.buttons.GenButton. (I know this is clumsy, but the panels are
potentially complex and I'm using sizers for layout.) This works for the
panel itself, but clicking on any of the controls contained in the panel -
just StaticText and StaticBitmap objects - does not propagate the event. I
assume this is because the events I'm binding aren't wx.CommandEvents, but I
can't intercept them directly either: I tried binding to the appropriate
events with a text control as the source, but that doesn't work. How can I
get the click to "pass through" to the panel? Or is what I'm doing futile?
thanks,
Nat
sample code:
mport wx
class MyFrame (wx.Frame) :
def __init__ (self) :
wx.Frame.__init__(self, None, -1, "Test frame",
style=wx.DEFAULT_FRAME_STYLE)
panel = MyPanel(self)
szr = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(szr)
#self.Fit()
self.Layout()
class MyPanel (wx.Panel) :
def __init__ (self, parent) :
wx.Panel.__init__(self, parent, -1)
self.is_active = False
self.SetBackgroundColour((220,220,220))
szr = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(szr)
txt = wx.StaticText(self, -1, "This is some text. Click me!")
szr.Add(txt, 0, wx.ALL|wx.EXPAND, 10)
szr.Layout()
szr.Fit(self)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown, txt)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp, txt)
self.Bind(wx.EVT_MOTION, self.OnMotion, txt)
def OnLeftDown (self, event) :
self.CaptureMouse()
self.SetFocus()
self.set_active()
def OnLeftUp (self, event) :
if self.is_active :
if self.HasCapture() :
self.ReleaseMouse()
self.reset_active()
print "panel clicked"
def set_active (self) :
self.is_active = True
self.SetBackgroundColour((130,160,180))
self.Refresh()
def reset_active (self) :
self.SetBackgroundColour((220,220,220))
self.Refresh()
self.is_active = False
def OnMotion(self, event):
if not self.IsEnabled() or not self.HasCapture():
return
if event.LeftIsDown() and self.HasCapture():
x,y = event.GetPositionTuple()
w,h = self.GetClientSizeTuple()
if self.is_active and (x<0 or y<0 or x>=w or y>=h):
self.reset_active()
return
event.Skip()
if __name__ == "__main__" :
a = wx.App(0)
frame = MyFrame()
frame.Show()
a.MainLoop()

------
#Boa:Frame:Frame1

import wx

def create(parent):
    return Frame1(parent)

[wxID_FRAME1, wxID_FRAME1CHECKBOX1, wxID_FRAME1PANEL1, wxID_FRAME1PANEL2,
wxID_FRAME1TEXTCTRL1,
] = [wx.NewId() for _init_ctrls in range(5)]

class Frame1(wx.Frame):
    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
              pos=wx.Point(136, 125), size=wx.Size(410, 489),
              style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
        self.SetClientSize(wx.Size(402, 455))

        self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
              pos=wx.Point(0, 0), size=wx.Size(402, 455),
              style=wx.TAB_TRAVERSAL)

        self.panel2 = wx.Panel(id=wxID_FRAME1PANEL2, name='panel2',
              parent=self.panel1, pos=wx.Point(72, 64), size=wx.Size(200, 100),
              style=wx.TAB_TRAVERSAL)
        self.panel2.SetBackgroundColour(wx.Colour(128, 128, 255))
        self.panel2.Bind(wx.EVT_LEFT_UP, self.OnPanel2LeftUp)

        self.textCtrl1 = wx.TextCtrl(id=wxID_FRAME1TEXTCTRL1, name='textCtrl1',
              parent=self.panel2, pos=wx.Point(8, 16), size=wx.Size(100, 21),
              style=0, value='textCtrl1')
        self.textCtrl1.Bind(wx.EVT_LEFT_UP, self.OnPanel2LeftUp)

        self.checkBox1 = wx.CheckBox(id=wxID_FRAME1CHECKBOX1, label='checkBox1',
              name='checkBox1', parent=self.panel2, pos=wx.Point(8, 48),
              size=wx.Size(70, 13), style=0)
        self.checkBox1.Bind(wx.EVT_LEFT_UP, self.OnPanel2LeftUp)

    def __init__(self, parent):
        self._init_ctrls(parent)

    def OnPanel2LeftUp(self, event):
        dlg = wx.MessageDialog(self, 'You clicked something',
'Caption', wx.OK | wx.ICON_INFORMATION)
        try:
            result = dlg.ShowModal()
        finally:
            dlg.Destroy()
        event.Skip()

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = create(None)
    frame.Show()

    app.MainLoop()

Yes, I discovered this a few minutes later:

class MyPanel (wx.Panel) :

def init (self, parent) :

omitted repeated stuff

self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)

self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)

self.Bind(wx.EVT_MOTION, self.OnMotion)

for child in self.GetChildren() :

child.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)

child.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)

child.Bind(wx.EVT_MOTION, self.OnMotion)

I’m a little suspicious of this approach, but it works well enough in my app, and there aren’t too many children anyway. I had been trying to do this before:

for child in self.GetChildren() :

self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown, child)

. . .which didn’t work, probably because (again) the events don’t propagate.

thanks,

Nat

···

On Wed, Apr 29, 2009 at 3:23 PM, C M cmpython@gmail.com wrote:

There’s maybe an interesting way to do this that I am not aware of, but

what about just binding left_up from each of the objects in your panel

to a common handler?

Nathaniel Echols wrote:

I had been trying to do this before:

    for child in self.GetChildren() :
      self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown, child)

. . .which didn't work, probably because (again) the events don't propagate.

Correct. See self.Bind vs. self.button.Bind - wxPyWiki

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!