首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用cmd2和wxPython创建非阻塞的图形用户界面?

如何使用cmd2和wxPython创建非阻塞的图形用户界面?
EN

Stack Overflow用户
提问于 2018-05-02 22:44:09
回答 1查看 814关注 0票数 2

如何通过一个wxPython解释器命令创建非阻塞的cmd2 GUI窗口,即使解释器和wx.App都必须在主线程上运行?

我正在使用cmd2模块在Python3.x中创建一个命令行解释器。在我的解释器中有一个命令将允许我创建一个“非阻塞”的wxPython图形用户界面窗口,其中有一个倒计时器,运行在一个单独的线程中。它需要与主解释器线程分开运行,否则它将防止在计时器运行时输入其他命令。

当我尝试使用wxTimer对象允许我的图形用户界面更新它的进度条并检查剩余时间时,我得到以下错误:

代码语言:javascript
复制
wx._core.wxAssertionError: C++ assertion "wxThread::IsMain()" failed at ..\..\src\common\timerimpl.cpp(60) in wxTimerImpl::Start(): timer can only be started from the main thread

任何启动计时器的尝试都会破坏代码。例如,以下简单代码无法工作:

代码语言:javascript
复制
import wx
import threading
import cmd2

class TimerFrame(wx.Frame):
    def __init__(self, time):
        super().__init__(None, title=str(time), size=(300, 300))
        self.InitUI()

    def InitUI(self):
        self.timer = wx.Timer(self, 1)
        self.timer.Start(100)
        self.Bind(wx.EVT_TIMER, self.OnTimer, id=1)
        self.Show()

    def OnTimer(self, event):
        print("Updating")

class Interpreter(cmd2.Cmd):
    def __init__(self):
        super().__init__()
        self.app = wx.App()

    def do_timer(self, _):
        threading.Thread(target=self.createTimer, args=(self.app,)).start()

    def createTimer(self, app):
        TimerFrame("Window Title")
        app.MainLoop()

if __name__ == "__main__":
    Interpreter().cmdloop()

注意,注释掉包含timer.Start(100)的行允许成功地在单独的线程中创建窗口,但它们缺乏必要的Timer功能。

我尝试过的其他不起作用的事情:

  • 在新线程中创建wx.App而不是传递它(结果是相同的错误,但是对于MainLoop而不是Timer)
  • 在主线程中创建wx.App并在那里运行其MainLoop (阻止命令行接受进一步的命令)
  • 在单独的线程中运行解释器命令循环(抱怨cmdloop必须从主线程运行)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-02 23:00:07

文档中有一个关于cmd2与事件循环的集成的部分。

许多Python并发库涉及或需要它们控制的事件循环,如asynciogeventTwisted等。

虽然这是专门讨论以网络为中心的事件循环,但实际上,像wx这样的GUI事件循环也存在同样的问题。

tl;dr不是调用cmdloop(),而是阻塞整个主线程直到解释器退出,只需调用preloop(),然后通过wx回调反复调用onecmd() (或onecmd_plus_hooks()runcmd_plus_hooks() )。

换句话说,您可以从cmd2事件循环驱动wx事件循环,这样wx循环就可以接管线程。

wx还有一种手动驱动事件循环的方法。请参见wx.EventLoopBase和相关类。(可能有一些很好的示例代码,类似于cmd2文档中的代码,但是您必须搜索它。)基本相同的想法是:不再运行wx循环并永远阻塞线程,而是手动创建一个wx.EventLoopwx.EventLoopActivator,然后从cmd2事件循环反复调用while loop.Pending(): loop.Dispatch() (可能还有app.ProcessIdle())。

换句话说,您可以从wx事件循环驱动cmd2事件循环,这样cmd2循环就可以接管线程。

如果这两种方法都不适合您,那么您可能可以使用multiprocessing而不是threading。这样,两个事件循环都在主线程中运行,但其中一个只是在子进程的主线程中运行。

这可能是GUI应用程序的一个问题,因此将wx放在子进程中可能不起作用(或者更糟糕的是,可能在某些平台/设置上工作,而在其他平台/设置上不起作用,或者可能工作,但是经常发生…神秘的失败)。,但是cmd2大概只需要看到stdin/stdout/tty,所以它可能会工作。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50144594

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档