【问题标题】:controlling UI elements in wxPython using threading使用线程控制 wxPython 中的 UI 元素
【发布时间】:2013-05-09 00:40:31
【问题描述】:

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

我为主窗口编写了一个类,并为另一个获取用户输入的框架编写了一个简单的类。

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

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

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

在我的主框架中的一个函数(事件处理程序)中,我可以创建一个输入对话框的新实例,以模态方式显示它,然后获取输入。

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

非常感谢您的建议!

【问题讨论】:

  • This blog post 展示了如何创建和发送自定义事件(以及如何使用CallAfterPublisher,这对于简单的情况通常更简单)。至于有逻辑线程阻塞……如果只想阻塞,使用普通线程同步;你不需要任何花哨的东西。

标签: python wxpython wxwidgets


【解决方案1】:

首先,困难的部分:

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

an example of this right on the wxPyWiki。您可能还想查看Working with wxPython in a separate thread 的链接。

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

唯一缺少的是:

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

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

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

我将从 Mouse 博客中获取示例,并且每次后台线程发布 EVT_RESULT 事件时,它都会阻塞,直到该事件被处理,并返回一个它可以......记录或其他东西的字符串,我想,它不是很有用,但我想通过 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(并删除该按钮)。

【讨论】:

  • 这成功了!我终于能够创建一个自定义事件并使用 block=True 的队列来阻止等待返回,然后将所有这些包装在一个简单的函数中。完美运行!
猜你喜欢
  • 2013-07-26
  • 1970-01-01
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多