SetValue 上的 wxpython TextCtrl 分段错误

wxpython TextCtrl segmentation fault on SetValue

我正在使用 wxglade 构建一个小应用程序。

它基本上只是一个 TextCtrl 多行和只读。 我启动一个读取文件并将内容显示到 TextCtrl 中的线程。 经过一系列执行后,我得到了一个分段错误。

Debian 9 Python 3.5.3 wxpython 4.0.7.post2

有什么建议吗? 非常感谢!

代码

import wx, threading, pdb
import gettext

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((400, 300))
        self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY)
        self.__set_properties()
        self.__do_layout()
        self.update_text()

    def update_text(self):
        print("update_text")
        text = ""
        try:
            f = open("test.txt", "r")
            text = f.read()
            f.close()
        except:
            print(_('Error opening test.txt file!'))
            text = _('Error opening test.txt file!')

        self.text_ctrl_1.SetValue(text)
        threading.Timer(1, self.update_text).start()

    def __set_properties(self):
        self.SetTitle(_("frame"))

    def __do_layout(self):
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(self.text_ctrl_1, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_1)
        self.Layout()

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

if __name__ == "__main__":
    gettext.install("app") # replace with the appropriate catalog name

    app = MyApp(0)
    app.MainLoop()

错误

update_text update_text update_text update_text

(wxglade_out.py:5127): Gtk-WARNING **: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created. You must use marks, character numbers, or line numbers to preserve a position across buffer modifications. You can apply tags and insert marks without invalidating your iterators, but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset) will invalidate all outstanding iterators

(wxglade_out.py:5127): Gtk-WARNING **: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created. You must use marks, character numbers, or line numbers to preserve a position across buffer modifications. You can apply tags and insert marks without invalidating your iterators, but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset) will invalidate all outstanding iterators Segmentation fault

我认为从线程内更新不会正常工作。
应使用主 GUI 循环执行更新。
我不知道这是否与 gtk 相关:Gtk +3 TextView application crashes

您可以尝试通过 pubsub 更新,您自己的一些设备使用变量或使用 wx.Timer 而不是线程,即

import wx
import time

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((400, 300))
        self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY)
        self.__set_properties()
        self.__do_layout()
        #self.update_text(None)
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update_text, self.timer)
        self.timer.Start(5000)

    def update_text(self, event):
        print("update_text")
        self.text_ctrl_1.Clear()
        wx.GetApp().Yield() # to see the clear occur release momentarily to the main loop
        try:
            with open("test.txt", "r") as f:
               text = f.read()
        except:
            print(_('Error opening test.txt file!'))
            text = _('Error opening test.txt file!')
        time.sleep(2)
        self.text_ctrl_1.SetValue(text)

    def __set_properties(self):
        self.SetTitle("My frame")

    def __do_layout(self):
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(self.text_ctrl_1, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_1)
        self.Layout()

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

if __name__ == "__main__":
    app = MyApp()
    app.MainLoop()