Why can't you call object attributes within __init__? - python

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

Related

Wxpython passing data from one frame to another

I have a text control, a choice control, and a button on one from. I would like to pass the values of both the text control and the choice control to another frame when button is pressed. In the on_press method in the code below, I returned both values as a list. How can I access this list in PanelTwo. Here's the code:
import wx
class PanelOne(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
box = wx.BoxSizer(wx.VERTICAL)
self.label = wx.StaticText(self,label = "Your Name:" ,style = wx.ALIGN_CENTRE)
box.Add(self.label, 1 , wx.EXPAND |wx.ALIGN_CENTER_HORIZONTAL |wx.ALL, 20)
self.inputTxtOne = wx.TextCtrl(self,wx.ID_ANY,value='')
box.Add(self.inputTxtOne, 1 , wx.EXPAND |wx.ALIGN_CENTER_HORIZONTAL |wx.ALL, 20)
languages = ['ZIRA', 'DAVID']
chlbl = wx.StaticText(self,label = "Voice control",style = wx.ALIGN_CENTRE)
box.Add(chlbl,1,wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL,5)
self.choice = wx.Choice(self,choices = languages)
box.Add(self.choice,1,wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL,5)
btn = wx.Button(self, label='Submit',style=wx.TRANSPARENT_WINDOW)
btn.SetFont(wx.Font(15,wx.FONTFAMILY_DEFAULT,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL))
btn.Bind(wx.EVT_BUTTON, self.on_press)
box.Add(btn,1,wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL,5)
self.SetSizer(box)
btn.Bind(wx.EVT_BUTTON, self.on_press)
def on_press(self,event):
name = self.inputTxtOne.GetValue()
voice_choice = self.choice.GetValue()
nv_list = [name,voice_choice]
parent_frame = self.GetParent()
parent_frame.Close()
frame = FrameTwo()
frame.Show()
return nv_list
class PanelTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
box = wx.BoxSizer(wx.VERTICAL)
self.label = wx.StaticText(self,label = "Your Name is "+name ,style = wx.ALIGN_CENTRE)
box.Add(self.label, 1 , wx.EXPAND |wx.ALIGN_CENTER_HORIZONTAL |wx.ALL, 20)
self.SetSizer(box)
class FrameOne(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="First Frame")
panel = PanelOne(self)
self.Show()
class FrameTwo(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Second Frame")
panel = PanelTwo(self)
self.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = FrameOne()
app.MainLoop()
There are several errors in your code. Let's go step by step.
First of all, the variable called "name" in PanelOne, must be part of the class instead of a local variable of the "on_press" method:
Change:
name = self.inputTxtOne.GetValue()
voice_choice = self.choice.GetValue()
by:
# Notice the use of self. In this way, we can access it from anywhere in the class
self.name = self.inputTxtOne.GetValue()
# What version of wx are you using? Get the choices value with GetSelection() method.
voice_choice = self.choice.GetSelection()
Now we need to access this variable from another class (PanelTwo). For this, we will pass an instance of the PanelOne class to the second frame (Frame2) and we will store it as a member variable (with the use of self, as we saw in the previous example).
# When you instantiate the Frame2 in PanelOne must pass the self instance of PanelOne
frame = FrameTwo(self)
Then in init method in Frame2:
def __init__(self, parent):
wx.Frame.__init__(self, None, title="Second Frame")
#Now the reference to PanelOne is a member varialble of class Frame2.
self.panelOne = parent
#Pass Frame2 instance (self) to PanelTwo in constructor:
panel = PanelTwo(self)
# Rest of your code...
Finally in PanelTwo:
class PanelTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self)
# parent is Frame2.
# panelOne is a public and member variable of Frame2 that references to PanelOne instance.
# name is a member and accesible variable of PanelOne.
print(parent.panelOne.name)
A recommendation: use WxGlade for make best code. With it you can learn a lot about how things are done in wxpython.
Good luck!

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

Update/Refresh Dynamically–Created WxPython Widgets

New python programmer here and trying to learn how to dynamically update widgets. To start, I have the following code. What I would like to do is change my variable "self.dynamiclength" to any integer, and have WxPython update the number of widgets appropriately. I have tried putting self.Refresh() and self.Update() in my TestFrame after updating self.dynamiclength to no avail.
I have done as much reading as possible on this before resorting to asking for help, but I am just too new at Wx to solve this one on my own. Thank you much!
import wx
import wx.lib.scrolledpanel as scrolled
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, size=(1000, 550))
panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
pbox0 = wx.BoxSizer(wx.VERTICAL)
controlback0 = wx.Button(panel, label="Back0")
controlforward0 = wx.Button(panel, label="Forward0")
pbox0.Add(controlback0, 0, wx.ALL)
pbox0.Add(controlforward0, 0, wx.ALL)
mainSizer.Add(pbox0)
self.scrolling_window = scrolled.ScrolledPanel( panel )
self.scrolling_window.SetAutoLayout(1)
self.scrolling_window.SetupScrolling()
self.sizer = wx.BoxSizer( wx.VERTICAL )
self.child_windows = []
##############################################
#this is the variable that I want to change,
#and I don't know how to get the 'for loop'
#below to update as well.
self.eedictionary = {}
self.dynamiclength = 5
for i in range(0,self.dynamiclength):
wind = self.addBox(i)
self.sizer.Add(wind, 0, wx.CENTER|wx.ALL, 5)
###############################################
#the following code binds all appropriate buttons to a pedigree variable updater
button_binding_list = ['controlback','controlforward']
for j in button_binding_list:
eid = self.eedictionary[str(i)+j]
self.scrolling_window.Bind(wx.EVT_BUTTON, lambda evt: self.onclick(evt, id), id=eid)
self.scrolling_window.SetSizer(self.sizer)
mainSizer.Add(self.scrolling_window, 1, wx.EXPAND)
panel.SetSizer(mainSizer)
def addBox(self, i):
pbox = wx.BoxSizer(wx.VERTICAL)
controlback = wx.Button(self.scrolling_window, label="Back")
controlforward = wx.Button(self.scrolling_window, label="Forward")
pbox.AddMany([(controlback, 0, wx.ALL), (controlforward, 0, wx.ALL)])
#for each object created in the addBox module, its id is added to the dictionary
self.eedictionary[str(i)+'controlback'] = controlback.GetId()
self.eedictionary[str(i)+'controlforward'] = controlforward.GetId()
return pbox
def onclick(self, event):
self.dynamiclength +=1
print 'added one to self.dynamiclength', self.dynamiclength
if __name__=='__main__':
app = wx.App(False)
f = TestFrame()
f.Show()
app.MainLoop()
I have similar test code which I have written some time ago. Maybe you will find it useful.
import wx
#===================================================================================================
class UpperPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.combo = wx.ComboBox(self, choices=["0", "1", "2", "3", "4"], size=(200, -1))
self.combo.Bind(wx.EVT_COMBOBOX, self.GetParent().middlePanel.Change)
self.logo = wx.Button(self, size=(300, 100))
self.sizer = wx.BoxSizer()
self.sizer.Add(self.combo, 0, wx.EXPAND)
self.sizer.Add(self.logo, 0, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
#===================================================================================================
class MiddlePanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.subs = []
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizerAndFit(self.sizer)
def Change(self, e):
self.sizer = wx.BoxSizer(wx.VERTICAL)
for a in self.subs:
a.Destroy()
self.subs = []
for a in range(int(e.GetString())):
b = wx.Button(self, size=(-1, 50))
self.subs.append(b)
self.sizer.Add(b, 1, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
self.GetParent().Fit()
#===================================================================================================
class MainWin(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.middlePanel = MiddlePanel(self)
self.upperPanel = UpperPanel(self)
self.textArea = wx.TextCtrl(self, size=(-1, 300), style=wx.TE_MULTILINE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.upperPanel, 0, wx.EXPAND)
self.sizer.Add(self.middlePanel, 0, wx.EXPAND)
self.sizer.Add(self.textArea, 1, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
#===================================================================================================
if __name__ == '__main__':
app = wx.PySimpleApp()
main_win = MainWin()
main_win.Show()
app.MainLoop()
If you need to update the number of widgets AFTER you've already created and shown the application, the you'll need to do it in a method, NOT in the init. The init only runs the first time the application is instantiated. Whenever you add or remove widgets after the frame is shown, you'll need to call Layout() on the parent widget or its sizer. See also
http://wxpython-users.1045709.n5.nabble.com/dynamically-adding-amp-removing-widgets-td2342432.html
https://groups.google.com/forum/?fromgroups#!topic/wxPython-users/eQjlYlsw4qs
Adding a widget with a button - wxPython

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