且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用线程控制 wxPython 中的 UI 元素

更新时间:2023-10-26 20:14:04

首先,难的部分:

我已经看到了实现这个的各种想法,但还没有找到一个很好的例子来说明如何在我的 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.