使用线程控制 wxPython 中的 UI 元素 [英] controlling UI elements in wxPython using threading

查看:28
本文介绍了使用线程控制 wxPython 中的 UI 元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Python/wxWidgets 编写一个简单的游戏.

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.

到目前为止,我的架构是应用程序启动并运行第二个线程,该线程运行一个名为gameLogic"的函数.然后主线程进入主应用循环.

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.

gameLogic 线程顺序运行,需要控制 UI.例如,它需要打开一个以主框架为父级的新对话框.但是,我发现这样做最终会导致崩溃(严重到在 OS X 中弹出报告崩溃窗口).

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).

我一直环顾四周并收集到我需要重构才能使用事件,但我不确定如何创建自己的事件.我可以让我的 gameLogic 线程在主窗口中引发一个事件,然后该事件将调出输入对话框并等待输入(模态),然后将该数据返回给 gameLogic 线程.因为UI线程是独立的,所以gameLogic线程在等待时可能会阻塞.

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.

我已经看到了实现这一点的各种想法,但还没有找到一个很好的例子来说明如何在我的 wxFrame 对象中创建自定义事件并从另一个线程调用它,并且还有逻辑线程阻塞并等待输入返回,然后如何将该输入返回给 gameThread.

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.

非常感谢您的建议!

推荐答案

首先,难的部分:

我已经看到了实现这个的各种想法,但还没有找到一个很好的例子来说明如何在我的 wxFrame 对象中创建自定义事件并从另一个线程调用它

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

wxPyWiki 上的一个例子.您可能还想查看 Working在单独的线程中使用 wxPython.

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.

但是,我认为博客文章 wxPython 和线程 来自 The Mouse Vs.Python 最好地解释了困难的部分.它还向您展示了执行此操作的更简单方法(使用 CallAfterPublisher 而不是发布自定义事件),但让我们坚持您所询问的方式.

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.

它唯一缺少的部分是:

...还有逻辑线程阻塞并等待输入返回,然后如何将该输入返回到gameThread.

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

但这并没有什么wx-spceific.将信息发送到 wx(或任何事件循环)很难的唯一原因是事件循环不能阻塞.您的逻辑线程可以阻塞,实际上应该.所以,任何普通的线程同步机制都可以.

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.

因此,您有一个线程想要永远阻塞直到一个值准备好,另一个线程想要能够在不阻塞的情况下发送该值.您可以使用 ConditionQueue 轻松完成此操作.后者在这里可能有点矫枉过正,但它更灵活,所以让我们这样做,只是为了好玩.

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.

我将采用 Mouse 博客中的示例,并在每次后台线程发布 EVT_RESULT 事件时进行阻塞,直到该事件被处理,并返回一个字符串可以……记录什么的,我想,它不是很有用,但我想展示它传递东西.

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)

在这个例子中,GUI 线程在 updateDisplay 的末尾执行 self.q.put(text).没有什么神奇的理由它必须在那里然后 - 只要它最终发生(并且恰好是 1 次),逻辑线程就会阻塞直到它发生.例如,updateDisplay 方法可以创建一个新按钮,并在用户单击它时发送 self.q.put(并删除按钮).

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.

这篇关于使用线程控制 wxPython 中的 UI 元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆