problem with dynamic popup menu when ids get too large?

Hi,

windows-8.1, python-2.7.8, wxpython-2.8.12.1

I have an application with a large dynamic popup menu.
Every time I construct it, I use ID_ANY as the id
for all the menu items.

After calling frame.PopupMenu(menu), I call menu.Delete()
but all of the ids used in the menu remain effectively
claimed and the next time the menu is constructed,
the ids used by the new menu items are higher than the
last time.

I assume that's ok because http://wiki.wxpython.org/Getting%20Started
says "You can make your own IDs, but there is no reason to do that."

However, occasionally I get reports from the users that when they
select a menu item, the application behaves as though some other
completely unrelated menu item had been selected.

I added some logging to see what was going on and asked them to
tell me when it next happened which it just has. It seems that the
problem happened the first time the user selected an item
after the menu item ids has exceeded 2**16. This might be an
unimportant coincidence. I only have one data point. But it
might be relevant.

When the problem occurs, the id of the selected menu item is a
much lower number than the ids of any of the menu items that
exist in the menu at the time. It looks as though it's using
a much earlier id from a previous incarnation of the menu.

Does anyone have any idea what might be going wrong?

If it could have something to do with the ever increasing ids,
is there a way to tell wx to reset its next-available-id to
be one more than the currently used id?

Would using application-defined ids be ok or might other uses
of ID_ANY reuse ids that I've chosen?

Cheers,
raf

Are you destroying the menu object after popping it up?

Nathan McCorkle wrote:

Are you destroying the menu object after popping it up?

yes. i said so in my original mail.

any other ideas?

cheers,
raf

raf wrote:

Hi,

windows-8.1, python-2.7.8, wxpython-2.8.12.1

I have an application with a large dynamic popup menu.
Every time I construct it, I use ID_ANY as the id
for all the menu items.

After calling frame.PopupMenu(menu), I call menu.Delete()
but all of the ids used in the menu remain effectively
claimed and the next time the menu is constructed,
the ids used by the new menu items are higher than the
last time.

I assume that's ok because Getting Started - wxPyWiki
says "You can make your own IDs, but there is no reason to do that."

However, occasionally I get reports from the users that when they
select a menu item, the application behaves as though some other
completely unrelated menu item had been selected.

I added some logging to see what was going on and asked them to
tell me when it next happened which it just has. It seems that the
problem happened the first time the user selected an item
after the menu item ids has exceeded 2**16. This might be an
unimportant coincidence. I only have one data point. But it
might be relevant.

It is. IIRC, Windows has a limit for menu IDs but a different limit for control IDs. In 2.8 and prior[1] the IDs generated for wx.ID_ANY (and those returned from wx.NewId()) are basically just an incrementing global variable with almost no smarts behind it. There is also wx.RegisterId(val) which will bump the auto-increment sequence to val+1 so you can reserve some IDs that way if you want, but you can still run into issues if the value wraps around and comes back to IDs that you may still be using somewhere. And then there are problems when the ID passes some artificial limit that the platform imposes.

When the problem occurs, the id of the selected menu item is a
much lower number than the ids of any of the menu items that
exist in the menu at the time. It looks as though it's using
a much earlier id from a previous incarnation of the menu.

Does anyone have any idea what might be going wrong?

If it could have something to do with the ever increasing ids,
is there a way to tell wx to reset its next-available-id to
be one more than the currently used id?

Would using application-defined ids be ok or might other uses
of ID_ANY reuse ids that I've chosen?

What I've tended to do in situations like this where there will be lots of items needing IDs being created, is to go ahead and use fixed ID values for the items, but use wx.NewId() to give me the ID values. That way they won't conflict with any other IDs that are still being generated with wx.ID_ANY (as long as they don't wrap around, but then that is what you are trying to prevent by doing this) and they will also not conflict with any stock IDs. For example, I'll have globals like this:

MY_ID_1 = wx.NewId()
MY_ID_2 = wx.NewId()
etc.

and then use those IDs where I create the popup menus. Presetting your menu IDs like that also means that you don't need to Bind/Unbind the event handlers each time you popup the menu, just do it once at the beginning of the program.

[1] There was a change made in the 2.9 series that puts some more intelligence behind the generated IDs, where it will try to track those that are reserved and reuse those that are not in use any more. I don't recall details at the moment on the implementation so I'm not sure how bulletproof it is, but it should be transparent to the application code.

···

--
Robin Dunn
Software Craftsman

Robin Dunn wrote:

raf wrote:
>Hi,
>
>windows-8.1, python-2.7.8, wxpython-2.8.12.1
>
>I have an application with a large dynamic popup menu.
>Every time I construct it, I use ID_ANY as the id
>for all the menu items.
>
>After calling frame.PopupMenu(menu), I call menu.Delete()
>but all of the ids used in the menu remain effectively
>claimed and the next time the menu is constructed,
>the ids used by the new menu items are higher than the
>last time.
>
>I assume that's ok because Getting Started - wxPyWiki
>says "You can make your own IDs, but there is no reason to do that."
>
>However, occasionally I get reports from the users that when they
>select a menu item, the application behaves as though some other
>completely unrelated menu item had been selected.
>
>I added some logging to see what was going on and asked them to
>tell me when it next happened which it just has. It seems that the
>problem happened the first time the user selected an item
>after the menu item ids has exceeded 2**16. This might be an
>unimportant coincidence. I only have one data point. But it
>might be relevant.

It is. IIRC, Windows has a limit for menu IDs but a different limit
for control IDs. In 2.8 and prior[1] the IDs generated for wx.ID_ANY
(and those returned from wx.NewId()) are basically just an
incrementing global variable with almost no smarts behind it. There
is also wx.RegisterId(val) which will bump the auto-increment
sequence to val+1 so you can reserve some IDs that way if you want,
but you can still run into issues if the value wraps around and comes
back to IDs that you may still be using somewhere. And then there are
problems when the ID passes some artificial limit that the platform
imposes.

>When the problem occurs, the id of the selected menu item is a
>much lower number than the ids of any of the menu items that
>exist in the menu at the time. It looks as though it's using
>a much earlier id from a previous incarnation of the menu.
>
>Does anyone have any idea what might be going wrong?
>
>If it could have something to do with the ever increasing ids,
>is there a way to tell wx to reset its next-available-id to
>be one more than the currently used id?
>
>Would using application-defined ids be ok or might other uses
>of ID_ANY reuse ids that I've chosen?

What I've tended to do in situations like this where there will be
lots of items needing IDs being created, is to go ahead and use fixed
ID values for the items, but use wx.NewId() to give me the ID values.
That way they won't conflict with any other IDs that are still being
generated with wx.ID_ANY (as long as they don't wrap around, but then
that is what you are trying to prevent by doing this) and they will
also not conflict with any stock IDs. For example, I'll have globals
like this:

MY_ID_1 = wx.NewId()
MY_ID_2 = wx.NewId()
etc.

and then use those IDs where I create the popup menus. Presetting
your menu IDs like that also means that you don't need to Bind/Unbind
the event handlers each time you popup the menu, just do it once at
the beginning of the program.

[1] There was a change made in the 2.9 series that puts some more
intelligence behind the generated IDs, where it will try to track
those that are reserved and reuse those that are not in use any more.
I don't recall details at the moment on the implementation so I'm not
sure how bulletproof it is, but it should be transparent to the
application code.

--
Robin Dunn
Software Craftsman
http://wxPython.org

Thanks, Robin. That's a great suggestion. I'm sure it'll fix my problem.

Cheers,
raf