重置 wxPython 应用程序/销毁所有内容并从初始框架开始

Resetting a wxPython app / destroying everything and starting from initial frame

我在这里阅读了其他答案,但它们似乎都涉及完全关闭应用程序,确保所有进程和框架等都在此过程中被销毁。我想做的有点不同。

我的应用程序包含三个框架(StartFrame、ParaFrame、ResultFrame)以及一个用于存储和操作数据的自定义 class。基础知识是: StartFrame 只是一些文本和一个“开始”按钮。 “开始”创建一个 ParaFrame 实例,隐藏 StartFrame,并显示 ParaFrame 实例。 Paraframe 有一堆小部件供用户 select 文件以及如何分析它。输入所有字段后,弹出“分析”按钮,(单击时)实例化自定义 DataHandler class,根据用户 selections 设置其参数,调用 DataHandler 方法分析数据,隐藏自身,并实例化+显示 ResultsFrame。毫不奇怪,ResultsFrame 显示了分析结果。

我想添加一个“重新开始”控件,它会破坏所有内容(所有框架、面板、DataHandler 实例等)并显示新的 StartFrame 实例,否则会破坏所有内容 除了 已实例化但隐藏的 StartFrame,再次显示该帧,但我不知所措。

简化示例如下:

import wx

class StartFrame(wx.Frame):
    """App start frame"""
    FRAME_MIN_SIZE = (900,600)
    def __init__(self, parent):
        wx.Frame.__init__(self, parent=parent,
         id=wx.ID_ANY, title="LOD Calculator", size=wx.Size(900,600),
         style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.RESIZE_BORDER|wx.TAB_TRAVERSAL)

        self.startpnl = wx.Panel(self)

        self.startvsizer=wx.BoxSizer(wx.VERTICAL)
        self.startbtnsizer=wx.BoxSizer(wx.HORIZONTAL)

        self.btn = wx.Button(self.startpnl, wx.ID_ANY, "Start Analysis",\
         size = (200,60))
        self.btn.Bind(wx.EVT_BUTTON, self._OnStart)
        self.startbtnsizer.AddStretchSpacer()
        self.startbtnsizer.Add(self.btn, 0, wx.CENTER)
        self.startbtnsizer.AddStretchSpacer()
        self.startvsizer.AddStretchSpacer()
        self.startvsizer.Add(self.startbtnsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
        self.startvsizer.AddStretchSpacer()
        self.startpnl.SetSizerAndFit(self.startvsizer)

    def _OnStart(self,event):
        self.frm2 = ParaFrame(None)
        self.Hide()
        self.frm2.Show()

class ParaFrame(wx.Frame):
    """Data load frame"""
    FRAME_MIN_SIZE = (950,800)
    def __init__(self, parent):
        wx.Frame.__init__(self, parent=parent,
         id=wx.ID_ANY, title="LOD Calculator", size=wx.Size(950,800),
         style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.RESIZE_BORDER|wx.TAB_TRAVERSAL)

        self.mainpnl = wx.Panel(self)
        self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.vsizer = wx.BoxSizer(wx.VERTICAL)

        self.analyzebtn = wx.Button(self.mainpnl, wx.ID_ANY, u"Analyze",\
         wx.DefaultPosition, wx.DefaultSize, 0)
        self.vsizer.AddStretchSpacer()
        self.vsizer.Add(self.analyzebtn, 0, wx.ALL, 10)
        self.vsizer.AddStretchSpacer()
        self.mainsizer.AddStretchSpacer()
        self.mainsizer.Add(self.vsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
        self.mainsizer.AddStretchSpacer()
        self.mainpnl.SetSizerAndFit(self.mainsizer)

        self.analyzebtn.Bind(wx.EVT_BUTTON, self._analyze)

    def _analyze(self, event):
        dhandler = DataHandler(self)
        dhandler.data = "a bunch"
        dhandler.data2 = " of data"
        dhandler.data3 = " goes here"
        dhandler._analyzeData()
        self.resfrm = ResultFrame(self, dhandler.data, dhandler.data2)
        self.Hide()
        self.resfrm.Show()

class DataHandler:
    def __init__(self,parent):
        self.data = ''
        self.data2 = ''
        self.data3 = ''

    def _analyzeData(self):
        anastr = self.data + self.data2 + " gets analyzed"
        print(anastr)

class ResultFrame(wx.Frame):
    def __init__(self, parent, targets, dftables):
        super(ResultFrame, self).__init__(parent, title="results", size=(1535,935))
        self.targets = targets
        self.dftables = dftables
        self.InitUI()
        self.Layout()

    def InitUI(self):
        self.mainpnl = wx.Panel(self)
        self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.vsizer = wx.BoxSizer(wx.VERTICAL)

        self.restartbtn = wx.Button(self.mainpnl, wx.ID_ANY, u"Start Over",\
         wx.DefaultPosition, wx.DefaultSize, 0)
        self.vsizer.AddStretchSpacer()
        self.vsizer.Add(self.restartbtn, 0, wx.ALL, 10)
        self.vsizer.AddStretchSpacer()
        self.mainsizer.AddStretchSpacer()
        self.mainsizer.Add(self.vsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
        self.mainsizer.AddStretchSpacer()
        self.mainpnl.SetSizerAndFit(self.mainsizer)

        self.restartbtn.Bind(wx.EVT_BUTTON, self.OnRestart)

### this is where I try to destroy the ParaFrame instance and everything spawned by it, unsuccessfully
    def OnRestart(self, event):
        frm.frm2.Destroy()
        frm.Show()

def main():
    import wx.lib.mixins.inspection
    app = wx.App()
    frm = StartFrame(None)
    frm.Show()
    wx.lib.inspection.InspectionTool().Show(refreshTree=True)
    app.MainLoop()

if __name__ == "__main__":
    main()

如您所见,在 ResultFrame 中我有一个“重新开始”按钮绑定到一个方法,在该方法中我尝试使用 [= 销毁 ParaFrame 实例(以及由此产生的 DataHandler 和 ResultFrame 实例) 12=],却报错:

Traceback (most recent call last):
  File "C:\Python\Scripts\stackexchange code.py", line 102, in OnRestart
    frm.frm2.Destroy()
NameError: name 'frm' is not defined

如果我将行更改为 frm2.Destroy(),我会得到同样的错误,但会显示 name 'frm2' is not defined.

我在这里做错了什么,我怎样才能实现我的目标?我对 OOP 还是很陌生,对 wxPython 也很陌生,所以我很感激任何帮助。我想要的只是让该按钮销毁所有内容并再次显示 the/an 初始 StartFrame。谢谢:)

只要您创建的每个 class 都没有父级,您就可以在创建链中的下一帧后使用 self.Destroy(),而不会破坏任何 children。 (将所需的任何数据作为父参数以外的参数传递)

所以你的代码看起来像这样:

import wx

class StartFrame(wx.Frame):
    """App start frame"""
    FRAME_MIN_SIZE = (900,600)
    def __init__(self, parent):
        wx.Frame.__init__(self, parent=parent,
         id=wx.ID_ANY, title="Load Calculator", size=wx.Size(900,600))

        self.startpnl = wx.Panel(self)

        self.startvsizer=wx.BoxSizer(wx.VERTICAL)
        self.startbtnsizer=wx.BoxSizer(wx.HORIZONTAL)

        self.btn = wx.Button(self.startpnl, wx.ID_ANY, "Start Analysis",\
         size = (200,60))
        self.btn.Bind(wx.EVT_BUTTON, self._OnStart)
        self.startbtnsizer.AddStretchSpacer()
        self.startbtnsizer.Add(self.btn, 0, wx.CENTER)
        self.startbtnsizer.AddStretchSpacer()
        self.startvsizer.AddStretchSpacer()
        self.startvsizer.Add(self.startbtnsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
        self.startvsizer.AddStretchSpacer()
        self.startpnl.SetSizerAndFit(self.startvsizer)

    def _OnStart(self,event):
        frm2 = ParaFrame(None)
        frm2.Show()
        self.Destroy()

class ParaFrame(wx.Frame):
    """Data load frame"""
    FRAME_MIN_SIZE = (950,800)
    def __init__(self, parent):
        wx.Frame.__init__(self, parent=parent,
         id=wx.ID_ANY, title="Load Calculator", size=wx.Size(900,600))

        self.mainpnl = wx.Panel(self)
        self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.vsizer = wx.BoxSizer(wx.VERTICAL)

        self.analyzebtn = wx.Button(self.mainpnl, wx.ID_ANY, u"Analyze",\
         wx.DefaultPosition, wx.DefaultSize, 0)
        self.vsizer.AddStretchSpacer()
        self.vsizer.Add(self.analyzebtn, 0, wx.ALL, 10)
        self.vsizer.AddStretchSpacer()
        self.mainsizer.AddStretchSpacer()
        self.mainsizer.Add(self.vsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
        self.mainsizer.AddStretchSpacer()
        self.mainpnl.SetSizerAndFit(self.mainsizer)

        self.analyzebtn.Bind(wx.EVT_BUTTON, self._analyze)

    def _analyze(self, event):
        dhandler = DataHandler(self)
        dhandler.data = "a bunch"
        dhandler.data2 = " of data"
        dhandler.data3 = " goes here"
        dhandler._analyzeData()
        resfrm = ResultFrame(None, dhandler.data, dhandler.data2)
        resfrm.Show()
        self.Destroy()

class DataHandler:
    def __init__(self,parent):
        self.data = ''
        self.data2 = ''
        self.data3 = ''

    def _analyzeData(self):
        anastr = self.data + self.data2 + " gets analyzed"
        print(anastr)

class ResultFrame(wx.Frame):
    def __init__(self, parent, targets, dftables):
        super(ResultFrame, self).__init__(parent, title="Results", size=(900,600))
        self.targets = targets
        self.dftables = dftables
        self.InitUI()
        self.Layout()

    def InitUI(self):
        self.mainpnl = wx.Panel(self)
        self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.vsizer = wx.BoxSizer(wx.VERTICAL)

        self.restartbtn = wx.Button(self.mainpnl, wx.ID_ANY, u"Start Over",\
         wx.DefaultPosition, wx.DefaultSize, 0)
        self.vsizer.AddStretchSpacer()
        self.vsizer.Add(self.restartbtn, 0, wx.ALL, 10)
        self.vsizer.AddStretchSpacer()
        self.mainsizer.AddStretchSpacer()
        self.mainsizer.Add(self.vsizer, wx.SizerFlags().Expand().Border(wx.ALL, 25))
        self.mainsizer.AddStretchSpacer()
        self.mainpnl.SetSizerAndFit(self.mainsizer)

        self.restartbtn.Bind(wx.EVT_BUTTON, self.OnRestart)

### this is where I try to destroy the ParaFrame instance and everything spawned by it, unsuccessfully
    def OnRestart(self, event):
        frm = StartFrame(None)
        frm.Show()
        self.Destroy()

def main():
    app = wx.App()
    frm = StartFrame(None)
    frm.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()