Relocateable popup menu?

Hi all,

I'm trying to make a relocatable popup menu. By this I mean that I want
to have a popup menu that can be popped up from various other windows,
not just one. At first this seemed like it would be easy: I just create
a custom menu class, then crete it an pop it up wherever I need it,
However, after looking at the demo, and after a recent discussion here
about binding menus, this looks like it can't be done.

Apparently, menu events are not delivered to the menu, but rather to the
window that hosts it. they are then bound to method in that window:

self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)

where self is a panel, in this case (from the demo). However, in my
case, I want the menu event to do the saem thing, regardless of what
window I poped it up in. Is there any way to do that? or do I need to
either:

1) duplicate the menu code in every window I want the popup to occur in.

or

2) forget using menues, and build something similar myself with a popup
window.

Are there any other options?

-Chris

Hi all,

I'm trying to make a relocatable popup menu. By this I mean that I want
to have a popup menu that can be popped up from various other windows,
not just one. At first this seemed like it would be easy: I just create
a custom menu class, then crete it an pop it up wherever I need it,
However, after looking at the demo, and after a recent discussion here
about binding menus, this looks like it can't be done.

Apparently, menu events are not delivered to the menu, but rather to the
window that hosts it. they are then bound to method in that window:

self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)

where self is a panel, in this case (from the demo). However, in my
case, I want the menu event to do the saem thing, regardless of what
window I poped it up in. Is there any way to do that? or do I need to
either:

1) duplicate the menu code in every window I want the popup to occur in.

use a FactoryMethod or something similar , you don't need to duplicate code just give your panel to a factory method and have it create and install the menu.
Or even better... subclass Window and install the popup automatically in constructor then... use that as a base class for all the windows you need to pop-up the menu
as for the "do the same thing everywhere"... this is a classical MVC issue... make some kind of a model and have the menus call methods from that model... use the same model for all the windows and voila... problem solved.

···

On Thu, 04 Nov 2004 22:33:43 -0800, Chris Barker <Chris.Barker@noaa.gov> wrote:

or

2) forget using menues, and build something similar myself with a popup
window.

Are there any other options?

-Chris

--
Peter Damoc
Hacker Wannabe

Hi all,

I'm trying to make a relocatable popup menu. By this I mean that I want to
have a popup menu that can be popped up from various other windows, not
just one. At first this seemed like it would be easy: I just create a
custom menu class, then crete it an pop it up wherever I need it, However,
after looking at the demo, and after a recent discussion here about
binding menus, this looks like it can't be done.

Apparently, menu events are not delivered to the menu, but rather to the
window that hosts it. they are then bound to method in that window:

self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)

Are you sure? Here they are delivered to the panel because you asked for
it, but I see no reason why it shouldn't work something like:

my_popup_menu.Bind(wx.EVT_MENU, my_popup_menu.handle_menu_events, id=-1)

actually, this small sample seems to do what you asked (tested on wxGTK2):

···

-------------------------------------------------------------------------
import wx

app = wx.PySimpleApp()

frame = wx.Frame(None, -1, "Test", size=(300, 200))
frame2 = wx.Frame(None, -1, "Another window", size=(300, 200))

menu = wx.Menu("My test menu")
menu.Append(1000, "Item 1")
menu.Append(1001, "Item 2")

def on_command(event):
    print "Received command:", event.GetId()

menu.Bind(wx.EVT_MENU, on_command, id=-1)

def make_handler(win):
    def on_right_down(event):
        win.PopupMenu(menu, event.GetPosition())
    return on_right_down

frame.Bind(wx.EVT_RIGHT_DOWN, make_handler(frame))
frame2.Bind(wx.EVT_RIGHT_DOWN, make_handler(frame2))

app.SetTopWindow(frame)
frame.Show()
frame2.Show()
app.MainLoop()
-------------------------------------------------------------------------

HTH,
Alberto

Alberto Griggio wrote:

Apparently, menu events are not delivered to the menu, but rather to the
window that hosts it. they are then bound to method in that window:

self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)

Are you sure?

I guess not, but the earlier discussion here seemed to indicate that, and the sample does too.

Here they are delivered to the panel because you asked for
it, but I see no reason why it shouldn't work something like:

my_popup_menu.Bind(wx.EVT_MENU, my_popup_menu.handle_menu_events, id=-1)

actually, this small sample seems to do what you asked (tested on wxGTK2):

yes, it does. I'm going to try to clean up the structure a little, and expand on it, and maybe you've solved the problem.

Alberto

By the way, Alberto, I'm trying to do this so I can add popup menus to Cornice. More on that once I've got something working.

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Peter Damoc wrote:

1) duplicate the menu code in every window I want the popup to occur in.

use a FactoryMethod or something similar , you don't need to duplicate code just give your panel to a factory method and have it create and install the menu.

well, I did something like this, but I still found that while I didn't duplicate the code, I was binding all the menu events in every single window I needed them, which is a lot, and it feels kludgey.

Or even better... subclass Window and install the popup automatically in constructor then... use that as a base class for all the windows you need to pop-up the menu

I didn't think I could do that, but of course I can. I think I could even do it in a mixin. However, I'd still end up binding all the menu events to LOTS of window instances, which feels ugly.

as for the "do the same thing everywhere"... this is a classical MVC issue... make some kind of a model and have the menus call methods from that model... use the same model for all the windows and voila... problem solved.

well, yes, but I still have to do all the binding.

I think Alberto gave me a suggestion that will work, which is appropriate, as I'm doing this to add functionality to his "Cornice" app. It's an image viewer, and I want to be able to add a popup menu to do some basic commands to an image: print it; rotate it;, etc. An image can be presented in three ways: A thumbnail, A larger view, and a full-frame view. I want the popup menu to be the same in all three views.

More as I get this working.

-Chris

···

On Thu, 04 Nov 2004 22:33:43 -0800, Chris Barker <Chris.Barker@noaa.gov> > wrote:

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Hi all,

I've "classified" Alberto's code, and now have a nice menu class that can be popped up in any number of windows. I'm thinking that this is much cleaner than the way the demo is written. Should I re-write the demo this way?

Below is the class, enclosed is a complete sample app.

-Chris

class MyPopupMenu(wx.Menu):
     def __init__(self, WinName):
         wx.Menu.__init__(self)

         self.WinName = WinName

         item = wx.MenuItem(self, wx.NewId(), "Item One")
         self.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnItem1, item)

         item = wx.MenuItem(self, wx.NewId(),"Item Two")
         self.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnItem2, item)

         item = wx.MenuItem(self, wx.NewId(),"Item Three")
         self.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnItem3, item)

     def OnItem1(self, event):
         print "Item One selected in the %s window"%self.WinName

     def OnItem2(self, event):
         print "Item Two selected in the %s window"%self.WinName

     def OnItem3(self, event):
         print "Item Two selected in the %s window"%self.WinName

Popup.py (2.55 KB)

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Peter Damoc wrote:

1) duplicate the menu code in every window I want the popup to occur in.

use a FactoryMethod or something similar , you don't need to duplicate
code just give your panel to a factory method and have it create and
install the menu.

well, I did something like this, but I still found that while I didn't
duplicate the code, I was binding all the menu events in every single
window I needed them, which is a lot, and it feels kludgey.

did you see a performance performace hit in binding all those menu events to the windows?
As far as I can see from the code you posted you still have to duplicate the binding... is just that the events are not binded to a certain window...
I'm courious, does it matter? I'm not familiar with the inner workings of the event system.... but I'm guessing that it doesn't matter.

Or even better... subclass Window and install the popup automatically in
constructor then... use that as a base class for all the windows you
need to pop-up the menu

I didn't think I could do that, but of course I can. I think I could
even do it in a mixin. However, I'd still end up binding all the menu
events to LOTS of window instances, which feels ugly.

well I guess you could Unbind them after the menu is destroyed if it turns out that having them all binded pose a problem.

···

On Fri, 05 Nov 2004 13:52:00 -0800, Chris Barker <Chris.Barker@noaa.gov> wrote:

On Thu, 04 Nov 2004 22:33:43 -0800, Chris Barker <Chris.Barker@noaa.gov> >> wrote:

as for the "do the same thing everywhere"... this is a classical MVC
issue... make some kind of a model and have the menus call methods from
that model... use the same model for all the windows and voila...
problem solved.

well, yes, but I still have to do all the binding.

I think Alberto gave me a suggestion that will work, which is
appropriate, as I'm doing this to add functionality to his "Cornice"
app. It's an image viewer, and I want to be able to add a popup menu to
do some basic commands to an image: print it; rotate it;, etc. An image
can be presented in three ways: A thumbnail, A larger view, and a
full-frame view. I want the popup menu to be the same in all three views.

More as I get this working.

-Chris

--
Peter Damoc
Hacker Wannabe

Chris Barker wrote:

Alberto Griggio wrote:

Apparently, menu events are not delivered to the menu, but rather to the
window that hosts it. they are then bound to method in that window:

self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)

Are you sure?

I guess not, but the earlier discussion here seemed to indicate that, and the sample does too.

I think that is something that was changed sometime in 2.5. The menus attached to menubars are still inconsistent between platforms though. Some will deliver the event to the frame first, others will deliver to the menu or the menubar first. (I don't remember the specifics.)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

I think that is something that was changed sometime in 2.5. The menus attached to menubars are still inconsistent between platforms though. Some will deliver the event to the frame first, others will deliver to the menu or the menubar first. (I don't remember the specifics.)

But is it consistent with menus that are not attached to a menubar? i.e. will the code I posted earlier work everywhere (I only tested GTK)?, and should I re-write and contribute the demo this way. (I'm posting it again in case you missed it, right click anywhere for a popup)

-Chris

Popup.py (2.55 KB)

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Chris Barker wrote:

Robin Dunn wrote:

I think that is something that was changed sometime in 2.5. The menus attached to menubars are still inconsistent between platforms though. Some will deliver the event to the frame first, others will deliver to the menu or the menubar first. (I don't remember the specifics.)

But is it consistent with menus that are not attached to a menubar?

Yes, it appears to be so (and I wasn't aware of the change until it was brought up the other day.)

i.e. will the code I posted earlier work everywhere (I only tested GTK)?, and should I re-write and contribute the demo this way.

Yes, please do. It would be nice to still show binding (at least some of the) events to the window as well, with comments in the code describing the difference, since sometimes it may make more sense to do it that way. Thay way if somebody is using that sample to learn how to do popup menus they will see an example of both ways and can choose whatever best fits their need.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!