I see questions relating to the title of this article a lot. How do I open a second frame / window? How do I get all the frames to close when I close the main application? When you are first learning wxPython, these kinds of questions can be kind of hard to find answers for as you aren't familiar enough with the framework or the terminology to know how to search for the answers.
Hopefully this article will help. We will learn how to open multiple frames and how to make them all close too. Let's get started!
Creating multiple frames is actually pretty simple. You just need to create a subclass of wx.Frame for each new frame that you want to create. Or if the new frames can look the same, then you just need to instantiate the second class multiple times. You will also need a subclass of wx.Frame for the main application's frame. Let's write some code as I think that will make things clearer:
import wx class OtherFrame(wx.Frame): """ Class used for creating frames other than the main one """ def __init__(self, title, parent=None): wx.Frame.__init__(self, parent=parent, title=title) self.Show() class MyPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) btn = wx.Button(self, label='Create New Frame') btn.Bind(wx.EVT_BUTTON, self.on_new_frame) self.frame_number = 1 def on_new_frame(self, event): title = 'SubFrame {}'.format(self.frame_number) frame = OtherFrame(title=title) self.frame_number += 1 class MainFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title='Main Frame', size=(800, 600)) panel = MyPanel(self) self.Show() if __name__ == '__main__': app = wx.App(False) frame = MainFrame() app.MainLoop()
So here we create two classes that subclass wx.Frame: OtherFrame and MainFrame. OtherFrame is what we will use for our extra frames while MainFrame is our main application's window. We also create a subclass of wx.Panel to hold a button and a button event handler. The button will be used to create the extra frames.
Try running this code and you will find that it can create any number of extra frames just by clicking the button multiple times. However if you try to close the main application with one or more of the other frames still running, then you will discover that the application will continue to run.
Let's learn how we can modify this code to close all frames when the main frame is closed.
You may have noticed this already, but both the MainFrame class and the OtherFrame class have parents set. The MainFrame class is hard-coded rather subtlety to None while OtherFrame has its parent just defaulted to None. This is actually the key to our issue. When you set a wx.Frame's parent to None, it becomes its own entity with no dependencies. So it doesn't care if another frame closes or not. However if we were to set all of the OtherFrame instance's parent to be the MainFrame instance, then they would all close when we close the main frame.
To make this change, all we need to do is change the on_new_frame function to the following:
def on_new_frame(self, event): title = 'SubFrame {}'.format(self.frame_number) frame = OtherFrame(title=title, parent=wx.GetTopLevelParent(self)) self.frame_number += 1
Here we set the parent to the MainFrame by using wxPython's wx.GetTopLevelParent function. Another way you could do this is to modify MyPanel's __init__ to save off a reference to the parent parameter:
def __init__(self, parent): wx.Panel.__init__(self, parent) btn = wx.Button(self, label='Create New Frame') btn.Bind(wx.EVT_BUTTON, self.on_new_frame) self.frame_number = 1 self.parent = parent def on_new_frame(self, event): title = 'SubFrame {}'.format(self.frame_number) frame = OtherFrame(title=title, parent=self.parent) self.frame_number += 1
In this case we just use the instance attribute here for setting our parent. Just to be thorough though, I want to mention one other way we could set the parent to the MainFrame instance and that is by calling GetParent():
def on_new_frame(self, event): title = 'SubFrame {}'.format(self.frame_number) frame = OtherFrame(title=title, parent=self.GetParent()) self.frame_number += 1
Here we just call the MyPanel's GetParent() method to get the panel's parent. This works because MyPanel is only used by the MainFrame. Things would get messy if we happened to use that panel class with other frames though, so I personally prefer the GetTopLevelParent method because it pretty much guarantees that we will get the right widget.
Working with multiple frames shouldn't be hard. I hope this tutorial has shown you just how easy it is to do. You can use the concepts in this article to also create dialogs as they work in much the same way as frames do, although they tend to be modal. Thanks for reading and happy coding!
Copyright © 2024 Mouse Vs Python | Powered by Pythonlibrary