Search code examples
user-interfaceeventsoptimizationevent-handlingwxpython

wxPython -- easier way to do custom events?


In my project, I am using wxPython and have a number of custom events that have to be caught, usually when a variable changes. So, here's how I have it set up:

- Event happens (inside a function), it sends a louie signal
- Event Handler catches all event louie signals and performs a func call
- Function call posts the event
- Binding from widget catches it
- Widget sends it to a function in the Event Handler for handling

That seems like an awful lot of runaround. Shouldn't there be a way to simply catch a variable change in a binding and send it to a function? My senses are telling me I'm going about this all wrong. Is there an easier way?

EDIT 1: Here is the minimal, runnable code for my setup, the louie dispatches are because of the threading:

import time
import threading
import wx
from wx.lib.newevent import NewEvent
from louie import dispatcher

customEvent, EVT_CUSTOM_TEST = NewEvent()

def eventFire(changeTo):
    global customEvent
    changerContainer = customEvent(changeTo=changeTo)
    wx.PostEvent(text, changerContainer)

def change(event):
    text.SetLabel(event.changeTo)

class customThread(threading.Thread):

    def __init__(self, group=None, target=None, name=None, verbose=None, **kwargs):
        super(customThread, self).__init__(group=group, target=target, name=name, verbose=verbose)
        self.daemon = True

    def run(self):
        for i in range(10):
            if i == 5:
                dispatcher.send("CHANGE ME", changeTo="Changed Text")
            time.sleep(1)

dispatcher.connect(eventFire, "CHANGE ME")

app = wx.App()
frame = wx.Frame(None, wx.ID_ANY, title="test", size=(100,100))
panel = wx.Panel(frame)
text = wx.StaticText(panel, wx.ID_ANY, "Tester")
frame.Center()
frame.Show()
text.Bind(EVT_CUSTOM_TEST, change)
newThread = customThread()
newThread.start()
app.MainLoop()

EDIT 2: Using wxPython 2.8.12.1 and Ubuntu 14.04


Solution

  • You can use threads in wxPython and just call a thread-safe method instead of using dispatcher. A thread-safe method is wx.PostEvent or wx.CallAfter. I like using pubsub to pass the data around. So instead of

    dispatcher.send("CHANGE ME", changeTo="Changed Text")
    

    I would just do something like

    wx.CallAfter(Publisher().sendMessage, "msg_name", "Changed Text")
    

    Then the method that pubsub is connected to will fire. So you can make a common method

    def change(text):
        text.SetLabel(text)
    

    And call that via pubsub. Here are some links:

    Note that that article uses wxPython 2.8 and the pubsub API changed in 2.9+. So you might want to look at this too: