wxPython - How to get MenuItem from event? - python

Using wxPython, I have a frame setup with a menu and a status bar. The menu is generated from an indented text file, from which I use to create a nice nested menu bar with each menu item bound to a specific function. I have a "Toggle Status Bar" menu check item which is bound OnToggleStatusBar().
I would like to see if the menu item is checked or not and react accordingly, but I cannot seem to access the menuItem from the event. If I use GetId(), how can that be used to find the menu item? I tried event.GetId() with FindWindowById() but got nothing. I also tried event.GetEventObject(), which returned a menu but not a menu item.
def OnToggleStatusBar(self, event):
id = event.GetId()
menu = event.GetEventObject()
menuItem = menu.FindWindowById(id) #does not work
print self.FindByWindowId(id) # prints None

You don't need to find the item, you can use wxMenuBar::IsChecked(), which will do it for you, directly. And you can either just store the menu bar in self.menuBar or retrieve it from the frame using its GetMenuBar() method.

It's a bit convoluted, but not too bad. Basically you need to use the menu's FindItem method, which takes the string name of the menu item. This returns its id, which you can then use the menu's FindItemById method for. Here's a quick and dirty example:
import wx
########################################################################
class MyForm(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="wx.Menu Tutorial")
self.panel = wx.Panel(self, wx.ID_ANY)
# Create menu bar
menuBar = wx.MenuBar()
# create check menu
checkMenu = wx.Menu()
wgItem = checkMenu.Append(wx.NewId(), "Wells Fargo", "", wx.ITEM_CHECK)
self.Bind(wx.EVT_MENU, self.onFargo, wgItem)
citiItem = checkMenu.Append(wx.NewId(), "Citibank", "", wx.ITEM_CHECK)
geItem = checkMenu.Append(wx.NewId(), "GE Money Bank", "", wx.ITEM_CHECK)
menuBar.Append(checkMenu, "&Check")
# Attach menu bar to frame
self.SetMenuBar(menuBar)
#----------------------------------------------------------------------
def onFargo(self, event):
""""""
menu = event.GetEventObject()
item_id = menu.FindItem("Wells Fargo")
item = menu.FindItemById(item_id)
print item.IsChecked()
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()

Use the event to access the data you want.
print event.Id
print event.Selection # returns 1 if checked 0 if not checked
print event.IsChecked() # returns True if checked False if not checked
print out all of the attributes with:
print dir(event)

Related

How do I make menu options do different things in WxPython?

I have a problem with my menu in WxPython! In the file header I have 'File' and in that I have 2 options, 'Save' and 'Close'...When I click save, I want it to print "Saved" and when I clock quit, the application should print "Closed" and close...But if I click even save it's closing! So can you please help me. Here is a part of my code:
menubar = wx.MenuBar()
fileMenu = wx.Menu()
menubar.Append ( fileMenu, '&File' )
m1 = fileMenu.Append ( wx.ID_EXIT, 'Save' )
self.Bind ( wx.EVT_MENU, self.OnSave, m1 )
m2 = fileMenu.Append ( wx.ID_EXIT, 'Quit' )
self.Bind ( wx.EVT_MENU, self.OnQuit, m2 )
self.SetMenuBar ( menubar )
And the functions:
def OnSave ( self, event ):
text = self.text_ctrl.GetValue()
fil.write ( text )
print ( "Saved file")
def OnQuit ( self, event ):
print ( "Closed" )
self.Close()
You are using the same Id i.e. wx.ID_EXIT for both menu items, which as #Petr Blahos has pointed out, is causing your problem.
There is however an issue with Petr's answer, whilst technically correct,
wx.NewId is now Deprecated and although it still works, it has been replaced by wx.NewIdRef.
wx.NewId()
Generates an integer identifier unique to this run of the program.
Return type
int
Deprecated IDs generated by this function can possibly conflict with IDs used elsewhere in the application code. It is recommended to instead use the wx.ID_ANY ID to assign generated IDs for the controls, menu items and etc. that you create in the application. These IDs are guaranteed to not conflict with the other IDs that are in use in the application. For those cases where you need to create an ID that can be used more than once then please see wx.NewIdRef.
wx.NewIdRef(count=1)
Reserves a new Window ID (or range of WindowIDs) and returns a wx.WindowIDRef object (or list of them) that will help manage the reservation of that ID.
This function is intended to be a drop-in replacement of the old and deprecated wx.NewId function, with the added benefit that the ID should never conflict with an in-use ID or other IDs generated by this function.
wx.NewIdRef also has a count function, so it can be used to grab a group of id's that you use as required. i.e.
>>> myIds = wx.NewIdRef(count=6)
>>> myIds
[WindowIDRef: -31973, WindowIDRef: -31972, WindowIDRef: -31971, WindowIDRef: -31970, WindowIDRef: -31969, WindowIDRef: -31968]
>>> useId = myIds[3].Id
>>> useId
-31970
Note that wx.NewIdRef, wx.ID_ANY and -1 can all be used in this menu context. See this simple example:
import wx
class Test(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent,title="Frame aka Window",size = (300,200))
panel = wx.Panel(self)
self.status=self.CreateStatusBar()
self.status.SetStatusText("Status bar text")
menubar=wx.MenuBar()
firstm=wx.Menu()
secondm=wx.Menu()
fm1 = wx.MenuItem(firstm, wx.NewIdRef(), 'New Window\tAlt+N')
firstm.Append(fm1)
self.Bind(wx.EVT_MENU, self.OnMenu1, id=fm1.GetId())
fm2 = wx.MenuItem(firstm, wx.NewIdRef(), 'Open', "Text for the statusbar")
firstm.Append(fm2)
self.Bind(wx.EVT_MENU, self.OnMenu2, id=fm2.GetId())
fm3 = wx.MenuItem(firstm, -1, 'Quit\tAlt+Q')
firstm.Append(fm3)
self.Bind(wx.EVT_MENU, self.OnMenu3, id=fm3.GetId())
sm1 = wx.MenuItem(firstm, wx.ID_ANY, 'Re-Do', "Statusbar Re-Do")
secondm.Append(sm1)
self.Bind(wx.EVT_MENU, self.OnsMenu1, id=sm1.GetId())
sm2 = wx.MenuItem(secondm, wx.ID_ANY, 'Un-Do', "Statusbar Un-Do")
secondm.Append(sm2)
self.Bind(wx.EVT_MENU, self.OnsMenu2, id=sm2.GetId())
menubar.Append(firstm,"File")
menubar.Append(secondm,"Edit")
self.SetMenuBar(menubar)
t = wx.StaticText(panel,-1,"Hello i'm a test", pos=(10,20))
def OnMenu1(self, event):
print("Menu item 1",event.GetId())
def OnMenu2(self, event):
print("Menu item 2",event.GetId())
def OnMenu3(self, event):
print("Menu item 3 Quit",event.GetId())
self.Destroy()
def OnsMenu1(self, event):
print("2nd Menu item 1",event.GetId())
def OnsMenu2(self, event):
print("2nd Menu item 2",event.GetId())
if __name__=='__main__':
app=wx.App()
frame=Test(None)
frame.Show()
app.MainLoop()
The first parameter of fileMenu.Append is important. It is used to tell one menu item from the other one, but you used the same value for both.
m1 = fileMenu.Append(wx.ID_SAVE, 'Save')
self.Bind(wx.EVT_MENU, self.OnSave, m1)
m2 = fileMenu.Append(wx.ID_EXIT, 'Quit')
self.Bind(wx.EVT_MENU, self.OnQuit, m2)
In my programs I prefer getting a free id from system, using wx.NewId(). If you have these "stock" menu entries, ID_SAVE and ID_EXIT make perfect sense, but if you make your own entries, you can do:
m3 = fileMenu.Append(wx.NewId(), 'My own menu item')
self.Bind(wx.EVT_MENU, self.OnMyOwnFunction, m3)
m4 = fileMenu.Append (wx.NewId(), 'Another menu item')
self.Bind(wx.EVT_MENU, self.OnAnotherFunction, m4)

Change default field in a multi field statusbar in wxpython (to display menuitem help text)

In wxpython when we create a multi field statusbar, the first field is being used by default, to display the help texts of mouse hovered menu items.
for example in the code below:
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Multi field status bar.')
panel = wx.Panel(self)
self.statusbar = self.CreateStatusBar(2)
self.menubar = wx.MenuBar()
self.menuitem = wx.Menu()
self.SetMenuBar(self.menubar)
if True: # Utilitie IDs:
self.ID_new_utilitie = wx.NewId()
self.ID_delete_utilitie = wx.NewId()
if True: # Menu items:
self.menuitem.Append(self.ID_new_utilitie, "&New", "Create New Utilitie.")
self.menuitem.Append(self.ID_delete_utilitie, "&Delete", "Delete Selected Utilitie.")
self.menubar.Append(self.menuitem, '&Actions')
if True: # Utilitie Actions:
wx.EVT_MENU(self, self.ID_new_utilitie, self.Create_New_Utilitie)
wx.EVT_MENU(self, self.ID_delete_utilitie, self.Delete_Selected_Utilitie)
self.Show()
def Create_New_Utilitie(self, event):
pass
def Delete_Selected_Utilitie(self, event):
pass
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
Whenever we hover over a menu item, the help text gets displayed automatically in the 1st field (by default).
An image illustrating my question while running the example above.
Please note that I am not asking how to SetStatusText the 2nd field with help texts but how to change the default field for displaying them.
Thanks.
OK, I finally found the answer.
to change the default field in the statusbar (where menu help strings are shown by default) you need to catch them with EVT_MENU_HIGHLIGHT_ALL(func).
So by taking as an example the code in my question above, here are the changes:
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Multi field status bar.')
panel = wx.Panel(self)
self.statusbar = self.CreateStatusBar(2)
self.menubar = wx.MenuBar()
self.menuitem = wx.Menu()
self.SetMenuBar(self.menubar)
self.statusbar.SetStatusText('Nothing here!!!', 0)
if True: # Utilitie IDs:
self.ID_new_utilitie = wx.NewId()
self.ID_delete_utilitie = wx.NewId()
if True: # Menu items:
self.menuitem.Append(self.ID_new_utilitie, "&New", "Create New Utilitie.")
self.menuitem.Append(self.ID_delete_utilitie, "&Delete", "Delete Selected Utilitie.")
self.menubar.Append(self.menuitem, '&Actions')
if True: # Utilitie Actions:
wx.EVT_MENU(self, self.ID_new_utilitie, self.Create_New_Utilitie)
wx.EVT_MENU(self, self.ID_delete_utilitie, self.Delete_Selected_Utilitie)
self.Show()
"""The event catcher here."""
wx.EVT_MENU_HIGHLIGHT_ALL(self, self.statusbar_status)
def Create_New_Utilitie(self, event):
pass
def Delete_Selected_Utilitie(self, event):
pass
def statusbar_status(self, event):
"""Polemos: Change default statusbar field for showing menu help."""
event_catcher = event.GetId()
try: msg = self.menubar.GetHelpString(event_catcher)
except: pass
try: self.statusbar.SetStatusText(msg, 1)
except: self.statusbar.SetStatusText('', 1) # Resets.
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
More info in the links below:
wxMenuItem Class Reference
Events Emitted by this Class
wx.MenuBar Methods Summary

WxPython Web Browser Set Title

I have created a web browser in python using WxPython. I've been able to get everything to function, go forward/back, reload, ect. My only problem is I'd like to be able to set the title at the top of the program to the title of the web page. I understand how to set the title with self.SetTitle(title), I also know from some of the research I've done that you can get the tile of the page using self.browser.GetCurrentTitle(). Only problem with using that is that it's a one time thing that doesn't refresh it's self, when a user clicks a new link. I'm assuming there is some sort of function or something I can catch when a new page is loaded and tell python to do something like this:
def OnLoad(self, event):
self.webtitle = self.browser.GetCurrentTitle()
self.browser.SetTitle(self.webtitle)
Or something along those lines I'm just not sure where or how I can connect or "catch" that function. I have read through the documentation of wx.html2.WebView, but I'm not able to make sense of how to do this, I have also looked through this site as well as a few mailing lists but I can't seem to find anything that would explain how to do this.
Here is the main code that will run my browser (obviously I've shortened it).
class PyBrowser(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.SetTitle('PyBrowser')
sizer = wx.BoxSizer(wx.VERTICAL)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
#Element Variables
self.browser = wx.html2.WebView.New(self)
self.address = wx.TextCtrl(self, value="http://",size=(500, 26))
self.go = wx.Button(self, label="Go", id=wx.ID_OK)
self.back = wx.BitmapButton(self, -1, wx.Bitmap('img/Back Button.png', wx.BITMAP_TYPE_PNG), size=(26,26) ,style=wx.NO_BORDER)#wx.Button(self, label="Back")
self.forward = wx.BitmapButton(self, -1, wx.Bitmap('img/Forward Button.png', wx.BITMAP_TYPE_PNG), size=(26,26) ,style=wx.NO_BORDER)#wx.Button(self, label="Forward")
self.reload = wx.BitmapButton(self, -1, wx.Bitmap('img/Reload Button.png', wx.BITMAP_TYPE_PNG), size=(26,26) ,style=wx.NO_BORDER)#wx.Button(self, label = "Refresh")
self.result = None
#Nav area
addressarea = wx.BoxSizer()
addressarea.Add(self.address, proportion = 1, border = 0)
...
#Adding elements
sizer.Add(addressarea, proportion = 0, flag = wx.EXPAND, border = 0)
sizer.Add(self.browser, 1, wx.EXPAND, 10)
#Button binding
self.Bind(wx.EVT_BUTTON, self.OnGo, id=wx.ID_OK)
...
#Menu bar stuff
#File Menu
self.fileMenu = wx.Menu()
self.New = self.fileMenu.Append(wx.ID_ANY, 'New Window')
...
#Help Menu
self.helpMenu = wx.Menu()
self.Help = self.helpMenu.Append(wx.ID_ANY, 'Help')
...
self.Bind(wx.EVT_MENU,self.OnHelp,self.Help)
...
#Adding the actual Menu Bar
self.menuBar = wx.MenuBar()
self.menuBar.Append(self.fileMenu, 'File')
self.menuBar.Append(self.helpMenu, 'Help')
self.SetMenuBar(self.menuBar)
self.SetSizer(sizer)
self.SetSize((1000, 700))
def OnGo(self, event):
self.result = self.address.GetValue()
self.browser.LoadURL(self.result)
if __name__ == '__main__':
app = wx.App()
dialog = PyBrowser(None, -1)
dialog.browser.LoadURL("http://www.google.com")
dialog.Show()
app.MainLoop()
To summarize what I'm asking is how to I set the title of a wx.Frame to the current title of a web page inside of wx.html2.WebView and change the name every time a new link is clicked?
The webview events that you require are listed here
I would catch the EVT_WEBVIEW_LOADED event and update the title in the event handler. I believe you could do something like this:
def onLoaded(self, event):
title = self.browser.GetCurrentTitle()
self.SetTitle(title)
What may be even better is to catch EVT_WEBVIEW_TITLE_CHANGED as that actually detects when the page title changes.
I figured out that from what Mike said, using an event to catch it would work. So instead of EVT_WEBVIEW_TITLE_CHANGED I used EVT_WEBVIEW_LOADED, I then binded the event to my function to handle changing the title. It looked like this:
#Binding the function the event to the function.
self.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.OnPageLoad, self.browser)
#Function binding to the page loading
def OnPageLoad(self, event):
self.WebTitle = self.browser.GetCurrentTitle()
self.SetTitle(self.WebTitle)

Button pointing to another function after Hiding one panel and showing another

I have the a situation here .
My motive : Hiding one panel and showing another.
Whats Working :Layout wise , my app is behaving like i want it to .
The Problem : After the I hide one panel and show another , the button in this panel (process button) is still pointing to the function binded with "OUT" button .
Steps to be followed :
Run the program .
Press "Process" button. Then press "Out" Button .
U will see old panel come back. Again press "Process" button .
Observe that the button is running the function binded with the "OUT" Button.
Here is the complete program :
import wx
class Panels(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title,size=(500,-1))
#Font
#__init__(self, pointSize, family, style, weight, underline=False, face=EmptyString, encoding=FONTENCODING_DEFAULT)
self.font1 = wx.Font(12, wx.DECORATIVE, wx.ITALIC,wx.FONTWEIGHT_BOLD, underline = True )
self.font2 = wx.Font(10, wx.DECORATIVE, wx.NORMAL,wx.FONTWEIGHT_BOLD, underline = False )
self.font3 = wx.Font(10, wx.DECORATIVE, wx.ITALIC,wx.FONTWEIGHT_BOLD, underline = True )
favicon = wx.Icon('icon.ico', wx.BITMAP_TYPE_ICO)
wx.Frame.SetIcon(self, favicon)
list_choice = ['ALL','UnSeen','DRAFT']
self.vbox = wx.BoxSizer(wx.VERTICAL)
#Launch panel Defined
panel_launch = wx.Panel(self,-1,style=wx.SUNKEN_BORDER)
#Layout Defination
hbox= wx.BoxSizer(wx.HORIZONTAL)
lbl_Username = wx.StaticText(panel_launch,0, "Username:")
lbl_Username.SetFont(self.font3)
self.txt_Username = wx.TextCtrl(panel_launch,0)#wx.Size(self,w,h)
lbl_Password = wx.StaticText(panel_launch,0, "Password:")
lbl_Password.SetFont(self.font3)
self.txt_Password = wx.TextCtrl(panel_launch,0,style=wx.TE_PASSWORD)
btn_Process = wx.Button(panel_launch,0, "Process")
hbox.Add(lbl_Username,0,wx.LEFT|wx.TOP,10)
hbox.Add(self.txt_Username,0,wx.LEFT|wx.TOP,10)
hbox.Add(lbl_Password,0,wx.LEFT|wx.TOP,10)
hbox.Add(self.txt_Password,0,wx.LEFT|wx.TOP,10)
hbox.Add(btn_Process,0,wx.LEFT|wx.TOP,10)
panel_launch.SetSizer(hbox)
#Menu section
menubar=wx.MenuBar()
pehla=wx.Menu()
doosra=wx.Menu()
teesra =wx.Menu()
option_menu=wx.Menu()
info=wx.Menu()
#Menu Items
self.item1_1=pehla.Append(wx.ID_OPEN,"&Add Mail\tAlt-A","This is to add a mail account") #Sub-Items of First menu pull down list
item1_2=pehla.Append(wx.ID_EXIT,"&Quit\tAlt-Q","This will exit app") #The last comment will show on status bar when mouse is on that option
item3_2=teesra.Append(wx.ID_ABOUT,"A&bout\tAlt-B","About Section")
menu_1=menubar.Append(pehla,'&File') #Naming of Menu items
menu_2=menubar.Append(doosra,'&Edit')
menu_3=menubar.Append(teesra,'&Info')
item2_1=option_menu.Append(wx.ID_ANY,'Export File Location')
doosra.AppendMenu(wx.ID_ANY,"&Options\tAlt-O",option_menu)
self.item1_1.Enable(False)
self.SetMenuBar(menubar)
self.vbox.Add(panel_launch,-1,wx.EXPAND,10)
self.SetSizer(self.vbox)
self.Layout()
self.Centre()
self.Show(True)
#Event Bindings
self.Bind(wx.EVT_BUTTON,self.OnSignIn,btn_Process)
self.Bind(wx.EVT_MENU,self.OnAddMail,self.item1_1)
self.Bind(wx.EVT_MENU, self.OnFileExit,item1_2)
#+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-
#--------------------------Frame Related Function Zone--------------------------------------
def OnSignIn(self,event):
print "We are in default"
mymail = self.txt_Username.GetValue()
mypwd = self.txt_Password.GetValue()
self.tstremove(event) #Hiding the old panel items
panel_signedin = wx.Panel(self,-1)
lbl_email = wx.StaticText(panel_signedin,0, "Email:")
lbl_email.SetFont(self.font3)
txt_mymail = wx.StaticText(panel_signedin,0,mymail)
txt_mymail.SetFont(self.font2)
btn_OUT = wx.Button(panel_signedin,0, "OUT")
self.Bind(wx.EVT_BUTTON,self.OnSignOut,btn_OUT)
hbox3= wx.BoxSizer(wx.HORIZONTAL)
hbox3.Add(lbl_email,0,wx.ALL,10)
hbox3.Add(txt_mymail,0,wx.ALL,10)
hbox3.Add(btn_OUT,0,wx.ALL,10)
panel_signedin.SetSizer(hbox3)
self.vbox.Add(panel_signedin,-1,wx.EXPAND,10)
self.SetSizer(self.vbox)
self.Layout()
self.item1_1.Enable(True) #Enable 'Add' menu item
def OnSignOut(self,event):
print "We are out"
event.GetEventObject().GetParent().Hide()
self.txt_Username.GetParent().Show(True)
self.Layout()
return
def OnSignInNew(self,event):
print "We are in New"
self.item1_1.Enable(True)
mymail1 = self.txt1_Username.GetValue()
mypwd1 = self.txt_Password.GetValue()
self.tstremove(event)
panel_signedin1 = wx.Panel(self,-1)
lbl_email1 = wx.StaticText(panel_signedin1,0, "Email:")
lbl_email1.SetFont(self.font3)
txt_mymail1 = wx.StaticText(panel_signedin1,0,mymail1)
txt_mymail1.SetFont(self.font2)
btn1_OUT = wx.Button(panel_signedin1,0, "OUT")
self.Bind(wx.EVT_BUTTON,self.OnSignOut,btn1_OUT)
hbox31= wx.BoxSizer(wx.HORIZONTAL)
hbox31.Add(lbl_email1,0,wx.ALL,10)
hbox31.Add(txt_mymail1,0,wx.ALL,10)
hbox31.Add(btn1_OUT,0,wx.ALL,10)
panel_signedin1.SetSizer(hbox31)
self.vbox.Add(panel_signedin1,-1,wx.EXPAND,10)
self.SetSizer(self.vbox)
self.Layout()
def tstremove(self,event):
event.GetEventObject().GetParent().Hide()
self.Layout()
return
def tstrecall(self,event):
event.GetEventObject().GetParent().UnHide()
self.Layout()
return
def OnAddMail(self,eventdef):
self.item1_1.Enable(False)
self.vbox.Add((-1,5))
panel_new = wx.Panel(self,0,style=wx.SUNKEN_BORDER)
hbox1= wx.BoxSizer(wx.HORIZONTAL)
lbl1_Username = wx.StaticText(panel_new,-1, "Username:")
lbl1_Username.SetFont(self.font3)
self.txt1_Username = wx.TextCtrl(panel_new,-1)#wx.Size(self,w,h)
lbl1_Password = wx.StaticText(panel_new,-1, "Password:")
lbl1_Password.SetFont(self.font3)
self.txt1_Password = wx.TextCtrl(panel_new,-1,style=wx.TE_PASSWORD)
btn1_Process = wx.Button(panel_new,-1, "Process")
self.Bind(wx.EVT_BUTTON,self.OnSignInNew,btn1_Process)
hbox1.Add(lbl1_Username,0,wx.LEFT|wx.TOP,10)
hbox1.Add(self.txt1_Username,0,wx.LEFT|wx.TOP,10)
hbox1.Add(lbl1_Password,0,wx.LEFT|wx.TOP,10)
hbox1.Add(self.txt1_Password,0,wx.LEFT|wx.TOP,10)
hbox1.Add(btn1_Process,0,wx.LEFT|wx.TOP,10)
panel_new.SetSizer(hbox1)
self.vbox.Add(panel_new,-1,wx.EXPAND,10)
self.SetSizer(self.vbox)
self.Layout()
self.Bind(wx.EVT_BUTTON,self.OnSignInNew,btn1_Process)
return
#+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-+-()-
#--------------------------Process Related Function Zone--------------------------------------
def OnFileExit(self, event):
""" File|Exit event """
self.Close()
app = wx.App()
Panels(None, -1, 'Test')
app.MainLoop()
I just can't get, how one button is pointing to another function?
Is hiding and showing not enough?
The problem is that you are giving BOTH buttons the same ID. Actually, you're giving almost ALL your widgets the same ID. You should NEVER do that. If you change the id parameter in all your widgets to wx.ID_ANY or just -1, you'll get randomly assigned ids and your program will work.

Return value from wxPython Frame

Could someone show me how I could return a value from a wxPython Frame? When the use clicks close, I popup a message dialog asking him a question. I would like to return the return code of this message dialog to my calling function.
Thanks
Because the wxFrame has events that process via the app.MainLoop() functionality, the only way to get at the return value of a wx.Frame() is via catching an event.
The standard practice of handling events is typically from within the class which derives from wx.Window itself (e.g., Frame, Panel, etc.). Since you want code exterior to the wx.Frame to receive information that was gathered upon processing the OnClose() event, then the best way to do that is to register an event handler for your frame.
The documentation for wx.Window::PushEventHandler is probably the best resource and even the wxpython wiki has a great article on how to do this. Within the article, they register a custom handler which is an instance of "MouseDownTracker." Rather than instantiating within the PushEventHandler call, you'd want to instantiate it prior to the call so that you can retain a handle to the EventHandler derived class. That way, you can check on your derived EventHandler class-variables after the Frame has been destroyed, or even allow that derived class to do special things for you.
Here is an adaptation of that code from the wx python wiki (admittedly a little convoluted due to the requirement of handling the results of a custom event with a "calling" function):
import sys
import wx
import wx.lib.newevent
(MyCustomEvent, EVT_CUSTOM) = wx.lib.newevent.NewEvent()
class CustomEventTracker(wx.EvtHandler):
def __init__(self, log, processingCodeFunctionHandle):
wx.EvtHandler.__init__(self)
self.processingCodeFunctionHandle = processingCodeFunctionHandle
self.log = log
EVT_CUSTOM(self, self.MyCustomEventHandler)
def MyCustomEventHandler(self, evt):
self.log.write(evt.resultOfDialog + '\n')
self.processingCodeFunctionHandle(evt.resultOfDialog)
evt.Skip()
class MyPanel2(wx.Panel):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent)
self.log = log
def OnResults(self, resultData):
self.log.write("Result data gathered: %s" % resultData)
class MyFrame(wx.Frame):
def __init__(self, parent, ID=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, ID, title, pos, size, style)
self.panel = panel = wx.Panel(self, -1, style=wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.FULL_REPAINT_ON_RESIZE)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add((25, 25))
row = wx.BoxSizer(wx.HORIZONTAL)
row.Add((25,1))
m_close = wx.Button(self.panel, wx.ID_CLOSE, "Close")
m_close.Bind(wx.EVT_BUTTON, self.OnClose)
row.Add(m_close, 0, wx.ALL, 10)
sizer.Add(row)
self.panel.SetSizer(sizer)
def OnClose(self, evt):
dlg = wx.MessageDialog(self, "Do you really want to close this frame?", "Confirm Exit", wx.OK | wx.CANCEL | wx.ICON_QUESTION)
result = dlg.ShowModal()
dlg.Destroy()
if result == wx.ID_CANCEL:
event = MyCustomEvent(resultOfDialog="User Clicked CANCEL")
self.GetEventHandler().ProcessEvent(event)
else: # result == wx.ID_OK
event = MyCustomEvent(resultOfDialog="User Clicked OK")
self.GetEventHandler().ProcessEvent(event)
self.Destroy()
app = wx.App(False)
f2 = wx.Frame(None, title="Frame 1 (for feedback)", size=(400, 350))
p2 = MyPanel2(f2, sys.stdout)
f2.Show()
eventTrackerHandle = CustomEventTracker(sys.stdout, p2.OnResults)
f1 = MyFrame(None, title="PushEventHandler Tester (deals with on close event)", size=(400, 350))
f1.PushEventHandler(eventTrackerHandle)
f1.Show()
app.MainLoop()
You can get the result of clicking the OK, CANCEL buttons from the Dialog ShowModal method.
Given dialog is an instance of one of the wxPython Dialog classes:
result = dialog.ShowModal()
if result == wx.ID_OK:
print "OK"
else:
print "Cancel"
dialog.Destroy()
A few years late for the initial question, but when looking for the answer to this question myself, I stumbled upon a built-in method of getting a return value from a modal without messing with any custom event funniness. Figured I'd post here in case anyone else needs it.
It's simply this guy right here:
wxDialog::EndModal void EndModal(int retCode)
Ends a modal dialog, passing a value to be returned from the
*wxDialog::ShowModal invocation.*
Using the above, you can return whatever you want from the Dialog.
An example usage would be subclassing a wx.Dialog, and then placing the EndModal function in the button handlers.
class ProjectSettingsDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Project Settings", size=(600,400))
sizer = wx.BoxSizer(wx.VERTICAL) #main sized
sizer.AddStretchSpacer(1)
msg = wx.StaticText(self, -1, label="This is a sample message")
sizer.Add(msg, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 15)
horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(self, -1, 'OK')
self.Bind(wx.EVT_BUTTON, self.OnOK, okButton)
cancelBtn = wx.Button(self, -1, "Cancel")
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelBtn)
horizontal_sizer.Add(okButton, 0, wx.ALIGN_LEFT)
horizontal_sizer.AddStretchSpacer(1)
horizontal_sizer.Add(cancelBtn, 0, wx.ALIGN_RIGHT)
sizer.Add(horizontal_sizer, 0)
sizer.AddStretchSpacer(1)
self.SetSizer(sizer)
def OnOK(self, event):
self.EndModal(wx.ID_OK) #returns numeric code to caller
self.Destroy()
def OnCancel(self, event):
self.EndModal(wx.ID_CANCEL) #returns numeric code to caller
self.Destroy()
(Note: I just banged this code out quickly; didn't test the sizers)
As you can see, all you need to do is call the EndModal from a button event to return a value to whatever spawned the dialog.
I wanted to do the same thing, to have a graphical "picker" that I could run from within a console app. Here's how I did it.
# Fruit.py
import wx
class Picker (wx.App):
def __init__ (self, title, parent=None, size=(400,300)):
wx.App.__init__(self, False)
self.frame = wx.Frame(parent, title=title, size=size)
self.apple_button = wx.Button(self.frame, -1, "Apple", (0,0))
self.apple_button.Bind(wx.EVT_BUTTON, self.apple_button_click)
self.orange_button = wx.Button(self.frame, -1, "Orange", (0,100))
self.orange_button.Bind(wx.EVT_BUTTON, self.orange_button_click)
self.fruit = None
self.frame.Show(True)
def apple_button_click (self, event):
self.fruit = 'apple'
self.frame.Destroy()
def orange_button_click (self, event):
self.fruit = 'orange'
self.frame.Destroy()
def pick (self):
self.MainLoop()
return self.fruit
Then from a console app, I would run this code.
# Usage.py
import Fruit
picker = Fruit.Picker('Pick a Fruit')
fruit = picker.pick()
print 'User picked %s' % fruit
user1594322's answer works but it requires you to put all of your controls in your wx.App, instead of wx.Frame. This will make recycling the code harder.
My solution involves define a "PassBack" variable when defining your init function. (similar to "parent" variable, but it is normally used already when initiating a wx.Frame)
From my code:
class MyApp(wx.App):
def __init__ (self, parent=None, size=(500,700)):
wx.App.__init__(self, False)
self.frame = MyFrame(parent, -1, passBack=self) #Pass this app in
self.outputFromFrame = "" #The output from my frame
def getOutput(self):
self.frame.Show()
self.MainLoop()
return self.outputFromFrame
and for the frame class:
class MyFrame(wx.Frame):
def __init__(self, parent, ID, passBack, title="My Frame"):
wx.Frame.__init__(self, parent, ID, title, size=(500, 700))
self.passBack = passBack #this will be used to pass back variables/objects
and somewhere during the execution of MyFrame
self.passBack.outputFromFrame = "Hello"
so all in all, to get a string from an application
app = MyApp()
val = app.getOutput()
#Proceed to do something with val
Check this answer on comp.lang.python: Linkie
I don't think a wxFrame can return a value since it is not modal. If you don't need to use a wxFrame, then a modal dialog could work for you. If you really need a frame, I'd consider using a custom event.
It would go something like this:
(1) User clicks to close the wxFrame
(2) You override OnClose (or something like that) to pop up a dialog to ask the user a question
(3) Create and post the custom event
(4) Close the wxFrame
(5) Some other code processes your custom event
I think I just had the same problem as you. Instead of making that popup a frame, I made it a dialog instead. I made a custom dialog by inheriting a wx.dialog instead of a wx.frame. Then you can utilize the code that joaquin posted above. You check the return value of the dialog to see what was entered. This can be done by storing the value of the textctrl when the user clicks ok into a local variable. Then before it's destroyed, you get that value somehow.
The custom dialog section of this site helped me out greatly.
http://zetcode.com/wxpython/dialogs/

Categories