wxPython, how do I fire events? - python

I am making my own button class, subclass of a panel where I draw with a DC, and I need to fire wx.EVT_BUTTON when my custom button is pressed. How do I do it?

The Wiki is pretty nice for reference. Andrea Gavana has a pretty complete recipe for building your own custom controls. The following is taken directly from there and extends what FogleBird answered with (note self is referring to a subclass of wx.PyControl):
def SendCheckBoxEvent(self):
""" Actually sends the wx.wxEVT_COMMAND_CHECKBOX_CLICKED event. """
# This part of the code may be reduced to a 3-liner code
# but it is kept for better understanding the event handling.
# If you can, however, avoid code duplication; in this case,
# I could have done:
#
# self._checked = not self.IsChecked()
# checkEvent = wx.CommandEvent(wx.wxEVT_COMMAND_CHECKBOX_CLICKED,
# self.GetId())
# checkEvent.SetInt(int(self._checked))
if self.IsChecked():
# We were checked, so we should become unchecked
self._checked = False
# Fire a wx.CommandEvent: this generates a
# wx.wxEVT_COMMAND_CHECKBOX_CLICKED event that can be caught by the
# developer by doing something like:
# MyCheckBox.Bind(wx.EVT_CHECKBOX, self.OnCheckBox)
checkEvent = wx.CommandEvent(wx.wxEVT_COMMAND_CHECKBOX_CLICKED,
self.GetId())
# Set the integer event value to 0 (we are switching to unchecked state)
checkEvent.SetInt(0)
else:
# We were unchecked, so we should become checked
self._checked = True
checkEvent = wx.CommandEvent(wx.wxEVT_COMMAND_CHECKBOX_CLICKED,
self.GetId())
# Set the integer event value to 1 (we are switching to checked state)
checkEvent.SetInt(1)
# Set the originating object for the event (ourselves)
checkEvent.SetEventObject(self)
# Watch for a possible listener of this event that will catch it and
# eventually process it
self.GetEventHandler().ProcessEvent(checkEvent)
# Refresh ourselves: the bitmap has changed
self.Refresh()

Create a wx.CommandEvent object, call its setters to set the appropriate attributes, and pass it to wx.PostEvent.
http://docs.wxwidgets.org/stable/wx_wxcommandevent.html#wxcommandeventctor
http://docs.wxwidgets.org/stable/wx_miscellany.html#wxpostevent
This is a duplicate, there is more information here on constructing these objects:
wxPython: Calling an event manually

Related

How can I perform delayed GUI changes while being able to see the before and after?

I need my GUI to feedback the user. For this purpose I color the background of a certain button in Green or Red according to the result of a certain verification. My purpose is to leave the Green color for several seconds and then return to the initial state (clear the fields and recover the button's original color).
My problem is that the "after" function, that I use for delaying the GUI before I return to the default GUI, doesn't show the before and after. I only see that the button is SUNKEN and then raised after the fields and buttons already returned to their defaults.
What am I doing wrong ?
if condition1 == condition2:
orig_color = self.button2.cget("background")
self.button2.config(bg='springgreen2')
self.return2default(orig_color)
self.after(3000) # 3 seconds delay to realize a Pass result
# return to the defaults
self.SN_field.delete("1.0", "end")
self.HwVer_field.delete("1.0", "end")
self.button2.config(bg=color)
else:
self.button2.config(bg='red2')
If you want something to happen after a delay, move that "something" into a function and schedule it to be called with after. That will allow the event loop to continue to process events (including events that cause the display to refresh).
For example:
def reset(self):
self.SN_field.dellete("1.0", ,"end")
self.HwVer_field.delete("1.0", "end")
self.button2.config(bg=color)
To call it, use after:
if condition1 == condition2:
orig_color = self.button2.cget("background")
self.button2.config(bg='springgreen2')
self.return2default(orig_color)
self.after(3000, self.reset)
When you call after with only a time argument, it causes your entire program to go to sleep, which means it is not able to update the display while it is sleeping.

Pyglet custom Event Loop fails with multiple windows

I have a custom Event Loop for Pyglet that can be simplified to this simple piece of code
while True:
pyglet.clock.tick()
for window in pyglet.app.windows:
window.switch_to()
# rendering code
window.flip()
window.dispatch_events() # <--- program hangs
When there is only one window, the event loop works fine but when I attempt to have two windows, the loop hangs at window.dispatch_events(). Calling the functions in different order yield the same problem. I also tried debugging:
pyglet.options['debug_win32'] = True
Error Message:
...
_user32.UnregisterHotKey(self._hwnd, 0)
b'Hot key is not registered.\r\n'
After some more tinkering I managed to get the windows responsive with a dirty hack, but still there are quite a few issues that arise with event processing. Fix was calling an update immediately after setting window visible.
Sample Initialisation code:
if __name__=="__main__":
engine = Engine()
engine.run()
class Engine: # test program
def __init__(self):
self.application = customLib.Application()
# define windows, by default they are set as not visible
self.window1 = self.application.guiManager.createWindow("Window 1",
(1000, 700),
cursorPath="../data/cursor.png")
self.window2 = self.application.guiManager.createWindow("Window 2",
(1000, 700),
cursorPath="../data/cursor.png")
# load up code such as loading textures and .obj files
self.window1.pygletWindow.set_visible()
self.window1.update() # calling this after setting self.window2 visible will cause original error
self.window2.pygletWindow.set_visible()
self.window2.update()
def run(self):
self.application.run()
### CUSTOMLIB ###
class Application:
...
def run(self):
while True:
pyglet.clock.tick()
self.guiManager.update()
class GUIManager: # manages windows
...
def update(self):
for window in self.windows:
window.update()
class Window: # pyglet window higher level wrapper
pygletWindow = None
...
def update(self):
e = self.pygletWindow.dispatch_events()
# all events are intercepted by a custom class, it transforms all unhandled events (keyboard, mouse, and close button for now) into a list of events that can then be looped through rather than requiring twenty different functions.
# the event list is acquired, looped through and then cleared
for event in self.eventManager.get():
if event.type == customLib.CLOSE:
# close application
elif event.type == customLib.K_W:
# move player forward
# rendering
self.pygletWindow.switch_to()
# window is cleared
# window content rendered
self.pygletWindow.flip()
The way around the previous problem seems a little foolish and not robust. With the current event loop I have, window feedback sometimes does not work. For example, to close the window I must sometimes click it several times or, when moving the window around it won't always grab the window. I dispatch_events() at every loop and my custom event handler does not handle window motion events. The glitching of event handling only occurs with multiple windows. With a single window the event loop is flawless. This "bug" can be fixed by using varients of pyglet EventLoop.
I have concluded that a custom Event Loops' cons outweigh its pros. It is much easier to subclass EventLoop (since it's optimised for the OS) and override any pesky routines. Even better is to make a whole new event loop copying initialisation code of EventLoop and any backend routines (utilising PlatformEventLoop), which then allows you to very easily implement custom routines. I will be leaving this question up because there is very little documentation about this matter.

wxpython - Post custom event that propagates

Right now I have a child panel that post some event. I've tried
myEvent = events.ChangedAvailModelsEvent()
#self.GetEventHandler().ProcessEvent(myEvent)
wx.PostEvent(self, myEvent)
I create my event with
ChangedAvailModelsEvent, EVT_CHANGEDAVAILMODELS = NewEvent()
I bind with
self.Bind(events.EVT_CHANGEDAVAILMODELS, self.OnUpdate)
which takes place in some nth grandparent. I have print statements telling me the event was processed, but I my function is never called afterwards. I'm not sure what the problem is. I feel like the event is not propagating upwards. Any help?
Change it to use NewCommandEvent instead of NewEvent. Command events will automatically propagate up the parent chain in search of a handler. Non-command events will only be processed by the object they are posted to. See http://wiki.wxpython.org/self.Bind_vs._self.button.Bind.

Event triggered when selecting a ListBox item

I initialize a wx.ListBox like this :
mylistbox = wx.ListBox(self, style=wx.LB_SINGLE)
mylistbox.Bind(wx.EVT_LISTBOX, self.OnEventListBox)
# some other things (append some items to the list)
mylistbox.SetSelection(5)
I also have :
def OnEventListBox(self, event):
print 'hello'
# plus lots of other things
How to make that the command mylistbox.SetSelection(5) in the initialization is immediately followed by the call of OnEventListBox?
Remark : It seems that SetSelection() doesn't generate a wx.EVT_LISTBOX automatically.
From the documentation:
Note that [SetSelection] does not cause any command events to be emitted...
This is on purpose, so that the events don't all trigger while you're trying to set up the UI. You could just manually call OnEventListBox for the desired functionality.
Better yet, if you don't need the event for what you're doing on init, you could extract the initialisation into a separate function, then call that on init and in OnEventListBox.

Using wxPython's FloatCanvas widget, is there a way to get the ControlDown information of the event?

I am wondering if I can get the keyboard modifiers' status on EVT_FC_LEFT_UP events fired from a FloatCanvas.
I would prefer to do it without writing callbacks myself for keyUp and keyDown. I can't see a better method of grabbing the control status, than having a class member keep track of the modifier states over the whole window.
Is it possible to grab the state of the control key inside of the EVT_FC_LEFT_UP callback?
there is no isKeyDown type method in wxPython (afaik)
the only way I can see you getting this is
control_pressed = False
.....
self.float_canvas.bind(wx.EVT_KEY_DOWN,OnKeyDown)
self.float_canvas.bind(wx.EVT_KEY_DOWN,OnKeyUp)
.....
def OnKeyDown(evt):
global control_pressed
if evt.GetKeyCode() == 117 #(or whatever the code for ctrl is)
control_pressed = True
def OnKeyUp(evt):
global control_pressed
if evt.GetKeyCode() == 117 #(or whatever the code for ctrl is)
control_pressed = False
And then check control_pressed in your wx.EVT_LEFT_UP event handler
also in real life(tm) I suspect you would want this all in a class not globals

Categories

Resources