The other day, I came across an interesting StackOverflow question where the fellow was trying to figure out how to open a sub-frame only once. Basically he wanted a single instance of the sub-frame (and other sub-frames). After digging around a bit on Google, I found an old thread from the wxPython Google Group that had an interesting approach to doing what was needed.
Basically it required a bit of meta-programming, but it was a fun little exercise that I thought my readers would find interesting. Here's the code:
import wx
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
########################################################################
class SingleInstanceFrame(wx.Frame):
""""""
instance = None
init = 0
#----------------------------------------------------------------------
def __new__(self, *args, **kwargs):
""""""
if self.instance is None:
self.instance = wx.Frame.__new__(self)
elif isinstance(self.instance, wx._core._wxPyDeadObject):
self.instance = wx.Frame.__new__(self)
return self.instance
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
print id(self)
if self.init:
return
self.init = 1
wx.Frame.__init__(self, None, title="Single Instance Frame")
panel = MyPanel(self)
self.Show()
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Main Frame")
panel = MyPanel(self)
btn = wx.Button(panel, label="Open Frame")
btn.Bind(wx.EVT_BUTTON, self.open_frame)
self.Show()
#----------------------------------------------------------------------
def open_frame(self, event):
frame = SingleInstanceFrame()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
The meat of this code is in the SingleInstanceFrame class, specifically in the __new__ method. Here we check to see if the variable self.instance is set to None. If so, we create a new instance. We will also create a new instance if the user closes the frame, which will cause it to become a wxPyDeadObject. This is what the second part of the if statement is for. It checks to see if the instance has been deleted and if it has, it creates a new instance.
You will also notice that we have a variable called self.init. This is used to check if the instance has already been initialized. If so, __init__ will just return instead of re-instantiating everything.
In wxPython 4 / Phoenix, there is no wx._core._wxPyDeadObject, so we have to modify our code a bit to make it work in the newer versions of wxPython. Here's how:
import wx
class MyPanel(wx.Panel):
""""""
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
class SingleInstanceFrame(wx.Frame):
""""""
instance = None
init = 0
def __new__(self, *args, **kwargs):
""""""
if self.instance is None:
self.instance = wx.Frame.__new__(self)
elif not self.instance:
self.instance = wx.Frame.__new__(self)
return self.instance
def __init__(self):
"""Constructor"""
print(id(self))
if self.init:
return
self.init = 1
wx.Frame.__init__(self, None, title="Single Instance Frame")
panel = MyPanel(self)
self.Show()
class MainFrame(wx.Frame):
""""""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Main Frame")
panel = MyPanel(self)
btn = wx.Button(panel, label="Open Frame")
btn.Bind(wx.EVT_BUTTON, self.open_frame)
self.Show()
def open_frame(self, event):
frame = SingleInstanceFrame()
if __name__ == '__main__':
print wx.version()
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
You will note that the only difference is found in the __new__ method where we changed the conditional statements slightly.
I hope you've found this tutorial useful. Have fun and happy coding!
Copyright © 2025 Mouse Vs Python | Powered by Pythonlibrary