Defining a custom event for a custom widget

Figure 3.9 displays the widget, a panel containing two buttons. The custom event, TwoButtonEvent, is triggered only after the user has clicked both buttons. The event contains a count of how many times the user has clicked on the widget. The idea here is to show how a new command event can be created out of smaller events—in this case, the left button down events on each individual button.

To create a custom event:

1 Define the new event class as a subclass of the wxPython class wx.PyEvent. If you want the event to be treated like a command event, create the event as a subclass of wx.PyCommandEvent. Like many override situations in wxPython, the Py version of a class allows the wxWidgets system to see that a method written in Python can override the C + + method.

2 Create an event type and a binder object to bind the event to specific objects.

3 Add code that can build instances of the new event, and introduce the instances into the event processing system using the ProcessEvent() method. Once the event is created, you can create bindings and handler methods as you would with any other wxPython event. Listing 3.5 displays the code that manages the widget.

Listing 3.5 Building a custom two-button event widget import wx class TwoButtonEvent(wx.PyCommandEvent):

wx.PyCommandEvent._init_(self, evtType, id)

self.clickCount = 0

def GetClickCount(self):

return self.clickCount

=1 Click Count: 4 HZ00

=1 Click Count: 4 HZ00

Figure 3.9 The custom two-button widget. Clicking both buttons in succession triggers a change in the window title.

Figure 3.9 The custom two-button widget. Clicking both buttons in succession triggers a change in the window title.

A Defining y' the event def SetClickCount(self, count): self.clickCount = count

© Generating an event type myEVT_TWO_BUTTON = wx.NewEventType() <—11 EVT_TWO_BUTTON = wx.PyEventBinder(myEVT_TWO_BUTTON, 1)

binder object

Creating a d bin class TwoButtonPanel(wx.Panel):

def _init_(self, parent, id=-1, leftText="Left", rightText="Right"):

self.leftButton = wx.Button(self, label=leftText) self.rightButton = wx.Button(self, label=rightText, pos=(100,0))

self.leftClick = False self.rightClick = False self.clickCount = 0 Binding the lower level events ft self.leftButton.Bind(wx.EVT_LEFT_DOWN, self.OnLeftClick) self.rightButton.Bind(wx.EVT_LEFT_DOWN, self.OnRightClick)

def OnLeftClick(self, event): self.leftClick = True self.OnClick()

event.Skip() f Skip for more processing def OnRightClick(self, event): self.rightClick = True self.OnClick()

event.Skip() g Skip for more processing def OnClick(self):

self.clickCount += 1

if self.leftClick and self.rightClick:

self_leftClick = False Creating the custom event h self.rightClick = False |

evt = TwoButtonEvent(myEVT_TWO_BUTTON, self.GetId()) <1—1 evt.SetClickCount(self.clickCount) <— Adding data to the event self.GetEventHandler().ProcessEvent(evt) <1-~l Processing i the event class CustomEventFrame(wx.Frame):

wx.Frame._init_(self, parent, id, 'Click Count: 0', size=(3 00, 100)) panel = TwoButtonPanel(self) Binding the custom event j self.Bind(EVT_TWO_BUTTON, self.OnTwoClick, panel) <1—1

self.SetTitle("Click Count: %s" % event.GetClickCount())

Define an event 1)

if __name__ == '__main__': handler function app = wx.PySimpleApp()

frame = CustomEventFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

O The constructor for the event class declares it a subclass of wx.PyCommandEvent. The wx.PyEvent and wx.PyCommandEvent are wxPython-specific constructs you can use to create new event classes and should be used to bridge the gap between the C + + classes and your Python code. If you try to use wx.Event directly, wxPython cannot see the new methods of your subclass during event processing, because the C++ event handlers do not know about the Python subclass. If you use wx.PyEvent, a reference to the Python instances are saved and later passed to the event handler directly, allowing the Python parts of the code to be used.

© The global function wx.NewEventType() is analogous to wx.NewId(); it returns an event type ID that is guaranteed to be unique. This value uniquely identifies an event type for the event processing system.

d The binder object is created using the new event type as a parameter. The second parameter is between 0 and 2, and represents the number of wxld identifiers expected by the wx.EvtHandler.Bind() method to determine which object is the source of the event. In this case, there is one ID representing the widget that generates the command event.

O To create the new higher level command event, the program must respond to specific user events, for instance, left mouse down on each button object. Depending on which display button is clicked, the events are bound to the OnLeftClick() and OnRightClick() methods. The handlers set a Boolean, indicating that the button has been clicked.

© 0 The Skip() call in this case allows for further processing after the event handler is complete. In this specific case, the new event does not require the skip call; it's dispatched before the handler method completes. However, all left down events need to call Skip() so that the handler does not block the eventual button click event. The button click event is not being handled by this program, but wxPython uses it to draw the button properly during a click. If it's blocked, the user does not get the expected feedback from a button push.

We chose not to bind to the wx.evt_button event to show you what happens if you don't call Skip() in cases like this. To see the difference in behavior between these two buttons, comment out either line © or 0.

Q If both left and right buttons are clicked, the code creates an instance of the new event. The event type and the ID of the two-button widget are the parameters of the constructor. Typically, a single event class can have more than one event type, although that's not the case in this example.

i The ProcessEvent() call injects the new event into the event system for processing, as described in section 3.4.1. The GetEventHandler() call returns an instance of wx.EvtHandler. In most cases, the returned instance is the widget object itself, but if other wx.EvtHandler() methods have been pushed on to the event handler stack, the top item in the stack is returned instead. j The custom event is bound just like any other event, in this case using the binder object created in line A. 1) The event handler function for this example changes the title of the window to display the new click count from the event.

At this point, your custom event can do anything pre-existing wxPython events can do, such as creating different widgets that trigger the same event. Creating events is an important part of the customization of wxPython.

Was this article helpful?

0 -2

Post a comment