Python bind an event from a different file - python

I'm new to python and trying to learn. I am creating a desktop application to record info from a form. I want to create a few panel objects that I will reuse in different frames, and I want to bind an event from the panel object in MyFrame class. I'm not sure if this is possible. Here is my code:
main.py:
import wx
from testPanel import testPanel
class MyFrame(wx.Frame):
def OnClick(self,event):
print "Clicked"
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title=title, size=(500,200))
...
myList = ['Project1', 'Project2', 'Project3', 'Project4']
myPanel = testPanel(self,-1,name="myPanel",lbl="Label: ", List=myList)
self.authorTxt = wx.TextCtrl(self, size=(140,-1))
self.button =wx.Button(self, label="Save")
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button)
...
box = wx.BoxSizer(wx.VERTICAL)
box.Add(myPanel, 1, wx.EXPAND)
box.Add(self.authorTxt, 1, wx.EXPAND)
box.Add(self.button, 1, wx.EXPAND)
...
testPanel.py
import wx
class testPanel(wx.Panel):
def EvtComboBox(self, event):
print"%s was selected" % event.GetString()
def __init__(self, parent, ID, name, lbl, List = []):
wx.Panel.__init__(self, parent, ID)
sizer = wx.BoxSizer(wx.HORIZONTAL)
lbl = wx.StaticText(self, label=lbl, size=(-1,1))
cb = wx.ComboBox(self, size=(-1, 1), choices=List, style=wx.CB_DROPDOWN)
self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, cb)
...
I am trying to make the text field "authorTxt" editable and uneditable depending on which ComboBox item is selected. Is there a way to bind the ComboBox event from testPanel.py in main.py?
Any help would be greatly appreciated!

I think you're looking for the following:
First, when you define cb in testPanel do it as:
self.cb = ...
Then you can do:
self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, myPanel.cb)
Where self.EvtComboBox would be a new function you need to define in MyFrame
That said, I highly recommend you use PubSub in this situation:
http://wiki.wxpython.org/WxLibPubSub
http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
EDIT: cb needed to be a member of testPanel

Related

Why can't you call object attributes within __init__?

I'm trying to understand why you can call an object's attribute within a class' method, but not within it's constructor. For example:
class Panel1(wx.Panel):
#include Frame in the constructor
def __init__(self, parent, frame):
wx.Panel.__init__(self, parent)
#set up so objects assigned in Frame (and it's children) can be called
self.frame = frame
button = wx.Button(self, label='Test')
#button.Bind(wx.EVT_BUTTON, lambda event: self.onButton(event))
button.Bind(wx.EVT_BUTTON, self.onButton)
pprint(vars(self.frame))
def onButton(self, event):
print("Button pressed.")
pprint(vars(self.frame))
#the following fails in the constructor as panel2 for some reason is not callable
a = self.frame.panel2.a
print("123: " + str(a))
Could someone point me to a link that explains why this isn't possible in the constructor?
Thanks!
*****EDIT****
Below is functional code to help explain my question better. If you try to call Panel2 attributes via the parent within Panel1's constructor, it fails. But it works fine when done inside Panel1's onButton method. I'd like to understand why.
import wx
from pprint import pprint
class Panel1(wx.Panel):
#include Frame in the constructor
def __init__(self, parent, frame):
wx.Panel.__init__(self, parent)
self.frame = frame
self.strPanel1 = "Panel1 string"
self.intPanel1 = 0
button = wx.Button(self, label='Test')
button.Bind(wx.EVT_BUTTON, self.onButton)
self.textbox = wx.TextCtrl(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
sizer.Add(self.textbox, 0, wx.ALL, 5)
sizer.Add(button, 0, wx.ALL, 5)
#pprint self.frame attributes fail here
def onButton(self, event):
#but not here!?!
print("Panel1 button pressed.")
pprint(vars(self.frame))
Panel2str = self.frame.panel2.strPanel2
print(Panel2str)
Panel2int = self.frame.panel2.intPanel2
print(str(Panel2int))
#Panel2 button press counter
self.frame.panel2.intPanel2 += 1
self.frame.panel2.trigger()
def trigger(self):
print("Panel1 has been triggered")
class Panel2(wx.Panel):
#include Frame in the constructor
def __init__(self, parent, frame):
wx.Panel.__init__(self, parent)
self.frame = frame
self.strPanel2 = "Panel2 string"
self.intPanel2 = 0
button = wx.Button(self, label='Test')
button.Bind(wx.EVT_BUTTON, self.onButton)
self.textbox = wx.TextCtrl(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
sizer.Add(self.textbox, 0, wx.ALL, 5)
sizer.Add(button, 0, wx.ALL, 5)
#pprint self.frame attributes fail here
def onButton(self, event):
#but not here!?!
print("Panel2 button pressed.")
pprint(vars(self.frame))
Panel1str = self.frame.panel1.strPanel1
print(Panel1str)
Panel1int = self.frame.panel1.intPanel1
print(str(Panel1int))
#Panel1 button press counter
self.frame.panel1.intPanel1 += 1
self.frame.panel1.trigger()
def trigger(self):
print("Panel2 has been triggered")
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="My Frame")
#Spliiting the frame
splitter = wx.SplitterWindow(self)
#Send frame to children
self.panel1 = Panel1(splitter, self)
self.panel2 = Panel2(splitter, self)
splitter.SplitVertically(self.panel1, self.panel2, 0)
splitter.SetMinimumPaneSize(200)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(splitter, 1, wx.EXPAND)
self.SetSizer(sizer)
if __name__ == '__main__':
app = wx.App()
frame = Frame()
frame.Show()
frame.Centre()
app.MainLoop()
Below is functional code to help explain my question better. If you try to call Panel2 attributes via the parent within Panel1's constructor, it fails.
It fails, because at the time your Panel1.__init__ is called, frame.panel2 is not set yet. Look at your Frame class:
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="My Frame")
#Spliiting the frame
splitter = wx.SplitterWindow(self)
#Send frame to children
self.panel1 = Panel1(splitter, self)
self.panel2 = Panel2(splitter, self)
...
When your Panel1 is initialized, panel2 is not set yet. It will only be set once Panel1 is done initializing and has been assigned to frame.panel1. If you absolutely need to access panel2 into Panel1.__init__, then you could make it so that your Panel1 take care of it. For example:
class Panel1:
def __init__(self, frame):
# Initialize panel2 and assign it to our frame
frame.panel2 = Panel2(frame)
# Now we can do whatever with panel2
frame.panel2
class Panel2:
def __init__(self, frame):
pass
class Frame:
def __init__(self):
# This initialize panel1, and internally initialize panel2
self.panel1 = Panel1(self)
frame = Frame()

How can I make a wxpython Widget span two cells without pushing other widgets aside?

I'm trying to make a form that has several input fields. Underneath these fields I want to have a wxpython Ultimate List Control (for all intents and purposes it's the same thing as a List Control). My issue is with sizers. To give some context, my form looks like this
Name [TextCtrl]
Blah [TextCtrl]
ListControl
I want it to look like
Name [TextCtrl]
Blah [TextCtrl]
ListCtrl (this spans to the end of the row)
My problem is when I try to add the List Control. I want the list control to Stretch from The Static Text to the Text Control, but it pushes the TextControl over. Can someone please point me in the right direction? I have attached the relevant code below.
class UserField(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent=parent, title="Info", size=(350, 400),
style=wx.DEFAULT_FRAME_STYLE)
self.init_ui()
self.Center()
self.ShowModal()
def init_ui(self):
panel = wx.Panel(self, wx.ID_ANY)
hbox = wx.BoxSizer(wx.VERTICAL)
flex_grid = wx.FlexGridSizer(5, 2, 5, 10) # row, col, vgap, hgap
info_text = wx.StaticText(parent=panel, label="Enter information")
self.search_button = wx.Button(parent=panel, label="Search")
self.list_control = UltimateListCtrl(panel,
agwStyle=wx.LC_REPORT | wx.BORDER_SUNKEN | ULC_HAS_VARIABLE_ROW_HEIGHT, )
flex_grid.AddMany(
[
info_text, self.search_button
]
)
lbox = wx.BoxSizer(wx.HORIZONTAL)
lbox.Add(self.list_control
hbox.Add(flex_grid, wx.EXPAND|wx.ALL)
hbox.Add(lbox, proportion=1, flag=wx.ALL|wx.EXPAND)
panel.SetSizer(hbox)
Here's a quick demonstration of wx.GridBagSizer. The program opens a simple frame with a single button that spawns a dialog with a GridBagSizer. You can place items in the sizer according to a position (pos) and optionally allow a widget to span multiple rows and/or columns (span).
import wx
class Example(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.SetSize((300, 200))
self.Centre()
self.Show(True)
self.InitUI()
def InitUI(self):
panel = wx.Panel(self)
sizer = wx.BoxSizer()
btn = wx.Button(panel, label="Spawn Window")
btn.Bind(wx.EVT_BUTTON, self.spawn_window)
sizer.Add(btn)
panel.SetSizerAndFit(sizer)
def spawn_window(self, evt):
UserField(self)
def OnQuit(self, e):
self.Close()
class UserField(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent=parent, title="Info", size=(350, 400),
style=wx.DEFAULT_FRAME_STYLE)
self.init_ui()
self.Center()
self.ShowModal()
def init_ui(self):
panel = wx.Panel(self)
sizer = wx.GridBagSizer(10, 10)
field1Label = wx.StaticText(panel, label="Field 1")
field2Label = wx.StaticText(panel, label="Field 2")
field1Ctrl = wx.TextCtrl(panel)
field2Ctrl = wx.TextCtrl(panel)
listCtrl = wx.ListCtrl(panel)
sizer.Add(field1Label, pos=(0, 0))
sizer.Add(field2Label, pos=(1, 0))
sizer.Add(field1Ctrl, pos=(0, 1))
sizer.Add(field2Ctrl, pos=(1, 1))
# HERE'S THE IMPORTANT LINE. NOTE THE 'span' ARGUMENT:
sizer.Add(listCtrl, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
panel.SetSizerAndFit(sizer)
if __name__ == '__main__':
ex = wx.App()
mainFrame = Example(None)
ex.MainLoop()

Attach/Detach two frames in wxpython

I am designing a GUI with several components and two wx.Frame objects F1 and F2. F1 is the main frame and F2 is the secondary frame. I would like to have a mechanism, so the user can attach these two frames into one frame, and also detach them into two frames again if needed.
Assume F1 and F2 contain panels P1 and P2 respectively. When detached, the use should be able to move and resize each frame independently, and closing F1 will close the entire GUI. When attached, F1 will contain both P1 and P2 vertically and F2 will seem to vanish and become a part of F1. There is a lot of wiring and events and messages passed between P1 and P2 which should work in both attached and detached modes.
I have seen this effect in some modern GUI's, but I was unable to find a proper technique online to carry this out. What is a proper way to do this?
Thanks
I came up with a solution for this using the pubsub module. Following is a little example I wrote to show how it is done:
import wx
import gettext
from wx.lib.pubsub import pub
class SubFramePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.ID_ANY)
self.attachDetachButton = wx.Button(self, wx.ID_ANY, _("Attach"))
self.sayHelloButton = wx.Button(self, wx.ID_ANY, _("Say Hello"))
subPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
subPanelSizer.Add(self.attachDetachButton, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 0)
subPanelSizer.Add(self.sayHelloButton, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 0)
self.SetSizer(subPanelSizer)
self.attachDetachButton.Bind(wx.EVT_BUTTON, self.OnAttachDetachButton)
self.sayHelloButton.Bind(wx.EVT_BUTTON, self.OnSayHelloButton)
def OnAttachDetachButton(self, event):
if self.attachDetachButton.GetLabel() == "Attach":
self.attachDetachButton.SetLabel("Detach")
pub.sendMessage("show.mainframe.OnAttach", data=self)
else:
self.attachDetachButton.SetLabel("Attach")
pub.sendMessage("show.mainframe.OnDetach", data=self)
event.Skip()
def OnSayHelloButton(self, event):
pub.sendMessage("show.mainframe.addText", data="Say Hello\n")
event.Skip()
class SubFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
if kwds.has_key("panel"):
self.panel = kwds["panel"]
del kwds["panel"]
else:
self.panel = None
wx.Frame.__init__(self, *args, **kwds)
if self.panel is None:
self.panel = SubFramePanel(self)
else:
self.panel.Reparent(self)
self.SetTitle(_("Sub Frame"))
self.SetSize((291, 93))
subFrameSizer = wx.BoxSizer(wx.VERTICAL)
subFrameSizer.Add(self.panel, 1, wx.EXPAND | wx.LEFT, 5)
self.SetSizer(subFrameSizer)
self.Layout()
pub.subscribe(self.OnClose, "show.subframe.OnClose")
def OnClose(self, data=None):
self.Close()
# end of class SubFrame
class MainFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE)
pub.subscribe(self.OnAddText, "show.mainframe.addText")
pub.subscribe(self.OnAttach, "show.mainframe.OnAttach")
pub.subscribe(self.OnDetach, "show.mainframe.OnDetach")
self.SetTitle(_("Main Frame"))
self.SetSize((492, 271))
self.mainFrameSizer = wx.BoxSizer(wx.VERTICAL)
self.mainFrameSizer.Add(self.text_ctrl_1, 1, wx.ALL | wx.EXPAND, 5)
self.SetSizer(self.mainFrameSizer)
self.Layout()
def OnAddText(self, data):
self.text_ctrl_1.WriteText(data)
def OnAttach(self, data):
self.mainFrameSizer.Add(data, 0, wx.ALL | wx.EXPAND, 5)
data.Reparent(self)
self.Layout()
pub.sendMessage("show.subframe.OnClose")
def OnDetach(self, data):
subFrame = SubFrame(self, wx.ID_ANY, "", panel=data)
self.mainFrameSizer.Remove(data)
self.Layout()
subFrame.Show()
class MyApp(wx.App):
def OnInit(self):
mainFrame = MainFrame(None, wx.ID_ANY, "")
self.SetTopWindow(mainFrame)
mainFrame.Show()
subFrame = SubFrame(mainFrame, wx.ID_ANY, "")
subFrame.Show()
return 1
if __name__ == "__main__":
gettext.install("app")
app = MyApp(0)
app.MainLoop()
I'm not sure you can move a wxPanel from a wxFrame to another on the fly on wx.
The main reason is that the panel is dependent of its parent and you can't change it on the fly.
Now if you really want to do it, you'll have to create copy the panel in the other frame and delete the previous panel and frame (or just hide them).
There's no built in copy but you can find a way to get the content of your original panel and copy it on the other one.
There is a library in wxPython called AUI. It provides the mechanism to detach a panel from a frame. The following link has an example along with some other information:
http://wiki.wxpython.org/AuiNotebook%20(AGW)

Using the same handler for multiple wx.TextCtrls?

I'm having a bit of trouble with a panel that has two wxPython TextCtrls in it. I want either an EVT_CHAR or EVT_KEY_UP handler bound to both controls, and I want to be able to tell which TextCtrl generated the event. I would think that event.Id would tell me this, but in the following sample code it's always 0. Any thoughts? I've only tested this on OS X.
This code simply checks that both TextCtrls have some text in them before enabling the Done button
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title,
wx.DefaultPosition, wx.Size(200, 150))
self.panel = BaseNameEntryPanel(self)
class BaseNameEntryPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.entry = wx.TextCtrl(self, wx.NewId())
self.entry2 = wx.TextCtrl(self, wx.NewId())
self.donebtn = wx.Button(self, wx.NewId(), "Done")
self.donebtn.Disable()
vsizer = wx.BoxSizer(wx.VERTICAL)
vsizer.Add(self.entry, 1, wx.EXPAND|wx.GROW)
vsizer.Add(self.entry2, 1, wx.EXPAND|wx.GROW)
vsizer.Add(self.donebtn, 1, wx.EXPAND|wx.GROW)
self.SetSizer(vsizer)
self.Fit()
self.entry.Bind(wx.EVT_KEY_UP, self.Handle)
self.entry2.Bind(wx.EVT_KEY_UP, self.Handle)
def Handle(self, event):
keycode = event.GetKeyCode()
print keycode, event.Id # <- event.Id is always 0!
def checker(entry):
return bool(entry.GetValue().strip())
self.donebtn.Enable(checker(self.entry) and checker(self.entry2))
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "Hello from wxPython")
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
You could try event.GetId() or event.GetEventObject() and see if either of these work.
Another approach to this is to use lambda or functools.partial to effectively pass a parameter to the handler. So, for example, sub in the lines below into your program:
self.entry.Bind(wx.EVT_KEY_UP, functools.partial(self.Handle, ob=self.entry))
self.entry2.Bind(wx.EVT_KEY_UP, functools.partial(self.Handle, ob=self.entry2))
def Handle(self, event, ob=None):
print ob
And then ob will be either entry or entry2 depending on which panel is clicked. But, of course, this shouldn't be necessary, and GetId and GetEventObject() should both work -- though I don't (yet) have a Mac to try these on.

How do I Create an instance of a class in another class in Python

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()

Categories