2

I'm writing a simple game using Python/wxWidgets.

I've written a class for the main window and a simple class for another frame that gets user input.

The architecture I have so far is that the app starts up and runs a second thread that runs a function called "gameLogic". The main thread then goes into main app loop.

The gameLogic thread runs sequentially and needs to control the UI. For example, it needs to open a new dialog with the main frame as its parent. However, I've found that doing this ends up causing crashes (bad enough to pop up the report crash window in OS X).

I've been looking around and gathering that I need to refactor to use events, but what I'm not sure of how to do is to create my own events. I could have my gameLogic thread raise an event in the main window that will then bring up the input dialog and wait for input (modally) then return that data back to the gameLogic thread. The gameLogic thread can block while waiting because the UI thread is separate.

In a function (event handler) in my main frame, I can create a new instance of an input dialog, show it modally, and then get the input.

I've seen all sorts of ideas for implementing this but haven't been able to find a good example of how to create the custom event in my wxFrame object and call it from the other thread, and also to have the logic thread block and wait till input comes back, and then how to get that input back to gameThread.

Advice greatly appreciated!

1
  • This blog post shows how to create and send custom events (and also how to use CallAfter and Publisher, which is often simpler for simple cases). As for having the logic thread block… if you just want to block, use normal thread synchronization; you don't need anything fancy. Commented May 9, 2013 at 0:49

1 Answer 1

1

First, the hard part:

I've seen all sorts of ideas for implementing this but haven't been able to find a good example of how to create the custom event in my wxFrame object and call it from the other thread

There's an example of this right on the wxPyWiki. You might also want to look at the link to Working with wxPython in a separate thread.

However, I think the blog post wxPython and Threads from The Mouse Vs. The Python explains the hard part best. And it also shows you the easier way to do this (using CallAfter and Publisher instead of posting custom events), but let's stick with the way you asked about.

The only piece it's missing is:

… and also to have the logic thread block and wait till input comes back, and then how to get that input back to gameThread.

But there's nothing wx-spceific about that. The only reason sending information into wx (or any event loop) is hard is that the event loop can't block. Your logic thread can block, and in fact should. So, any normal thread synchronization mechanism is just fine.

So, you've got one thread that wants to block forever until a value is ready, and another thread that wants to be able to send that value without blocking. You can do this pretty easily with a Condition, or a Queue. The latter is probably overkill here, but it's more flexible, so let's do that, just for fun.

I'll take the example from the Mouse blog, and make it so each time the background thread posts an EVT_RESULT event, it then blocks until that event is handled, getting back a string that it can… log or something, I guess, it's not very useful, but I wanted to show it passing something.

from queue import Queue
# ...

class TestThread(Thread):
# ...
    def run(self):
        for i in range(6):
            # ...
            wx.PostEvent(self.wxObject, ResultEvent(amtOfTime)
            result_from_gui = self.wxObject.q.get(True, None)
# ...

class MyForm(wx.Frame):
# ...
    def __init__(self):
        # ...
        self.q = Queue()
    # ...
    def updateDisplay(self, msg):
        # ...
        t = msg.data
        if isinstance(t, int):
            text = "Time since thread started: %s seconds" % t
        else:
            text = "%s" % t
            self.btn.Enable()
        self.displayLbl.SetLabel(text)
        self.q.put(text)

In this example, the GUI thread does self.q.put(text) at the end of updateDisplay. There's no magic reason it has to be there and then—as long as it eventually happens (and exactly 1 time), the logic thread will block until it does. For example, the updateDisplay method could create a new button, and send the self.q.put (and remove the button) when the user clicks it.

Sign up to request clarification or add additional context in comments.

1 Comment

This worked! I was finally able to create a custom event and use a queue with block=True to block waiting for return, then wrapped all that up in a simple function. Works perfect!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.