I am creating an application by using the language wxPython.
I have a simple problem in which I cant really find the solution in the internet.
I have a main user interface with a menubar which contain a menu called new file.
By clicking the new file, a new window will appear demanding the user to fill up the necessary information.
The problem is that, by clicking multiple times the menu (new file), the application opens multiple windows.
How can i prevent this?
The following code creates a new sub frame if one doesn't exists already.
If it does exist already, it uses the existing sub frame.
Note the code is tested with latest wxpython phoenix and classic.
import wx
from wx.lib import sized_controls
class MultiMessageFrame(sized_controls.SizedFrame):
def __init__(self, *args, **kwargs):
super(MultiMessageFrame, self).__init__(*args, **kwargs)
pane = self.GetContentsPane()
text_ctrl = wx.TextCtrl(
pane, style=wx.TE_READONLY | wx.TE_CENTRE | wx.TE_MULTILINE)
text_ctrl.SetSizerProps(proportion=1, expand=True)
text_ctrl.SetBackgroundColour('White')
self.text_ctrl = text_ctrl
pane_btns = sized_controls.SizedPanel(pane)
pane_btns.SetSizerType('horizontal')
pane_btns.SetSizerProps(align='center')
button_ok = wx.Button(pane_btns, wx.ID_OK)
button_ok.Bind(wx.EVT_BUTTON, self.on_button_ok)
def append_msg(self, title_text, msg_text):
self.SetTitle(title_text)
self.text_ctrl.AppendText(msg_text)
def on_button_ok(self, event):
self.Close()
class MainFrame(sized_controls.SizedFrame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(*args, **kwargs)
self.SetInitialSize((800, 600))
self.CreateStatusBar()
menubar = wx.MenuBar()
self.SetMenuBar(menubar)
menu_file = wx.Menu()
menu_file.Append(
wx.ID_NEW, 'Show msg', 'Add a new message to message frame')
menubar.Append(menu_file, '&File')
self.Bind(wx.EVT_MENU, self.on_new, id=wx.ID_NEW)
self.count = 1
self.multi_message_frame = None
def on_new(self, event):
title_text = 'MultiMessageFrame already exists'
if not self.multi_message_frame:
title_text = 'Newly created MultiMessageFrame'
self.multi_message_frame = MultiMessageFrame(
self, style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)
self.multi_message_frame.Bind(
wx.EVT_CLOSE, self.on_multi_message_frame_close)
self.multi_message_frame.Center()
self.multi_message_frame.Show()
self.multi_message_frame.append_msg(
title_text, 'message no.{}\n'.format(self.count))
self.count += 1
def on_multi_message_frame_close(self, event):
self.multi_message_frame = None
event.Skip()
if __name__ == '__main__':
app = wx.App(False)
main_frame = MainFrame(None)
main_frame.Show()
app.MainLoop()
Related
I'm trying to find a way to hide the menubar when the mouse isnt over it, i know i could get the mouse xy position and check if its in a certain area but im having no luck hiding the menubar itself.
My code:
class HelloFrame(wx.Frame):
images = []
def __init__(self, *args, **kw):
# ensure the parent's __init__ is called
super(HelloFrame, self).__init__(*args, **kw)
# create a panel in the frame
pnl = wx.Panel(self)
# create a menu bar
self.makeMenuBar()
# and a status bar
#self.CreateStatusBar()
#self.SetStatusText("Welcome to wxPython!")
def makeMenuBar(self):
fileMenu = wx.Menu()
helloItem = fileMenu.Append(-1, "&Load Backround Image")
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT)
helpMenu = wx.Menu()
aboutItem = helpMenu.Append(wx.ID_ABOUT)
menuBar = wx.MenuBar()
menuBar.Append(fileMenu, "&File")
#menuBar.Append(OptionsMenu, "&Options") TBM
menuBar.Append(helpMenu, "&Help")
# Give the menu bar to the frame
self.SetMenuBar(menuBar)
# Finally, associate a handler function with the EVT_MENU event for
# each of the menu items. That means that when that menu item is
# activated then the associated handler function will be called.
self.Bind(wx.EVT_MENU, self.OpenImage, helloItem)
self.Bind(wx.EVT_MENU, self.OnExit, exitItem)
self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)
def OnExit(self, event):
self.Close(True)
def OnAbout(self, event):
wx.MessageBox("Test",
"About",
wx.OK|wx.ICON_INFORMATION)
def OpenImage(self, event):
with wx.FileDialog (None, "Choose image", wildcard="PNG files (*.png)|*.png", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
else:
# Gets rid of other images
for i in self.images:
i.Destroy()
self.images.remove(i)
# Displays image and adds image object to [self.images]
ImageName = fileDialog.GetPath()
png = wx.Image(ImageName, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.images.append(wx.StaticBitmap(self, -1, png, (0, 0), (png.GetWidth(), png.GetHeight())))
print(ImageName)
if __name__ == '__main__':
app = wx.App()
frm = HelloFrame(None, title='FileHeader Test')
frm.Show()
app.MainLoop()
ive tried just adding menubar.Hide() in the function makemenubar but to no avail
ive also tried self.Hide() in the same area but no luck
I should start by saying that I don't recommend that you do this.
It's non-standard and could confuse.
It's expensive cpu-wise for no obvious benefit.
That said, here is one way of doing it, using Show, Hide and EVT_MOTION.
The issue is that once it is hidden/removed, the next mouse OVER doesn't happen because it is no longer there. As you suppose, we can assign a territorial range where we suppose it would be. If the cursor strays into that zone, the menu can be shown or hidden.
import wx
class HelloFrame(wx.Frame):
images = []
def __init__(self, *args, **kw):
# ensure the parent's __init__ is called
super(HelloFrame, self).__init__(*args, **kw)
# create a panel in the frame
pnl = wx.Panel(self)
pnl2 = wx.Panel(self)
pnl.SetBackgroundColour("green")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(pnl,1, wx.EXPAND,10)
sizer.Add(pnl2,1, wx.EXPAND,10)
self.SetSizer(sizer)
# create a menu bar
self.makeMenuBar()
# and a status bar
#self.CreateStatusBar()
#self.SetStatusText("Welcome to wxPython!")
pnl.Bind(wx.EVT_MOTION, self.MenuPos)
def makeMenuBar(self):
fileMenu = wx.Menu()
helloItem = fileMenu.Append(-1, "&Load Backround Image")
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT)
helpMenu = wx.Menu()
aboutItem = helpMenu.Append(wx.ID_ABOUT)
self.menuBar = wx.MenuBar()
self.menuBar.Append(fileMenu, "&File")
self.menuBar.Append(helpMenu, "&Help")
# Give the menu bar to the frame
self.SetMenuBar(self.menuBar)
self.menuBar.Hide()
# Finally, associate a handler function with the EVT_MENU event for
# each of the menu items. That means that when that menu item is
# activated then the associated handler function will be called.
self.Bind(wx.EVT_MENU, self.OpenImage, helloItem)
self.Bind(wx.EVT_MENU, self.OnExit, exitItem)
self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)
def MenuPos(self, event):
x, y = event.GetPosition()
if y <= 2:
self.menuBar.Show()
if y >= 3:
self.menuBar.Hide()
event.Skip()
def OnExit(self, event):
self.Close(True)
def OnAbout(self, event):
wx.MessageBox("Test",
"About",
wx.OK|wx.ICON_INFORMATION)
def OpenImage(self, event):
with wx.FileDialog (None, "Choose image", wildcard="PNG files (*.png)|*.png", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
else:
# Gets rid of other images
for i in self.images:
i.Destroy()
self.images.remove(i)
# Displays image and adds image object to [self.images]
ImageName = fileDialog.GetPath()
png = wx.Image(ImageName, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.images.append(wx.StaticBitmap(self, -1, png, (0, 0), (png.GetWidth(), png.GetHeight())))
print(ImageName)
if __name__ == '__main__':
app = wx.App()
frm = HelloFrame(None, title='FileHeader Test')
frm.Show()
app.MainLoop()
I would like to have two (I will add more later) panels that occupy the same space within the frame and for them to be shown/hidden when the respective button is pressed on the toolbar, "mListPanel" should be the default. Currently the settings panel is shown when the application is launched and the buttons don't do anything. I've searched and tried lots of stuff for hours and still can't get it to work. I apologise if it's something simple, I've only started learning python today.
This is what the code looks like now:
import wx
class mListPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
#wx.StaticText(self, -1, label='Search:')#, pos=(10, 3))
#wx.TextCtrl(self, pos=(10, 10), size=(250, 50))
class settingsPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
class bifr(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Title")
self.listPanel = mListPanel(self)
self.optPanel = settingsPanel(self)
menuBar = wx.MenuBar()
fileButton = wx.Menu()
importItem = wx.Menu()
fileButton.AppendMenu(wx.ID_ADD, 'Add M', importItem)
importItem.Append(wx.ID_ANY, 'Import from computer')
importItem.Append(wx.ID_ANY, 'Import from the internet')
exitItem = fileButton.Append(wx.ID_EXIT, 'Exit')
menuBar.Append(fileButton, 'File')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.Quit, exitItem)
toolBar = self.CreateToolBar()
homeToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Home', wx.Bitmap('icons/home_icon&32.png'))
importLocalToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Import from computer', wx.Bitmap('icons/comp_icon&32.png'))
importToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Import from the internet', wx.Bitmap('icons/arrow_bottom_icon&32.png'))
settingsToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'settings', wx.Bitmap('icons/wrench_plus_2_icon&32.png'))
toolBar.Realize()
self.Bind(wx.EVT_TOOL, self.switchPanels(), settingsToolButton)
self.Bind(wx.EVT_TOOL, self.switchPanels(), homeToolButton)
self.Layout()
def switchPanels(self):
if self.optPanel.IsShown():
self.optPanel.Hide()
self.listPanel.Show()
self.SetTitle("Home")
elif self.listPanel.IsShown():
self.listPanel.Hide()
self.optPanel.Show()
self.SetTitle("Settings")
else:
self.SetTitle("Error")
self.Layout()
def Quit(self, e):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = bifr()
frame.Show()
app.MainLoop()
first off, i would highly suggest that you learn about wxpython sizers and get a good understanding of them (they're really not that hard the understand) as soon as possible before delving deeper into wxpython, just a friendly tip :).
as for your example, a few things:
when your'e not using sizers, you have to give size and position for every window or else they just wont show, so you'd have to change your panel classes to something like this (again this is only for demonstration, you should be doing this with wx.sizers, and not position and size):
class mListPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,pos=(0,100),size=(500,500))
class settingsPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,pos=(0,200),size (1000,1000))
further more, when binding an event it should look like this:
self.Bind(wx.EVT_TOOL, self.switchPanels, settingsToolButton)
self.Bind(wx.EVT_TOOL, self.switchPanels, homeToolButton)
notice how I've written only the name of the function without the added (), as an event is passed to it, you cant enter your own parameters to a function emitted from an event (unless you do it with the following syntax lambda e:FooEventHandler(paramaters))
and the event handler (function) should look like this:
def switchPanels(self, event):
if self.optPanel.IsShown():
self.optPanel.Hide()
self.listPanel.Show()
self.SetTitle("Home")
elif self.listPanel.IsShown():
self.listPanel.Hide()
self.optPanel.Show()
self.SetTitle("Settings")
else:
self.SetTitle("Error")
self.Layout()
there should always be a second parameter next to self in functions that are bind to event as the event object is passes there, and you can find its associated methods and parameters in the documentation (in this example it is the wx.EVT_TOOL).
I am programming an application using wxpython and wx.lib.pubsub. in python 2.7.3
1- There is a Frame with a menu item. When this menu is clicked, a message is published by pubsub.
2- This message destroys (if possible) and creates a "first level" dialogue.
3- "First Level" dialogue has an list of valules and an "add value" button. (NOTE: Such list of variables can be modified so I am trying to update this list)
4- When the "add value" button is clicked, another message is published by pubsub.
5- This message creates a "Second Level" dialogue, so a new name for the new variable can be written.
6- There is a "continue" button in this "second level" dialogue which has two consequences:
First one: Self.Destroy();
Second one: goes to step 2, i.e. destroys the "first level" dialogue and creates it again.
To that point the program seems to work fine, however, when I finish "adding" variables to the "first level" dialogue I Destroy it and then I cannot go back to the main Frame stated in step 1.
Why is this happening?
All the Dialogues are shown via ShowModal(). However if I use only Show() it seems to work fine but, since the program has many menus and items, ShowModal() is preferred.
Any idea why it works with Show() but not with ShowModal()?
If there is a simpler way to perform the task I want to do, it would be appreciated.
import wx
from wx.lib.pubsub import Publisher as pub
class itemReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'show.dialog')
def __OnShowDialog(self, message):
self.dlgParent = message.data[0]
print str(self.dlgParent)
self.valuesToShow = message.data[1]
print self.valuesToShow
#try to destroy dialog before creating a new one
try:
self.manageParametersDialog.Destroy()
except:
pass
self.manageParametersDialog = manageParamsDialog(self.dlgParent, self.valuesToShow)
print "ready to show first level dialogue"
self.manageParametersDialog.ShowModal() #if .Show() instead, there is no problem
class secondaryReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'add.item')
def __OnShowDialog(self, message):
dlgParent = message.data[0]
dlgGrandParent = message.data[1]
self.variableList = message.data[2]
editParameterDialog = editParamDlg(dlgParent, dlgGrandParent, self.variableList)
editParameterDialog.ShowModal()
class manageParamsDialog (wx.Dialog):
def __init__(self, parent, valueList):
self.valueList = valueList
self.parent = parent
wx.Dialog.__init__(self, parent, -1, "first level dialogue", style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
sizer=wx.BoxSizer(wx.VERTICAL)
self.optionList = wx.ListBox(self, -1, size=(200, 70), choices = valueList)
sizer.Add(self.optionList)
addButton = wx.Button(self, -1, 'Add New')
self.Bind(wx.EVT_BUTTON, self.OnButton, addButton)
sizer.Add(addButton)
cancelButton = wx.Button(self, -1, 'Cancel')
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelButton)
sizer.Add(cancelButton)
self.SetSizer(sizer)
self.Fit()
def OnButton (self, e):
pub.sendMessage('add.item', [self, self.parent, self.valueList])
def OnCancel(self,e):
self.Destroy()
class editParamDlg(wx.Dialog):
def __init__(self, parent, grandParent, variableList):
self.variableList = variableList
self.grandParent = grandParent
wx.Dialog.__init__(self, parent, -1, "second level dialogue", style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
hboxSizer = wx.BoxSizer(wx.HORIZONTAL)
self.textInput = wx.TextCtrl(self, -1)
hboxSizer.Add(self.textInput)
addButton = wx.Button(self, -1, 'Continue')
self.Bind(wx.EVT_BUTTON, self.OnAdd, addButton)
hboxSizer.Add(addButton)
cancelButton = wx.Button(self, -1, 'Cancel')
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelButton)
hboxSizer.Add(cancelButton)
self.SetSizer(hboxSizer)
self.Fit()
def OnAdd(self, e):
self.variableList.append(self.textInput.GetValue())
self.Destroy()
pub.sendMessage('show.dialog',[self.grandParent, self.variableList])
def OnCancel(self,e):
self.Destroy()
class ToolbarFrame(wx.Frame):
#this ToolbarFrame is the main window, with a Toolbar and a white panel below.
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "this is a frame", size=(480, 320))
myPanel = wx.Panel(self)
myPanel.SetBackgroundColour("White")
menuBar = wx.MenuBar()
fileMenu = wx.Menu()
menuItem = wx.MenuItem(fileMenu, -1, "menu item", "opens dialog via pubsub")
self.Bind(wx.EVT_MENU, self.OnMenuItem, menuItem)
fileMenu.AppendItem(menuItem)
menuBar.Append(fileMenu, "File")
self.SetMenuBar(menuBar)
def OnMenuItem(self, e):
pub.sendMessage('show.dialog', [self, ["one", "two", "three"]])
app = wx.PySimpleApp()
frame = ToolbarFrame(parent=None, id=-1)
frame.Show()
newItemListener = itemReceiver()
editParameterListener = secondaryReceiver()
app.MainLoop()
try changing secondaryReciever as follows
class secondaryReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'add.item')
def __OnShowDialog(self, message):
dlgParent = message.data[0]
dlgGrandParent = message.data[1]
self.variableList = message.data[2]
editParameterDialog = editParamDlg(dlgParent, dlgGrandParent, self.variableList)
editParameterDialog.ShowModal()
#this line will not execute till the dialog closes
self.dlgParent.optionList.SetItems(editParameterDialog.variableList)
editParameterDialog.Destroy()
and also change editParamDlg
def OnAdd(self, e):
self.variableList.append(self.textInput.GetValue())
self.Close()
the problem was that you would call the show.modal from that OnAdd ... which would try to destroy the existing window and then open a new one... but the old one wasnt destroyed ... this left weird remnants that caused you errors ... and really all you want to do is update the item list ...
I am trying to debug this code, the Frame doesn't close on pressing the 'x' button, however when I comment out the wxTimer I am able to close it. Upon googling I found this - http://wiki.wxpython.org/Timer and I tried to bind an event to the top level window however the onClose function is never called on pressing the 'x' button.
Any suggestions?
class matplotsink(wx.Panel):
def __init__(self, parent, title, queue):
# wx.Frame.__init__(self, parent, -1, title)
wx.Panel.__init__(self, parent, wx.SIMPLE_BORDER)
#self.datagen = DataGen()
#self.data = self.datagen.next()
self.data = []
self.parent = parent
self.title = title
self.queue = queue
self.paused = False
#self.create_menu()
#self.create_status_bar()
self.toplevelcontainer = wx.GetApp().GetTopWindow()
self.toplevelcontainer.CreateStatusBar()
print 'Hey'
# menuBar = wx.MenuBar()
# fileMenu = wx.Menu()
# fileMenu.Append(wx.ID_NEW, '&New')
# fileMenu.Append(wx.ID_OPEN, '&Open')
# fileMenu.Append(wx.ID_SAVE, '&Save')
# menuBar.Append(fileMenu, '&File')
# self.toplevelcontainer.SetMenuBar(menuBar)
self.toplevelcontainer.Bind(wx.EVT_CLOSE, self.onCloseFrame)
self.create_main_panel()
self.redraw_timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.draw_callback, self.redraw_timer)
self.redraw_timer.Start(100)
def onCloseFrame(self,event):
print 'Hey1'
self.redraw_timer.Stop()
self.toplevelcontainer.Destroy()
self.toplevelcontainer.Close()
I cannot see that onCLoseFrame() does not get called. Quite to the contrary, self.toplevelcontainer.Destroy() retriggers EVT_CLOSE ad infinitum until the maximum recursion depth is reached. This is the reason self.toplevelcontainer never gets closed.
Instead of trying to destroy the top level window yourself, let the event handler do its job by skipping after you are done with the cleanup:
def onCloseFrame(self, event):
# ...
self.redraw_timer.Stop() #cleanup, important!
event.Skip()
You can check this stackoverflow answer (the link in the answer) for an explanation. As I have seen, the particular wxPython-wiki entry for wx.Timer is not very useful.
I am creating a menu and assigning images to menu items, sometime first item in menu doesn't display any image, I am not able to find the reason. I have tried to make a simple stand alone example and below is the code which does demonstrates the problem on my machine.
I am using windows XP, wx 2.8.7.1 (msw-unicode)'
import wx
def getBmp():
bmp = wx.EmptyBitmap(16,16)
return bmp
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, style=wx.DEFAULT_FRAME_STYLE, parent=None)
self.SetTitle("why New has no image?")
menuBar = wx.MenuBar()
fileMenu=wx.Menu()
item = fileMenu.Append(wx.ID_NEW, "New")
item.SetBitmap(getBmp())
item = fileMenu.Append(wx.ID_OPEN, "Open")
item.SetBitmap(getBmp())
item = fileMenu.Append(wx.ID_SAVE, "Save")
item.SetBitmap(getBmp())
menuBar.Append(fileMenu, "File")
self.SetMenuBar(menuBar)
app = wx.PySimpleApp()
frame=MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()
So are you able to see the problem and what could be the reason for it?
Conclusion: Yes this is a official bug, I have created a simple Menu class to overcome this bug, using the trick given by "balpha" in selected answer
It overrides each menu.Append method and sees if menu item with image is being added for first time, if yes creates a dummy item and deletes it later.
This also adds feature/constraint so that instead of calling SetBitmap, you should pass bitmap as optional argument image
import wx
class MockMenu(wx.Menu):
"""
A custom menu class in which image param can be passed to each Append method
it also takes care of bug http://trac.wxwidgets.org/ticket/4011
"""
def __init__(self, *args, **kwargs):
wx.Menu.__init__(self, *args, **kwargs)
self._count = 0
def applyBmp(self, unboundMethod, *args, **kwargs):
"""
there is a bug in wxPython so that it will not display first item bitmap
http://trac.wxwidgets.org/ticket/4011
so we keep track and add a dummy before it and delete it after words
may not work if menu has only one item
"""
bmp = None
if 'image' in kwargs:
bmp = kwargs['image']
tempitem = None
# add temp item so it is first item with bmp
if bmp and self._count == 1:
tempitem = wx.Menu.Append(self, -1,"HACK")
tempitem.SetBitmap(bmp)
ret = unboundMethod(self, *args, **kwargs)
if bmp:
ret.SetBitmap(bmp)
# delete temp item
if tempitem is not None:
self.Remove(tempitem.GetId())
self._lastRet = ret
return ret
def Append(self, *args, **kwargs):
return self.applyBmp(wx.Menu.Append, *args, **kwargs)
def AppendCheckItem(self, *args, **kwargs):
return self.applyBmp(wx.Menu.AppendCheckItem, *args, **kwargs)
def AppendMenu(self, *args, **kwargs):
return self.applyBmp(wx.Menu.AppendMenu, *args, **kwargs)
This hack does not appear to be necessary if you create each menu item with wx.MenuItem(), set its bitmap, and only then append it to the menu. This causes the bitmaps to show up correctly. I'm testing with wxPython 2.8.10.1 on Windows.
This is a confirmed bug which appearently has been open for quite a while. After trying around a little bit, this workaround seems to do it:
menuBar = wx.MenuBar()
fileMenu=wx.Menu()
tempitem = fileMenu.Append(-1,"X") # !!!
tempitem.SetBitmap(getBmp()) # !!!
item = fileMenu.Append(wx.ID_NEW, "New")
fileMenu.Remove(tempitem.GetId()) # !!!
item.SetBitmap(getBmp())
item = fileMenu.Append(wx.ID_OPEN, "Open")
item.SetBitmap(getBmp())
item = fileMenu.Append(wx.ID_SAVE, "Save")
item.SetBitmap(getBmp())
menuBar.Append(fileMenu, "File")
self.SetMenuBar(menuBar)
Note that the position of the fileMenu.Remove call is the earliest position that works, but you can also move it to the bottom. HTH.