wxPython: Populate menu in sub panel - python

I have a wx application where the main frame has several sub panels. I'd like to have a menu bar in the main frame, where each of the menus is associated with a panel. This means that the creation of menu items and binding them to event handlers should be done in the individual panels and not in the main frame. Here is a minimal example:
import wx
class myPanel1(wx.Panel):
def __init__(self, parent, menubar):
super().__init__(parent=parent)
menu = wx.Menu()
menuAction1 = menu.Append(wx.ID_ANY, 'Action1')
menuAction2 = menu.Append(wx.ID_ANY, 'Action2')
menubar.Append(menu, '&Actions')
# This does not work because the EVT_MENU is only seen by the main frame(?)
self.Bind(wx.EVT_MENU, self.onAction1, menuAction1)
self.Bind(wx.EVT_MENU, self.onAction2, menuAction2)
def onAction1(self, event):
print('Hello1')
def onAction2(self, event):
print('Hello2')
class mainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.menubar = wx.MenuBar()
# There are more panels in my actual program
self.panel1 = myPanel1(self, self.menubar)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.panel1, flag=wx.EXPAND, proportion=1)
self.SetSizerAndFit(sizer)
self.SetMenuBar(self.menubar)
self.Layout()
class myApp(wx.App):
def OnInit(self):
frame = mainWindow(parent=None, title='Title')
self.SetTopWindow(frame)
frame.Show()
return True
if __name__ == '__main__':
app = myApp()
app.MainLoop()
The problem is now that myPanel1.onAction1 is not called because the menu event from the main frame does not propagate to the sub panel.
Is there any neat way to do this?

Meanwhile I've found the answer myself. It's as simple as changing
self.Bind(wx.EVT_MENU, self.onAction1, menuAction1)
in myPanel1.__init__ to
self.GetParent().Bind(wx.EVT_MENU, self.onAction1, menuAction1)
Anyway, thanks everyone who thought about this question for their effort.

Related

How to link multiple wx.Dialogs in wxPython

I want to make a game in wxPython (no other modules) and I want to make it so that you can enter some values in popup screens before the game starts, and then the game will be drawn on a canvas which in turn is drawn on a panel, which is bound to the main game.
I made the gamescreen with all fancy stuff (works solo)
I made the input screens
But I cannot link them.
How do I start the game so it will open a dialog box, then on the closure of it open another one, and then open the game ?
I tried the following, but it will not open my canvas:
# makes a game by showing 2 dialogs
# after dialogs have been answered, starts the game by drawing the canvas.
# imports
import wx
import Speelveld3
# globals
SCRWIDTH = 950
SCRHEIGHT = 700
# dialogbox class
class MyDialog1(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent)
self.username = wx.TextCtrl(self)
self.okButton = wx.Button(self, wx.ID_OK, "OK")
class MyDialog2(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent)
self.canvasWidth = wx.TextCtrl(self)
self.okButton = wx.Button(self, wx.ID_OK, "OK")
# main class
class Game(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, title='My game', size=(SCRWIDTH, SCRHEIGHT))
self.username = ""
self.canvasWidth = 10
# hide the frame for now
self.Hide()
def OnInit(self):
#Make your dialogs
dlg1 = MyDialog1(self)
#if the user pressed "OK" (i.e. NOT "Cancel" or any other button you might add)
if dlg1.ShowModal() == wx.ID_OK:
#get the username from the dialog
self.username = dlg1.username.GetValue()
#clean up the dialog (AFTER you get the username)
dlg1.Destroy()
dlg2 = MyDialog2(self)
#if the user pressed "OK" (i.e. NOT "Cancel" or any other button you might add)
if dlg2.ShowModal() == wx.ID_OK:
#get the username from the dialog
self.canvasWidth = dlg2.canvasWidth.GetValue()
#clean up the dialog (AFTER you get the username)
dlg2.Destroy()
# Now that you have your settings, Make the gameboard
# THIS PART IS STILL BROKEN!
# I can paste the whole board class (structure of it is taken from the tetris tutorial)
# but that seems a bit much tbh...
self.gameBoard = Board.Board(self)
self.gameBoard = SetFocus()
self.gameBoard.start()
self.Centre()
self.Show(True) #show the frame
if __name__ == '__main__':
# how can I start the game here?
app = wx.App()
frame = Game()
board = Speelveld3.Speelveld(frame)
board.start()
frame.Show()
app.MainLoop()
You've double posted, and the lack of any wx.Dialog in your sample code suggests to me that you haven't even looked at a tutorial yet, but I will give you the benefit of the doubt.
First, if you want to return information from a dialog, the easiest way is to define a custom dialog. Define a new class that inherits from wx.Dialog and then set it up just like you would a normal panel or a frame. It seems to me that you will need two of these. They'll look something like this:
class MyDialog1(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent)
self.username = wx.TextCtrl(self) #this is where users will enter their username
self.okButton = wx.Button(self, wx.ID_OK, "OK") #Note that I'm using wx.ID_OK. This is important
Now, for the logic you want. Pretty much every object in wxPython that you actually see has the functions Show() and Hide() (API here). You don't want to show your frame until AFTER the dialogs are finished, so in your __init__(), call Hide(). I'm also initializing a variable, username, which is where I will store the data from my dialog.
class Game(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(SCRWIDTH, SCRHEIGHT))
self.username = ""
self.Hide() #don't show the frame just yet
#self.Hide() is the exact same as self.Show(False)
Now, for your dialogs. Like Mike Driscoll suggested, you call your dialogs BEFORE making your canvas. wx.Dialogs are launched using ShowModal(). By setting the ID of self.okButton to the constant wx.ID_OK, wxPython recognizes that the dialog should be closed after the button in clicked. You should also be aware of wx.ID_CANCEL.
def OnInit(self):
#Make your dialogs
dlg1 = MyDialog1(self)
if dlg1.ShowModal() == wx.ID_OK:
#if the user pressed "OK" (i.e. NOT "Cancel" or any other button you might add)
self.username = dlg1.username.GetValue() #get the username from the dialog
dlg1.Destroy() #clean up the dialog (AFTER you get the username)
#do this again for your second dialog
#Now that you have your settings, Make the gameboard
self.gameBoard = Board.Board(self)
self.gameBoard = SetFocus()
self.gameBoard.start()
self.Centre()
self.Show(True) #show the frame
In your OnInit you just need to call your dialogs and show them modally BEFORE you create your Board instance. Then it should work correctly.
EDIT (6-28-12): Here's some code:
import wx
########################################################################
class MyDlg(wx.Dialog):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Dialog.__init__(self, None, title="I'm a dialog!")
lbl = wx.StaticText(self, label="Hi from the panel's init!")
btn = wx.Button(self, id=wx.ID_OK, label="Close me")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(lbl, 0, wx.ALL, 5)
sizer.Add(btn, 0, wx.ALL, 5)
self.SetSizer(sizer)
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
# show a custom dialog
dlg = MyDlg()
dlg.ShowModal()
dlg.Destroy()
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
pdc = wx.PaintDC(self)
try:
dc = wx.GCDC(pdc)
except:
dc = pdc
rect = wx.Rect(0,0, 100, 100)
for RGB, pos in [((178, 34, 34), ( 50, 90)),
(( 35, 142, 35), (110, 150)),
(( 0, 0, 139), (170, 90))
]:
r, g, b = RGB
penclr = wx.Colour(r, g, b, wx.ALPHA_OPAQUE)
brushclr = wx.Colour(r, g, b, 128) # half transparent
dc.SetPen(wx.Pen(penclr))
dc.SetBrush(wx.Brush(brushclr))
rect.SetPosition(pos)
dc.DrawRoundedRectangleRect(rect, 8)
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Example frame")
# show a MessageDialog
style = wx.OK|wx.ICON_INFORMATION
dlg = wx.MessageDialog(parent=None,
message="Hello from the frame's init",
caption="Information", style=style)
dlg.ShowModal()
dlg.Destroy()
# create panel
panel = MyPanel(self)
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()

Confused on how to structure the GUI (wxpython)

I've gone from one book to another, one google search to another and I notice EVERY SINGLE ONE starts the main window in a completely different way.
I don't want to pick up bad habits so can someone please give me the best of these options and why its the better method. Below are all the ways i've seen it done
A)
class iFrame(wx.Frame):
def init(....):
wx.Frame._init_(...)
B)
class iFrame(wx.Frame):
def init(...):
super_init_(...)
C)
Then I see some that uses the Panel instead such as
class iPanel(wx.Panel)
def init(...):
wx.Panel.init(...)
D)
And even more confusing some are using the regular App class of wx
class iApp(wx.App):
def OnInit(self):
wx.Frame.init(...)
Forgive me if some of my structures are wrong but I'm recalling these off the top of my head, Question again...Which one of these, IF ANY is the best way to structure the GUI. It's hard following tutorials and books when they all do things in diff ways
edit: Sorry if format is not correct, but normally it works...
My favorite way to start wx application development is:
import wx
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.button = wx.Button(self.panel, label="Test")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.button)
self.panel.SetSizerAndFit(self.sizer)
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
See also this question, which is related.
Don't worry about it. You aren't going to ruin your future programming by making the wrong choice now.
None of the options you mention are wrong. They all do things differently because different applications have different requirements. No one way is best.
Just work on what you want and do what works for you, and once you have greater familiarity then you'll understand why different code does it differently.
I have learned the hard way that, as in every application, encapsulation is important. And peculiar to wxPython, the main frame object should have precisely one panel widget, plus optional menu bar, toolbar and status bar widgets. Nothing else.
This is my basic pattern for new wxPython applications:
(Updated 2019 Feb 07: Wx Phoenix and Python 3)
import wx
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(None, *args, **kwargs)
self.Title = 'Basic wxPython module'
self.SetMenuBar(MenuBar(self))
self.ToolBar = MainToolbar(self)
self.status_bar = StatusBar(self).status_bar
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(panel)
self.SetSizerAndFit(sizer)
self.Centre()
self.Show()
def on_quit_click(self, event):
"""Handle close event."""
del event
wx.CallAfter(self.Destroy)
class MainPanel(wx.Panel):
"""Panel class to contain frame widgets."""
def __init__(self, parent, *args, **kwargs):
super(MainPanel, self).__init__(parent, *args, **kwargs)
"""Create and populate main sizer."""
sizer = wx.BoxSizer(wx.VERTICAL)
cmd_quit = wx.Button(self, id=wx.ID_EXIT)
cmd_quit.Bind(wx.EVT_BUTTON, parent.on_quit_click)
sizer.Add(cmd_quit)
self.SetSizer(sizer)
class MenuBar(wx.MenuBar):
"""Create the menu bar."""
def __init__(self, parent, *args, **kwargs):
super(MenuBar, self).__init__(*args, **kwargs)
# File menu
file_menu = wx.Menu()
self.Append(file_menu, '&File')
quit_menu_item = wx.MenuItem(file_menu, wx.ID_EXIT)
parent.Bind(wx.EVT_MENU, parent.on_quit_click, id=wx.ID_EXIT)
file_menu.Append(quit_menu_item)
class MainToolbar(wx.ToolBar):
"""Create tool bar."""
def __init__(self, parent, *args, **kwargs):
super(MainToolbar, self).__init__(parent, *args, **kwargs)
#quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT)
#self.AddTool(wx.ID_EXIT, 'Quit', wx.Bitmap(quit_bmp))
#self.SetToolShortHelp(wx.ID_EXIT, 'Quit')
#self.Bind(wx.EVT_TOOL, parent.on_quit_click, id=wx.ID_EXIT)
#self.Realize()
class StatusBar(object):
def __init__(self, parent):
"""Create status bar."""
self.status_bar = parent.CreateStatusBar()
if __name__ == '__main__':
"""Run the application."""
screen_app = wx.App()
main_frame = MainFrame()
screen_app.MainLoop()
In response to a comment by XilyummY, I have added this additional answer to show how the main classes can be organised in separate files.
This is my solution based on four files:
main.py: the main frame for the application and the application loader;
main_panel.py: the main panel for the application;
menu_bar.py: the menu bar definition for the frame;
tool_bar.py: the tool bar from the frame.
The code follows in this order:
main.py
import wx
from main_panel import MainPanel
from menu_bar import MenuBar
from tool_bar import MainToolbar
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(None, *args, **kwargs)
self.Title = 'Basic wxPython module'
self.SetMenuBar(MenuBar(self))
self.ToolBar = MainToolbar(self)
self.status_bar = StatusBar(self).status_bar
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(panel)
self.SetSizerAndFit(sizer)
self.Centre()
self.Show()
def on_quit_click(self, event):
"""Handle close event."""
del event
wx.CallAfter(self.Destroy)
class StatusBar(object):
def __init__(self, parent):
"""Create status bar."""
self.status_bar = parent.CreateStatusBar()
if __name__ == '__main__':
"""Run the application."""
screen_app = wx.App()
main_frame = MainFrame()
screen_app.MainLoop()
main_panel.py
import wx
class MainPanel(wx.Panel):
"""Panel class to contain frame widgets."""
def __init__(self, parent, *args, **kwargs):
super(MainPanel, self).__init__(parent, *args, **kwargs)
"""Create and populate main sizer."""
sizer = wx.BoxSizer(wx.VERTICAL)
cmd_quit = wx.Button(self, id=wx.ID_EXIT)
cmd_quit.Bind(wx.EVT_BUTTON, parent.on_quit_click)
sizer.Add(cmd_quit)
self.SetSizer(sizer)
menu_bar.py
import wx
class MenuBar(wx.MenuBar):
"""Create the menu bar."""
def __init__(self, parent, *args, **kwargs):
super(MenuBar, self).__init__(*args, **kwargs)
# File menu
file_menu = wx.Menu()
self.Append(file_menu, '&File')
quit_menu_item = wx.MenuItem(file_menu, wx.ID_EXIT)
parent.Bind(wx.EVT_MENU, parent.on_quit_click, id=wx.ID_EXIT)
file_menu.Append(quit_menu_item)
tool_bar.py
import wx
class MainToolbar(wx.ToolBar):
"""Create tool bar."""
def __init__(self, parent, *args, **kwargs):
super(MainToolbar, self).__init__(parent, *args, **kwargs)
new_bmp = wx.ArtProvider.GetBitmap(wx.ART_NEW)
#preferences_bmp = wx.Bitmap('images/preferences.png')
quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT)
self.AddTool(wx.ID_NEW, 'New', new_bmp)
#self.AddTool(wx.ID_PREFERENCES, 'Preferences', preferences_bmp)
self.AddTool(wx.ID_EXIT, 'Quit', quit_bmp)
self.SetToolShortHelp(wx.ID_NEW, 'New ...')
self.SetToolShortHelp(wx.ID_PREFERENCES, 'Preferences ...')
self.SetToolShortHelp(wx.ID_EXIT, 'Quit')
self.Bind(wx.EVT_TOOL, parent.on_quit_click, id=wx.ID_EXIT)
self.Realize()

Creating ScrolledWindow in wxPython

I am trying to make a ScrolledWindow that can scroll over a grid of images, but the scrollbar isn't appearing. wxWidgets documentation says:
The most automatic and newest way [to set the scrollbars in wxScrolledWindow] is to simply let sizers determine the scrolling area. This is now the default when you set an interior sizer into a wxScrolledWindow with wxWindow::SetSizer. The scrolling area will be set to the size requested by the sizer and the scrollbars will be assigned for each orientation according to the need for them and the scrolling increment set by wxScrolledWindow::SetScrollRate
So I try to set the sizer of my ScrolledWindow with a GridSizer but it's not working. The code:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id=-1,title="",pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
name="frame"):
wx.Frame.__init__(self,parent,id,title,pos,size,style,name)
self.panel = wx.ScrolledWindow(self,wx.ID_ANY)
menuBar = wx.MenuBar()
menu1 = wx.Menu()
m = menu1.Append(wx.NewId(), "&Blah", "Show Pictures")
menuBar.Append(menu1,"&Blah")
self.Bind(wx.EVT_MENU,self.OnInit,m)
self.SetMenuBar(menuBar)
def OnInit(self, event):
sizer = wx.GridSizer(rows=7,cols=3)
filenames = []
for i in range(20):
filenames.append("img"+str(i)+".png")
for fn in filenames:
img = wx.Image(fn,wx.BITMAP_TYPE_ANY)
sizer.Add(wx.StaticBitmap(self.panel,wx.ID_ANY,wx.BitmapFromImage(img)))
self.panel.SetSizer(sizer)
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(parent=None,title="Frame")
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == "__main__":
app = MyApp()
app.MainLoop()
Insert this
self.panel.SetScrollbars(1, 1, 1, 1)
after self.panel = wx.ScrolledWindow(self,wx.ID_ANY)
If you want some info on the SetScrollBars method then look at this wxwidgets documentation page

wxPython: Good way to overlay a wx.Panel on an existing wx.Panel

I have a wx.Frame, in which there is a main wx.Panel with several widgets inside of it. I want one button in there to cause a "help panel" to come up. This help panel would probably be a wx.Panel, and I want it to overlay the entire main wx.Panel (not including the menu bar of the wx.Frame). There should be some sort of close button on the help button that will make it disappear again.
What is a good way to achieve this? I've looked into wx.Notebook but haven't found a way to make it not show the tabs.
Note that I don't want to destroy and recreate the help panel every time the user closes and opens it: I just want it to be hidden.
There are several ways
a) you can create a custom child panel, and make it same size and position at 0,0 among top of all child widgets. no need of destroying it just Show/Hide it
this also resizes with parent frame
b) popup a wx.PopupWindow or derived class and place and size it at correct location
so as suggest in a) here is an example, where all controls are put in panel using sizer, as separate help cntrl is created which can be shown/hidden from button, but you can create a custom cntrl which hides itself on clicking close
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self)
# create controls
self.cntrlPanel = wx.Panel(self.panel)
stc1 = wx.StaticText(self.cntrlPanel, label="wow it works")
stc2 = wx.StaticText(self.cntrlPanel, label="yes it works")
btn = wx.Button(self.cntrlPanel, label="help?")
btn.Bind(wx.EVT_BUTTON, self._onShowHelp)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(stc1)
sizer.Add(stc2)
sizer.Add(btn)
self.cntrlPanel.SetSizer(sizer)
# create help panel
self.helpPanel = wx.Panel(self.panel)
self.stcHelp = wx.StaticText(self.helpPanel, label="help help help\n"*8)
btn = wx.Button(self.helpPanel, label="close[x]")
btn.Bind(wx.EVT_BUTTON, self._onShowCntrls)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.stcHelp)
sizer.Add(btn)
self.helpPanel.SetSizer(sizer)
self.helpPanel.Hide()
self.helpPanel.Raise()
self.helpPanel.SetBackgroundColour((240,250,240))
self.Bind(wx.EVT_SIZE, self._onSize)
self._onShowCntrls(None)
def _onShowHelp(self, event):
self.helpPanel.SetPosition((0,0))
self.helpPanel.Show()
self.cntrlPanel.Hide()
def _onShowCntrls(self, event):
self.cntrlPanel.SetPosition((0,0))
self.helpPanel.Hide()
self.cntrlPanel.Show()
def _onSize(self, event):
event.Skip()
self.helpPanel.SetSize(self.GetClientSizeTuple())
self.cntrlPanel.SetSize(self.GetClientSizeTuple())
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()
Updated code for Python 3.9.4 and wxPython 4.1.1. Hope people find it useful. The original post was useful to me, but as a neophyte, it took me some effort to have it run properly with recently released tools.
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self)
# create controls
self.cntrlPanel = wx.Panel(self.panel)
stc1 = wx.StaticText(self.cntrlPanel, label="wow it works")
stc2 = wx.StaticText(self.cntrlPanel, label="yes it works")
btn = wx.Button(self.cntrlPanel, label="help?")
btn.Bind(wx.EVT_BUTTON, self._onShowHelp)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(stc1)
sizer.Add(stc2)
sizer.Add(btn)
self.cntrlPanel.SetSizer(sizer)
# create help panel
self.helpPanel = wx.Panel(self.panel)
self.stcHelp = wx.StaticText(self.helpPanel, label="help help help\n"*8)
btn = wx.Button(self.helpPanel, label="close[x]")
btn.Bind(wx.EVT_BUTTON, self._onShowCntrls)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.stcHelp)
sizer.Add(btn)
self.helpPanel.SetSizer(sizer)
self.helpPanel.Hide()
self.helpPanel.Raise()
self.helpPanel.SetBackgroundColour((240,250,240))
self.Bind(wx.EVT_SIZE, self._onSize)
self._onShowCntrls(None)
def _onShowHelp(self, event):
self.helpPanel.SetPosition((0,0))
self.helpPanel.Show()
self.cntrlPanel.Hide()
def _onShowCntrls(self, event):
self.cntrlPanel.SetPosition((0,0))
self.helpPanel.Hide()
self.cntrlPanel.Show()
def _onSize(self, event):
event.Skip()
self.helpPanel.SetSize(self.GetClientSize())
self.cntrlPanel.SetSize(self.GetClientSize())
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()

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