首页 > 解决方案 > 线程中的 wxPython 无法正常关闭

问题描述

我是 wxPython 的新手,所以我基本上只是复制了一些会显示托盘图标并使其成为线程的内容:

import wx
import wx.adv
from threading import Thread

TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'icon.png'


class Main(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        self.app = App()
        self.app.MainLoop()


def create_menu_item(menu, label, func):
    item = wx.MenuItem(menu, -1, label)
    menu.Bind(wx.EVT_MENU, func, id=item.GetId())
    menu.Append(item)
    return item


class TaskBarIcon(wx.adv.TaskBarIcon):
    def __init__(self, frame):
        self.frame = frame
        super(TaskBarIcon, self).__init__()
        self.set_icon(TRAY_ICON)
        self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)

    def CreatePopupMenu(self):
        menu = wx.Menu()
        create_menu_item(menu, 'Exit', self.on_exit)
        return menu

    def set_icon(self, path):
        icon = wx.Icon(wx.Bitmap(path))
        self.SetIcon(icon, TRAY_TOOLTIP)

    def on_left_down(self, event):
        print('Tray icon was left-clicked.')

    def on_exit(self, event):
        wx.CallAfter(self.Destroy)
        self.frame.Close()


class App(wx.App):
    def __init__(self):
        wx.App.__init__(self, False)

    def OnInit(self):
        frame = wx.Frame(None)
        self.SetTopWindow(frame)
        TaskBarIcon(frame)
        return True

从我的主线程,这是一个运行时间很长的服务,我正在启动 GUI 线程,激活调度程序并检查它是否应该像这样运行:

gui = trayIcon.Main()
gui.start()
schedule.every(60).minutes.do(main)

while gui.is_alive():
    schedule.run_pending()
    time.sleep(1)

# if this is reached the gui thread has terminated and the program should shut down
sys.exit()

它应该是这样工作的:当单击托盘图标菜单中的退出项时,GUI 线程关闭,然后在主线程的 while 循环中不再检测到并调用 sys.exit()。不幸的是,wxPython 然后显示一个错误对话框,其中包含以下文本:


wxWidgets 调试警报

....\src\common\socket.cpp(767): assert "wxIsMainThread()" failed in wxSocketBase::IsInitialized(): unsafe to call from other threads [in thread 1284] 你想停止程序吗?您也可以选择 [取消] 来抑制进一步的警告。

如何正确关闭 GUI 或至少抑制此警告?之后程序按原样退出,尽管我怀疑抑制消息会留下某种内存泄漏。

提前致谢

紫杉醇

标签: pythonmultithreadingwxpython

解决方案


基本上看起来你已经把它倒过来了。主线程应该始终是 wxPython 线程。MainLoop 不应在线程内调用。相反,您应该让 wxPython 控制并在其中运行您长时间运行的线程。

然后当你长时间运行的任务完成后,你可以使用线程安全的方法,比如wx.CallAfter告诉 wxPython 是时候退出了。由于 wxPython 是可控的,它可以正确地关闭自己。

以下是一些可能对您有所帮助的文章:


推荐阅读