MacOS下wxPython RadioBox更新TextCtrl时触发PyGILState_Release致命错误
PyGILState_Release致命错误:MacOS下wxRadioBox事件中修改wxTextCtrl的解决方法
问题现象
开发了包含wxTextCtrl和wxRadioBox的wxPython对话框,在RadioBox的事件处理函数中修改TextCtrl内容时,首次点击单选按钮正常,二次点击触发致命错误:
Fatal Python error: PyGILState_Release: thread state 0x7facc7d078d0 must be current when releasing
注释WriteText调用后问题消失,该问题仅在MacOS 11.7.4上出现,Linux环境无此问题。
涉及环境
- 操作系统:MacOS 11.7.4
- Python版本:2.7/3.7/3.9
- wxPython版本:4.1.1/4.2.0
解决方案
这个错误源于MacOS平台上wxPython的UI线程与GIL(全局解释器锁)管理的兼容性冲突,直接在RadioBox事件处理中同步修改TextCtrl会触发线程状态异常。可以通过以下两种方式解决:
方案1:使用wx.CallAfter延迟UI操作
将TextCtrl的修改操作放到主UI线程的事件队列中执行,避免在事件处理函数中直接同步操作:
def on_radio_box(self, event): def update_text(): self.data_weight.Clear() if event.Int == 0: self.data_weight.WriteText("A") elif event.Int == 1: self.data_weight.WriteText("B") else: self.data_weight.WriteText("C") wx.CallAfter(update_text)
方案2:替换Clear()+WriteText为SetValue
SetValue是原子性的UI操作,比分开调用Clear和WriteText更安全,能避免线程状态冲突:
def on_radio_box(self, event): if event.Int == 0: self.data_weight.SetValue("A") elif event.Int == 1: self.data_weight.SetValue("B") else: self.data_weight.SetValue("C")
修改后的完整代码(方案2示例)
import wx class MySplitDialog(wx.Dialog): def __init__(self, parent, iid, title): wx.Dialog.__init__(self, parent, iid, title) base_vsizer = wx.BoxSizer(wx.VERTICAL) base_hsizer = wx.BoxSizer(wx.HORIZONTAL) self.label1 = wx.StaticText(self, label="Change this:") base_hsizer.Add(self.label1, 0, wx.ALIGN_CENTER | wx.ALL, 5) self.data_weight = wx.TextCtrl(self, value="1000", size=[80, -1], style=wx.TE_READONLY | wx.TE_RIGHT) base_hsizer.Add(self.data_weight, 0, wx.ALIGN_CENTER | wx.ALL, 5) base_vsizer.Add(base_hsizer, 0, wx.ALIGN_CENTER | wx.ALL, 5) # Add a Radio Box self.radio_ctl = wx.RadioBox(self, label="Split on basis of", choices=["A", "B", "C"], majorDimension=1, style=wx.RA_SPECIFY_COLS) self.radio_ctl.SetSelection(1) base_vsizer.Add(self.radio_ctl, 0, wx.GROW | wx.ALL, 5) self.radio_ctl.Bind(wx.EVT_RADIOBOX, self.on_radio_box) self.SetSizer(base_vsizer) def on_radio_box(self, event): if event.Int == 0: self.data_weight.SetValue("A") elif event.Int == 1: self.data_weight.SetValue("B") else: self.data_weight.SetValue("C") app = wx.App(False) MySplitDialog(None, 0, "Split").ShowModal() app.MainLoop()
内容的提问来源于stack exchange,提问作者Dion Kant
相关产品推荐
相关产品推荐

