I've just gotten into wxpython for Python 3.6 and I've hit a roadblock. I just can't work out how to make elements/widgets scale with the screen! I know you have to use sizers but that's about it, I'm still fairly new to programming so just reading the documentation didn't help. If someone could just sample some code that works I'd be very thankful as I could then read through it and work out what I was doing wrong. The code that draws out the GUI I want to scale with window size is below, the key idea is that the TextCtrl scales, other elements don't really need scaling.
def createGUI(self):
panel = wx.Panel(self)
menuBar = wx.MenuBar()
menuButton = wx.Menu()
newItem = wx.MenuItem(menuButton, wx.ID_NEW, 'New Note\tCtrl+N')
delItem = wx.MenuItem(menuButton, wx.ID_DELETE, 'Delete Note\tCtrl+Backspace')
saveItem = wx.MenuItem(menuButton, wx.ID_SAVE, 'Save\tCtrl+S')
exitItem = wx.MenuItem(menuButton, wx.ID_EXIT, 'Quit\tCtrl+Q')
menuButton.Append(newItem)
menuButton.Append(saveItem)
menuButton.Append(delItem)
menuButton.Append(exitItem)
menuBar.Append(menuButton, 'Menu')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.new, newItem)
self.Bind(wx.EVT_MENU, self.delete, delItem)
self.Bind(wx.EVT_MENU, self.save, saveItem)
self.Bind(wx.EVT_MENU, self.onExit, exitItem)
self.noteText = wx.TextCtrl(panel)
self.noteText.AppendText(self.notecontent)
self.Bind(wx.EVT_CLOSE, self.onExit)
self.SetTitle(f'Welcome {self.username}! You are working on {self.notepath}')
self.Centre()
self.Show(True)
To begin with it's best to equate sizers to something familiar and I usually think of storage boxes or a chest of drawers.
When we define widgets, they are all dumped into a container, the parent object, often the ubiquitous self or self.panel.
If we do not assign a size and pos to each item, it's just a jumbled mess, a pile of widgets.
The sizer, there are many types, are the virtual drawers in our chest of drawers, that herds this pile of widgets into order.
The widgets are assigned to the appropriate sizer, note sizers can go into other sizers, and eventually when everything has been assigned a place or drawer in our chest of drawers, the sizers do their magic, arranging and sizing all of widgets into a coherent screen for display or arranging the drawers contents and relative positions in the chest of drawers.
Below, I've used the simplest sizer a boxsizer.
One will arrange things vertically and the other horizontally.
The horizontal sizer is for the buttons and the vertical sizer, will be the main sizer, into which I place not only the TectCtrl but also the buttons, prearranged in their horizontal sizer.
For a better and more comprehensive description of sizers and their controls see: https://docs.wxpython.org/sizers_overview.html and for details on the actual sizers available see the detailed documenation on each.
A loose approximation of your code:
#!/usr/bin/python
import wx
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title=title,
size=(450, 350))
self.panel = wx.Panel(self, -1)
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.noteText = wx.TextCtrl(self.panel, -1, style=wx.TE_MULTILINE)
self.Button_close = wx.Button(self.panel, -1, label="Quit")
self.Button_1 = wx.Button(self.panel, -1, label="Btn1")
self.Button_2 = wx.Button(self.panel, -1, label="Btn2")
self.Bind(wx.EVT_CLOSE, self.onExit)
self.Button_close.Bind(wx.EVT_BUTTON, self.onExit)
self.Button_1.Bind(wx.EVT_BUTTON, self.onButton)
self.Button_2.Bind(wx.EVT_BUTTON, self.onButton)
# Place buttons within their own horizontal sizer
self.button_sizer.Add(self.Button_close,proportion=0, flag=wx.ALL, border=10)
self.button_sizer.Add(self.Button_1,proportion=0, flag=wx.ALL, border=10)
self.button_sizer.Add(self.Button_2,proportion=0, flag=wx.ALL, border=10)
# Add textctrl and the button sizer to the main sizer (vertical)
self.main_sizer.Add(self.noteText,proportion=1, flag=wx.EXPAND|wx.ALL, border=10)
self.main_sizer.Add(self.button_sizer, 0, 0, 0)
self.panel.SetSizer(self.main_sizer)
self.Show()
def onExit(self, event):
self.Destroy()
def onButton(self, event):
print("A button was pressed")
if __name__ == '__main__':
app = wx.App()
Example(None, title="Example")
app.MainLoop()
Edit:
Here's the same code without using sizers, although they are recommended.
#!/usr/bin/python
import wx
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title=title,
size=(450, 350))
self.panel = wx.Panel(self, -1)
self.noteText = wx.TextCtrl(self.panel, -1, pos=(10,10), size=(400,280), style=wx.TE_MULTILINE)
self.Button_close = wx.Button(self.panel, -1, label="Quit", pos=(10,290), size=(50,30))
self.Button_1 = wx.Button(self.panel, -1, label="Btn1", pos=(70,290), size=(50,30))
self.Button_2 = wx.Button(self.panel, -1, label="Btn2", pos=(130,290), size=(50,30))
self.Bind(wx.EVT_CLOSE, self.onExit)
self.Button_close.Bind(wx.EVT_BUTTON, self.onExit)
self.Button_1.Bind(wx.EVT_BUTTON, self.onButton)
self.Button_2.Bind(wx.EVT_BUTTON, self.onButton)
self.Show()
def onExit(self, event):
self.Destroy()
def onButton(self, event):
print("A button was pressed")
if __name__ == '__main__':
app = wx.App()
Example(None, title="Example")
app.MainLoop()
Related
I'm creating a wx.agw.SuperToolTip. I'm updating the message in the tip every few seconds, and if the tip is showing when the message updates the tip is redrawn in a different position.
The new position seems to be relative to the original position's relation to the top left corner of the screen, but that could just be coincidence.
Also, if I modify wx.lib.agw.supertooltip.ToolTipWindowBase.Invalidate() by commenting out the call to self.CalculateBestSize() the problem goes away. Of course then the window won't resize, so that's no solution.
I'm using wxPython 2.8.12.1.
Here's an app that demonstrates the problem:
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title,
pos=(150, 150), size=(350, 225))
panel = wx.Panel(self)
btn = wx.Button(panel, -1, "Hover over this")
self._superTip = SuperToolTip("")
self._superTip.SetHeader("Heyo!")
self._superTip.SetTarget(btn)
self._superTip.EnableTip(True)
self._superTip.SetDrawHeaderLine(True)
self._superTip.SetDrawFooterLine(True)
self._superTip.SetStartDelay(1)
self._superTip.SetEndDelay(60)
currentFooterFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
currentFooterFont.SetPointSize(6)
currentFooterFont.SetWeight(wx.NORMAL)
self._superTip.SetFooterFont(currentFooterFont)
self._superTip.SetFooter('(Click to close)')
self._superTip.ApplyStyle("Blue Glass")
self._superTip.SetDropShadow(True)
self.ttTimer = wx.Timer(self)
self.ttText = 'What the?'
self.Bind(wx.EVT_TIMER, self.onTimer, self.ttTimer)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(btn, 0, wx.ALL, 10)
panel.SetSizer(sizer)
self.ttTimer.Start(2000)
panel.Layout()
def onTimer(self, evt):
self._superTip.SetMessage(self.ttText)
self.ttText += '?'
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, "STT error demo")
self.SetTopWindow(frame)
frame.Show(True)
return True
app = MyApp(redirect=True)
app.MainLoop()
Any thoughts on how I can update a visible tooltip without its location changing?
Thanks a lot.
I'm developing a small application with multiple TextCtrl and ComboBox widgets. I want that when I press the key Ctrl-C Ctrl-V and Ctrl-X I get the usual behaviour of copying, pasting and cutting in the appropriate entry.
What I obtain right now is that, while I can right-click and have the text copd/past/cutd, I can't through the keybindings or the menu entries. How can I obtain this in a simple way?
Menu keybindings work by default with Alt-first_menu_letter -> submenu_first_letter.
The menu event of the selected item should bind the corresponding event handler:
self.Bind(wx.EVT_MENU, self.on_copy, self.copy)
for a copy method, you first select the text you want to copy with the mouse. Then you can get the widget that is focused (the specific textcontrol with the selected string to be copied) with:
widget = self.FindFocus()
in this way now you can get the selected string from that widget:
self.copied = widget.GetStringSelection()
And the same has to be done for pasting the copied text in the textctrl you situate the cursor.
Here you have a working example:
import wx
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.tctrl_1 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
self.tctrl_2 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
self.menubar = wx.MenuBar()
self.test = wx.Menu()
self.copy = wx.MenuItem(self.test, wx.NewId(), "copy", "is_going to copy", wx.ITEM_NORMAL)
self.test.AppendItem(self.copy)
self.paste = wx.MenuItem(self.test, wx.NewId(), "paste", "will paste", wx.ITEM_NORMAL)
self.test.AppendItem(self.paste)
self.menubar.Append(self.test, "Test")
self.SetMenuBar(self.menubar)
self.__set_properties()
self.__do_layout()
self.Bind(wx.EVT_MENU, self.on_copy, self.copy)
self.Bind(wx.EVT_MENU, self.on_paste, self.paste)
def __set_properties(self):
self.SetTitle("frame_1")
def __do_layout(self):
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(self.tctrl_1, 1, wx.EXPAND, 0)
sizer_2.Add(self.tctrl_2, 1, wx.EXPAND, 0)
sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
def on_copy(self, event):
widget = self.FindFocus()
self.copied = widget.GetStringSelection()
def on_paste(self, event):
widget = self.FindFocus()
widget.WriteText(self.copied)
if __name__ == "__main__":
app = wx.PySimpleApp(0)
frame = MyFrame(None, -1, "")
frame.Show()
app.MainLoop()
I am trying to make a window with 2 panels. One panel is just a notebook panel. The second panel contains a toolbar on top and a text control on the bottom. I want to arrange this panel in my frame using wx.aui.AuiManager.
The problem is that I get a big empty space of grey in my custom panel.
Here is my code:
import wx
import wx.aui
import images # contains toolbar icons
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"AUI Tutorial",
size=(600,400))
self._mgr = wx.aui.AuiManager()
self._mgr.SetManagedWindow(self)
notebook = wx.aui.AuiNotebook(self)
nb_panel = TabPanel(notebook)
my_panel = MyPanel(self)
notebook.AddPage(nb_panel, "First Tab", False)
self._mgr.AddPane(notebook,
wx.aui.AuiPaneInfo().Name("notebook-content").
CenterPane().PaneBorder(False))
self._mgr.AddPane(my_panel,
wx.aui.AuiPaneInfo().Name("txtctrl-content").
CenterPane().PaneBorder(False))
self._mgr.GetPane("notebook-content").Show().Top().Layer(0).Row(0).Position(0)
self._mgr.GetPane("txtctrl-content").Show().Bottom().Layer(1).Row(0).Position(0)
self._mgr.Update()
class MyPanel(wx.Panel):
"""
My panel with a toolbar and richtextctrl
"""
def __init__(self,parent):
wx.Panel.__init__(self,parent=parent,id=wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
toolbar = wx.ToolBar(self,-1)
toolbar.AddLabelTool(wx.ID_EXIT, '', images._rt_smiley.GetBitmap())
self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)
toolbar.Realize()
sizer.Add(toolbar,proportion=0,flag=wx.ALL | wx.ALIGN_TOP)
text = ""
txtctrl = wx.TextCtrl(self,-1, text, wx.Point(0, 0), wx.Size(150, 90),
wx.NO_BORDER | wx.TE_MULTILINE | wx.TE_READONLY|wx.HSCROLL)
sizer.Add(txtctrl,proportion=0,flag=wx.EXPAND)
self.SetSizer(sizer)
def OnExit(self,event):
self.Close()
class TabPanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self,parent=parent,id=wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(txtOne, 0, wx.ALL, 5)
sizer.Add(txtTwo, 0, wx.ALL, 5)
self.SetSizer(sizer)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.MainLoop()
So, how do I fix my code so that I don't have that grey block taking up MyPanel? Also, my toolbar button doesn't seem to run self.OnExit(). Why is that?
Thank you for your help.
Take out the line:
self._mgr.GetPane("notebook-content").Show().Top().Layer(0).Row(0).Position(0)
As for the OnExit() handler, it is firing!
If you want to exit the application, replace it with app.Exit()
I'm creating a small wxPython utility for the first time, and I'm stuck on a problem.
I would like to add components to an already created frame. To do this, I am destroying the frame's old panel, and creating a new panel with all new components.
1: Is there a better way of dynamically adding content to a panel?
2: Why, in the following example, do I get a a strange redraw error in which in the panel is drawn only in the top left hand corner, and when resized, the panel is drawn correctly?
(WinXP, Python 2.5, latest wxPython)
Thank you for the help!
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'TimeTablr')
#Variables
self.iCalFiles = ['Empty', 'Empty', 'Empty']
self.panel = wx.Panel(self, -1)
self.layoutElements()
def layoutElements(self):
self.panel.Destroy()
self.panel = wx.Panel(self, -1)
#Buttons
self.getFilesButton = wx.Button(self.panel, 1, 'Get Files')
self.calculateButton = wx.Button(self.panel, 2, 'Calculate')
self.quitButton = wx.Button(self.panel, 3, 'Quit Application')
#Binds
self.Bind(wx.EVT_BUTTON, self.Quit, id=3)
self.Bind(wx.EVT_BUTTON, self.getFiles, id=1)
#Layout Managers
vbox = wx.BoxSizer(wx.VERTICAL)
#Panel Contents
self.ctrlsToDescribe = []
self.fileNames = []
for iCalFile in self.iCalFiles:
self.ctrlsToDescribe.append(wx.TextCtrl(self.panel, -1))
self.fileNames.append(wx.StaticText(self.panel, -1, iCalFile))
#Add Components to Layout Managers
for i in range(0, len(self.ctrlsToDescribe)):
hboxtemp = wx.BoxSizer(wx.HORIZONTAL)
hboxtemp.AddStretchSpacer()
hboxtemp.Add(self.fileNames[i], 1, wx.EXPAND)
hboxtemp.AddStretchSpacer()
hboxtemp.Add(self.ctrlsToDescribe[i], 2, wx.EXPAND)
hboxtemp.AddStretchSpacer()
vbox.Add(hboxtemp)
finalHBox = wx.BoxSizer(wx.HORIZONTAL)
finalHBox.Add(self.getFilesButton)
finalHBox.Add(self.calculateButton)
finalHBox.Add(self.quitButton)
vbox.Add(finalHBox)
self.panel.SetSizer(vbox)
self.Show()
def Quit(self, event):
self.Destroy()
def getFiles(self, event):
self.iCalFiles = ['Example1','Example1','Example1','Example1','Example1','Example1']
self.layoutElements()
self.Update()
app = wx.App()
MainFrame()
app.MainLoop()
del app
1) I beleive the Sizer will let you insert elements into the existing ordering of them. That would probably be a bit faster.
2) I don't see the behavior you're describing on OSX, but at a guess, try calling self.Layout() before self.Show() in layoutElements?
I had a similar problem where the panel would be squished into the upper-right corner. I solved it by calling panel.Fit().
In your example, you should call self.panel.Fit() after self.panel.SetSizer(vbox)
I am trying to learn Python and WxPython. I have been a SAS programmer for years. This OOP stuff is slowly coming together but I am still fuzzy on a lot of the concepts. Below is a section of code. I am trying to use a button click to create an instance of another class. Specifically-I have my main panel in one class and I wanted to instance a secondary panel when a user clicked on one of the menu items on the main panel. I made all of this work when the secondary panel was just a function. I can't seem to get ti to work as a class.
Here is the code
import wx
class mainPanel(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, 'directEDGAR Supplemental Tools', size=(450, 450))
wx.Panel(self,-1)
wx.StaticText(self,-1, "This is where I will describe\n the purpose of these tools",(100,10))
menubar = wx.MenuBar()
parser = wx.Menu()
one =wx.MenuItem(parser,1,'&Extract Tables with One Heading or Label')
two =wx.MenuItem(parser,1,'&Extract Tables with Two Headings or Labels')
three =wx.MenuItem(parser,1,'&Extract Tables with Three Headings or Labels')
four =wx.MenuItem(parser,1,'&Extract Tables with Four Headings or Labels')
quit = wx.MenuItem(parser, 2, '&Quit\tCtrl+Q')
parser.AppendItem(one)
parser.AppendItem(two)
parser.AppendItem(three)
parser.AppendItem(four)
parser.AppendItem(quit)
menubar.Append(parser, '&Table Parsers')
textRip = wx.Menu()
section =wx.MenuItem(parser,1,'&Extract Text With Section Headings')
textRip.AppendItem(section)
menubar.Append(textRip, '&Text Rippers')
dataHandling = wx.Menu()
deHydrate =wx.MenuItem(dataHandling,1,'&Extract Data from Tables')
dataHandling.AppendItem(deHydrate)
menubar.Append(dataHandling, '&Data Extraction')
self.Bind(wx.EVT_MENU, self.OnQuit, id=2)
this is where I think I am being clever by using a button click to create an instance
of subPanel.
self.Bind(wx.EVT_MENU, self.subPanel(None, -1, 'TEST'),id=1)
self.SetMenuBar(menubar)
self.Centre()
self.Show(True)
def OnQuit(self, event):
self.Close()
class subPanel(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, 'directEDGAR Supplemental Tools', size=(450, 450))
wx.Panel(self,-1)
wx.StaticText(self,-1, "This is where I will describe\n the purpose of these tools",(100,10))
getDirectory = wx.Button(panel, -1, "Get Directory Path", pos=(20,350))
getDirectory.SetDefault()
getTerm1 = wx.Button(panel, -1, "Get Search Term", pos=(20,400))
getTerm1.SetDefault()
#getDirectory.Bind(wx.EVT_BUTTON, getDirectory.OnClick, getDirectory.button)
self.Centre()
self.Show(True)
app = wx.App()
mainPanel(None, -1, '')
app.MainLoop()
I don't know wxWidgets, but based on what I know of Python, I'm guessing that you need to change:
self.Bind(wx.EVT_MENU, self.subPanel(None, -1, 'TEST'),id=1)
to:
self.Bind(wx.EVT_MENU, subPanel(None, -1, 'TEST'),id=1)
"subPanel" is a globally defined class, not a member of "self" (which is a mainPanel).
Edit: Ah, "Bind" seems to bind an action to a function, so you need to give it a function that creates the other class. Try the following. It still doesn't work, but at least it now crashes during the subPanel creation.
self.Bind(wx.EVT_MENU, lambda(x): subPanel(None, -1, 'TEST'),id=1)
You should handle the button click event, and create the panel in your button handler (like you already do with your OnQuit method).
I think the following code basically does what you're after -- creates a new Frame when the button is clicked/menu item is selected.
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, title="My Frame", num=1):
self.num = num
wx.Frame.__init__(self, parent, -1, title)
panel = wx.Panel(self)
button = wx.Button(panel, -1, "New Panel")
button.SetPosition((15, 15))
self.Bind(wx.EVT_BUTTON, self.OnNewPanel, button)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
# Now create a menu
menubar = wx.MenuBar()
self.SetMenuBar(menubar)
# Panel menu
panel_menu = wx.Menu()
# The menu item
menu_newpanel = wx.MenuItem(panel_menu,
wx.NewId(),
"&New Panel",
"Creates a new panel",
wx.ITEM_NORMAL)
panel_menu.AppendItem(menu_newpanel)
menubar.Append(panel_menu, "&Panels")
# Bind the menu event
self.Bind(wx.EVT_MENU, self.OnNewPanel, menu_newpanel)
def OnNewPanel(self, event):
panel = MyFrame(self, "Panel %s" % self.num, self.num+1)
panel.Show()
def OnCloseWindow(self, event):
self.Destroy()
def main():
application = wx.PySimpleApp()
frame = MyFrame(None)
frame.Show()
application.MainLoop()
if __name__ == "__main__":
main()
Edit: Added code to do this from a menu.
You need an event handler in your bind expression
self.bind(wx.EVT_MENU, subPanel(None, -1, 'TEST'),id=1)
needs to be changed to:
self.bind(wx.EVT_MENU, <event handler>, <id of menu item>)
where your event handler responds to the event and instantiates the subpanel:
def OnMenuItem(self, evt): #don't forget the evt
sp = SubPanel(self, wx.ID_ANY, 'TEST')
#I assume you will add it to a sizer
#if you aren't... you should
test_sizer.Add(sp, 1, wx.EXPAND)
#force the frame to refresh the sizers:
self.Layout()
Alternatively, you can instantiate the subpanel in your frame's __init__ and call a subpanel.Hide() after instantiation, and then your menuitem event handler and call a show on the panel subpanel.Show()
Edit: Here is some code that will do what I think that you are asking:
#!usr/bin/env python
import wx
class TestFrame(wx.Frame):
def __init__(self, parent, *args, **kwargs):
wx.Frame.__init__(self, parent, *args, **kwargs)
framesizer = wx.BoxSizer(wx.VERTICAL)
mainpanel = MainPanel(self, wx.ID_ANY)
self.subpanel = SubPanel(self, wx.ID_ANY)
self.subpanel.Hide()
framesizer.Add(mainpanel, 1, wx.EXPAND)
framesizer.Add(self.subpanel, 1, wx.EXPAND)
self.SetSizerAndFit(framesizer)
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
wx.Panel.__init__(self, parent, *args, **kwargs)
panelsizer = wx.BoxSizer(wx.VERTICAL)
but = wx.Button(self, wx.ID_ANY, "Add")
self.Bind(wx.EVT_BUTTON, self.OnAdd, but)
self.panel_shown = False
panelsizer.Add(but, 0)
self.SetSizer(panelsizer)
def OnAdd(self, evt):
if not self.panel_shown:
self.GetParent().subpanel.Show()
self.GetParent().Fit()
self.GetParent().Layout()
self.panel_shown = True
else:
self.GetParent().subpanel.Hide()
self.GetParent().Fit()
self.GetParent().Layout()
self.panel_shown = False
class SubPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
wx.Panel.__init__(self, parent, *args, **kwargs)
spsizer = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, wx.ID_ANY, label='I am a subpanel')
spsizer.Add(text, 1, wx.EXPAND)
self.SetSizer(spsizer)
if __name__ == '__main__':
app = wx.App()
frame = TestFrame(None, wx.ID_ANY, "Test Frame")
frame.Show()
app.MainLoop()