wxPython 4.0.2

504 views
Skip to first unread message

Robin Dunn

unread,
Jun 18, 2018, 12:49:36 AM6/18/18
to wxpytho...@googlegroups.com, wxpyth...@googlegroups.com, wx-u...@googlegroups.com, wx-an...@googlegroups.com, Python-Ann...@python.org
Announcing wxPython 4.0.2
=========================

PyPI: https://pypi.org/project/wxPython/4.0.2
Extras: https://extras.wxPython.org/wxPython4/extras/
Pip: ``pip install wxPython==4.0.2``

Changes in this release include the following:

* Fixed wx.html2.EVT_WEBVIEW_NAVIGATING event not being sent on some
versions of Linux. (#741)

* wx.Sizers can now be used as an iterator to iterate over the items
within the sizer. (#738)

* Fix Python3 division in ThumbnailCtrl. (#746)

* Fix leaking image list in CheckListCtrlMixin (#752)

* All items marked as deprecated in the wxWidgets interface
(documentation) files will now throw a DeprecationWarning when used
from wxPython. Many of these items are disappearing in 4.1 so it's
important to ensure they are deprecated at runtime too instead of
just in the docs. (#749)

* Ensure that the attribute list given to the GLCanvas constructor is
zero-terminated like it was in Classic. (#770)

* Updated to the wxWidgets 3.0.4 release version.

* Added the wxWidgets version number to the tail end of the string
returned by wx.version().

* Bind EVT_WINDOW_DESTROY event only to the tree windows in
CustomTreeCtrl, since otherwise it would be caught when child
windows are destroyed too, which causes problems in this
case. (#778)

* Fixed a problem where wx.TreeCtrl.OnCompareItems was not being
called in derived classes on Windows. This was due to an
optimization that wasn't compatible with how the classes are
wrapped. (#774)

* Added wrappers for wx.ClassInfo and exposed
wx.Object.GetClassInfo. This class is part of wxWidgets' internal
type information system and although it is not very useful for
Python applications it is useful for debugging some internal
wxPython issues.

* Removed the wx.lib.pubsub package, and replaced it with code that
imports the standalone PyPubSub in order remain compatible with
older code that still uses wx.lib.pubsub. (#782, #792)

* Fixed bug in wx.lib.intctrl (#790)

* Fixed subclassing of wx.TextCompleter and wx.TextCompleterSimple
(#827)

* Fixes for Python3 compatibility in PyCrust. (#823)

* Fix wxGet to be able to use pip v10. (#817)

* Change winid parameter in wx.ScrolledWindow to id, for
consistency. (#816)

* Ensure that the page exists in book controls GetPage and RemovePage
methods. At least one of the wx ports do not do this. (#830)

* Added missing wx.NumberEntryDialog

* Change wx.TextCompleterSimple.GetCompletions to send the list of
strings as a return value, rather than a parameter that gets
filled. (#836)

* Enabled the wx.GraphicsContext.Create(metaFileDC) wrapper (#811)

* Metafile support is also available on OSX, so wx.msw.Metafile and
wx.msw.MetafileDC have been moved to the core wx module. So they can
now be accessed as wx.Metafile and wx.MetafileDC.

* Updated the waf tool used by the build to version 2.0.7. This fixes
problems with building for Python 3.7.

* Fixed alignment in buttons on MSW which have had foreground or
background colors set. (#815)

* Fix for unexpected assertion inside wx.aui.AuiMDIChildFrame.Close.

* Fix a bug in setting AuiDockingGuide size. (#727)

* Remove unnecessary AUI notebook updating, and use wx.BufferedDC in
Repaint() to mitigate flicker. (wx.lib.agw.aui). (#851, #686)

* Fixed crashing bug when using client data with items in
wx.dataview.DataViewTreeCtrl. (#856)

* Detach wx.Control in AuiToolbar from current sizer before attach to
a new one. (#843)

* Fixed a problem in wx.lib.mixins.listctrl.TextEditMixin where the
height of the editor widget could be set to zero. (See discussion in
#849)

* Fix a bug in calculating whether a tool fits into the
AuiToolBar. (#863)

* Override SetForegroundColour and SetBackgroundColour in
MaskedEditMixin (#808)

* Add an explicit wx.GraphicsContext.Create overload for
wx.AutoBufferedPaintDC. (#783)

* Return original AGW window style in
AuiToolBar.GetAGWWindowStyleFlag. (#870)

* Fix a bug in group management on wx.lib.masked.numctrl; the previous
code used truediv ('/') to calculate _groupSpace, but in python 3.x
this leads to a float result, instead of an integer as was
expected. Using floordiv ('//') instead to solve the problem. (#865)

* Hide the window when the tool does not fit into AuiToolBar. (#872)

* Fixed the virtual dispatch code for the PGEditor.GetValueFromControl
method to properly pass the parameters to the Python implementation,
and also fixed how the return value is handled. (#742)

* Fixed all implementations of the PGProperty.StringToValue and
IntToValue methods to treat the value parameter as a return
value. (#742)

* Add missing wx.adv.EVT_CALENDAR_WEEK_CLICKED (#875)

* Fixed the stock labels to conform to Windows design
guidelines. (#787)

* Always reset floating size and style when floating a toolbar in
agw.aui. (#880)





What is wxPython?
-----------------

wxPython is a cross-platform GUI toolkit for the Python programming
language. It allows Python programmers to create programs with a
robust, highly functional graphical user interface, simply and
easily. It is implemented as a set of Python extension modules that
wrap the GUI components of the popular wxWidgets cross platform
library, which is written in C++. Supported platforms are Microsoft
Windows, Mac OS X and macOS, and Linux or other unix-like systems with
GTK2 or GTK3 libraries. In most cases the native widgets are used on
each platform to provide a 100% native look and feel for the
application.


What is wxPython Phoenix?
-------------------------

wxPython's Project Phoenix is a new from-the-ground-up implementation
of wxPython, created with the intent of making wxPython “better,
stronger, faster than he was before.” In other words, this new
implementation is focused on improving speed, maintainability and
extensibility of wxPython, as well as removing most of the cruft that
had accumulated over the long life of Classic wxPython.

The project has been in development off and on, mostly behind the
scenes, for many years. For the past few years automated snapshot
builds have been available for those adventurous enough to try it, and
many people eventually started using the snapshots in their projects,
even for production releases. While there are still some things on
the periphery that need to be completed, the core of the new wxPython
extension modules which wrap the wxWidgets code has been stable for a
long time now.

Due to some things being cleaned up, reorganized, simplified and
dehackified wxPython Phoenix is not completely backwards compatible
with wxPython Classic. This is intended. In general, however, the API
differences tend to be minor and some applications can use Phoenix
with slight, or even with no modifications. In some other cases the
correct way to do things was also available in Classic and it's only
the wrong way that has been removed from Phoenix. For more
information there is a Migration Guide document available at:
https://docs.wxpython.org/MigrationGuide.html

The new wxPython API reference documentation, including all
Python-specific additions and customizations, and docs for the wx.lib
package, is located at: https://docs.wxpython.org/



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

Marco Prosperi

unread,
Jun 18, 2018, 3:30:18 AM6/18/18
to wxPython-users


Great work! I've just installed and looking around: I get a lot of deprecation warning about NewId. Which is the recommended way of managing ids? Passing wx.ANY in the constructor of objects and then object.GetId() to get the id?

Marco

Anders Munch

unread,
Jun 18, 2018, 8:06:56 AM6/18/18
to wxpytho...@googlegroups.com
Marco Prosperi:
> I get a lot of deprecation warning about NewId.

Just tried it. Some of the warnings are in the wx library itself, e.g. in wx.py.frame.

I googled to find http://docs.wxwidgets.org/3.1/overview_windowids.html.
It looks like wx.Window.NewControlId is the replacement. Although that one returns negative numbers, whereas NewId returns positive numbers, but I think that only makes a difference if you use hardwired literal constant ID's, which you shouldn't do anyway.

Robin, please keep wx.NewId and remove the deprecation, adding a forwarding implementation when it becomes necessary.
NewId calls are ubiquitous. You don't remove something like that, unless there's a hard technical need, or it's some sort of really badly named bug magnet, and wx.NewId is neither.

regards, Anders

Eric Fahlgren

unread,
Jun 18, 2018, 10:56:33 AM6/18/18
to wxPyth...@googlegroups.com, wxpytho...@googlegroups.com, wx-u...@googlegroups.com, wx-an...@googlegroups.com, Python-Ann...@python.org
On Sun, Jun 17, 2018 at 9:49 PM Robin Dunn <ro...@alldunn.com> wrote:
Announcing wxPython 4.0.2
=========================

Thanks once more, Robin!  Good work.

Robin Dunn

unread,
Jun 18, 2018, 12:42:29 PM6/18/18
to wxPython-users

One of the changes in this release is that anything that is marked as deprecated in the wxWidgets interface is now automatically marked as deprecated for wxPython as well. The initial reason for this is that most of them are being permanently removed in 4.1, but there are other good reasons as well. It will make runtime behavior match the documentation, ("Why does it say deprecated in the documentation but doesn't raise a deprecation warning?") and also to conform to one of Phoenix's design goals of reducing the divergence between wxWidgets and wxPython.
 
If the change really rubs you the wrong way then you can always monkey-patch in a new implementation of NewId into wx in your applications.

--
Robin



Anders Munch

unread,
Jun 19, 2018, 6:14:59 AM6/19/18
to wxpytho...@googlegroups.com
Robin Dunn:
> If the change really rubs you the wrong way

It really does. The author of the deprecation notice seems to live in a world where people hardwire constant ID numbers in their application. Who does that? If library authors do that, then the numbers are bound to collide sooner or later.

The deprecation notice says:
> Deprecated: Ids generated by it can conflict with the Ids defined by the user code

But wx.NewId is the *solution* to that problem, not the cause of it. The solution to the problem of hardwired constant ID numbers is to stop hardwiring constant ID numbers.

Monkeypatching wx is no good, because to avoid collisions everyone must use the same mechanism to allocate numbers, and if I make my own allocation function for my own private use, then that is not the case.

Before I go on a mass editing spree to replace wx.NewId, can you confirm that wx.Window.NewControlId is the proper replacement?

regards, Anders

Robin Dunn

unread,
Jun 19, 2018, 12:47:10 PM6/19/18
to wxPython-users


On Tuesday, June 19, 2018 at 3:14:59 AM UTC-7, Anders Munch wrote:
Robin Dunn:
> If the change really rubs you the wrong way

It really does.  The author of the deprecation notice seems to live in a world where people hardwire constant ID numbers in their application. Who does that?  If library authors do that, then the numbers are bound to collide sooner or later.

Until recently the usual way to bind events in C++ wxWidgets is to use the static event table, which means static IDs are needed. (Dynamically binding handers is becoming more popular now, thanks in part to wxPython's example with our Bind()). Duplicate IDs only really becomes a problem when the widgets with that ID are in the same containment hierarchy, and the events for those widgets are being caught in a common ancestor instead of within the widget itself or its immediate parent. That means that even with static IDs it is not a problem in something like 95% or better of the cases.
 


The deprecation notice says:
> Deprecated: Ids generated by it can conflict with the Ids defined by the user code

But wx.NewId is the *solution* to that problem, not the cause of it.  The solution to the problem of hardwired constant ID numbers is to stop hardwiring constant ID numbers.

Monkeypatching wx is no good, because to avoid collisions everyone must use the same mechanism to allocate numbers, and if I make my own allocation function for my own private use, then that is not the case.

But there are also potential conflicts with wxNewId because anything that uses wx.ID_ANY to allow wx to allocate the ID will be using NewControlId instead of wxNewId

 

Before I go on a mass editing spree to replace wx.NewId, can you confirm that wx.Window.NewControlId is the proper replacement?

It is. It uses an internal management class to reserve IDs when used and release them when the widget is destroyed, so even if the internal counter wraps around those IDs that are still active will not be reused.

--
Robin

Marco Prosperi

unread,
Jun 19, 2018, 2:59:10 PM6/19/18
to wxPython-users

hello, in the process of managing NewId deprecation warnings, replacing them with other code, I would like to silent these warnings in production code. In another thread a couple of days ago, before wxpython 4.0.2, I ended up with this solution:

if main_is_frozen():
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        #warnings.filterwarnings('ignore')
        run()
else:
    run()

Unfortunately NewId deprecation warning by pass this filters. What could be used instead?

thank in advance

Marco

Robin Dunn

unread,
Jun 19, 2018, 7:49:04 PM6/19/18
to wxPython-users
On Tuesday, June 19, 2018 at 11:59:10 AM UTC-7, Marco Prosperi wrote:

hello, in the process of managing NewId deprecation warnings, replacing them with other code, I would like to silent these warnings in production code. In another thread a couple of days ago, before wxpython 4.0.2, I ended up with this solution:

if main_is_frozen():
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        #warnings.filterwarnings('ignore')
        run()
else:
    run()

Unfortunately NewId deprecation warning by pass this filters. What could be used instead?


Is that code before or after the import of wx? There is `warnings.simplefilter('default', wxPyDeprecationWarning)` happening in the wx module which could be overriding your 'ignore'.

--
Robin


Anders Munch

unread,
Jun 20, 2018, 9:43:35 AM6/20/18
to wxpytho...@googlegroups.com
Robin Dunn:
> That means that even with static IDs it is not a problem in something like 95% or better of the cases.

95% is not really an impressive number in this context.

> But there are also potential conflicts with wxNewId because anything that uses wx.ID_ANY to allow wx to allocate the ID will be using NewControlId instead of wxNewId

I would call that an implementation bug in wx.NewId. It had one job: To return a unique value. Or, seeing as NewId was there first, maybe the bug is in NewControlId.
May I suggest an alternate implementation of wx.NewId, just one single line in core.py that goes:

NewId = Window.NewControlId

regards, Anders

Marco Prosperi

unread,
Jun 20, 2018, 6:22:29 PM6/20/18
to wxPython-users



Is that code before or after the import of wx? There is `warnings.simplefilter('default', wxPyDeprecationWarning)` happening in the wx module which could be overriding your 'ignore'.


In run() I create the main Frame and create main loop. NewId warnings show up if import wx at the top of the script, outside of everything, but also if I import it in run function. I've tried to reduce everything to a simple app (attached) to understand better. In this case NewId warnings are never shown, even if I don't filter them! Can't understand
python 3.5,win10,wxpython 4.0.2
 
--
Robin


wxsample.py

Robin Dunn

unread,
Jun 20, 2018, 7:04:27 PM6/20/18
to wxPython-users


On Wednesday, June 20, 2018 at 6:43:35 AM UTC-7, Anders Munch wrote:
Robin Dunn:
> That means that even with static IDs it is not a problem in something like 95% or better of the cases.

95% is not really an impressive number in this context.

I was being very conservative. I expect that it's much higher than that.

--
Robin


efahl

unread,
Jun 20, 2018, 8:23:47 PM6/20/18
to wxPython-users

On Tuesday, June 19, 2018 at 9:47:10 AM UTC-7, Robin Dunn wrote:
Before I go on a mass editing spree to replace wx.NewId, can you confirm that wx.Window.NewControlId is the proper replacement?

It is. It uses an internal management class to reserve IDs when used and release them when the widget is destroyed, so even if the internal counter wraps around those IDs that are still active will not be reused.

NewControlId values being deallocated automatically when the widget is destroyed gives rise to a new problem.  In my context menus, I have some fixed entries ("Expand all" on a tree, for instance).  I keep a data structure, including the id, around and use that when creating the popup menu.  Works great the first time the context menu is built, but the second time I get the error below.  Seems I need some static ids outside the range controlled by the IdManager.  How do I generate those without collisions?

   lm/gui/contextMenu.py 865 in addToMenu:
      0865              item = wx.MenuItem(menu, self.id, self.text, kind=kind)
wxAssertionError: C++ assertion "gs_autoIdsRefCount[winid] != ID_FREE" failed at ..\..\src\common\windowid.cpp(146) in `anonymous-namespace'::DecIdRefCount(): id count already 0
 

Robin Dunn

unread,
Jun 20, 2018, 9:46:58 PM6/20/18
to wxPython-users
On Wednesday, June 20, 2018 at 5:23:47 PM UTC-7, efahl wrote:

On Tuesday, June 19, 2018 at 9:47:10 AM UTC-7, Robin Dunn wrote:
Before I go on a mass editing spree to replace wx.NewId, can you confirm that wx.Window.NewControlId is the proper replacement?

It is. It uses an internal management class to reserve IDs when used and release them when the widget is destroyed, so even if the internal counter wraps around those IDs that are still active will not be reused.

NewControlId values being deallocated automatically when the widget is destroyed gives rise to a new problem.  In my context menus, I have some fixed entries ("Expand all" on a tree, for instance).  I keep a data structure, including the id, around and use that when creating the popup menu.  Works great the first time the context menu is built, but the second time I get the error below.  Seems I need some static ids outside the range controlled by the IdManager.  How do I generate those without collisions?


I guess I need to wrap wxIdManager.

--
Robin

efahl

unread,
Jun 20, 2018, 10:30:42 PM6/20/18
to wxPython-users
But looking at the wxIdManager's interface, it doesn't appear to offer that functionality?  It would need something like a StaticControlId() method to produced values that are not put into the ref counted pool.  Or will your envisioned wrapper add a "don't deallocate" option and re-increment the ref count or something like that?

My current solution is to replace only those problematic NewControlId calls with this...  (I only have a couple dozen, so I don't think I'll run out of space.)

@staticvariable(next=wx.ID_HIGHEST)
def StaticId():
    StaticId.next += 1
    return StaticId.next 

Anders Munch

unread,
Jun 21, 2018, 3:00:26 AM6/21/18
to wxpytho...@googlegroups.com
Robin Dunn:
>>> That means that even with static IDs it is not a problem in something like 95% or better of the cases.
>>95% is not really an impressive number in this context.
>I was being very conservative. I expect that it's much higher than that.

I would have said the same thing about 99.99%. The higher the number, the more obscure and hard to track down the bug will be when it finally does hit you.

Thank you for having championed a better way.

regards, Anders

Anders Munch

unread,
Jun 21, 2018, 3:14:14 AM6/21/18
to wxpytho...@googlegroups.com
Robin Dunn:
> efahl:
>> NewControlId values being deallocated automatically when the widget is destroyed gives rise to a new problem. 

Wait, how does that work? NewControlId is a staticmethod, so how can any widget being destroyed deallocate it?

regards, Anders

Robin Dunn

unread,
Jun 21, 2018, 12:20:17 PM6/21/18
to wxPython-users
It uses wxIdManager, which has methods for reserving and unreserving IDs. When the widget is destroyed it calls the unreserve method and the manager determines if it is one that it has allocated and makes it available again.

--
Robin
 

Robin Dunn

unread,
Jun 21, 2018, 2:23:24 PM6/21/18
to wxPython-users
On Wednesday, June 20, 2018 at 7:30:42 PM UTC-7, efahl wrote:


On Wednesday, June 20, 2018 at 6:46:58 PM UTC-7, Robin Dunn wrote:
On Wednesday, June 20, 2018 at 5:23:47 PM UTC-7, efahl wrote:
NewControlId values being deallocated automatically when the widget is destroyed gives rise to a new problem.  In my context menus, I have some fixed entries ("Expand all" on a tree, for instance).  I keep a data structure, including the id, around and use that when creating the popup menu.  Works great the first time the context menu is built, but the second time I get the error below.  Seems I need some static ids outside the range controlled by the IdManager.  How do I generate those without collisions?

I guess I need to wrap wxIdManager.
 
But looking at the wxIdManager's interface, it doesn't appear to offer that functionality?  It would need something like a StaticControlId() method to produced values that are not put into the ref counted pool.  Or will your envisioned wrapper add a "don't deallocate" option and re-increment the ref count or something like that?


There is another class that is not documented which is used by the widgets to auto unreserve the IDs. I may have to do something with that. We'll see how it goes.

 --
Robin

efahl

unread,
Jun 21, 2018, 7:53:18 PM6/21/18
to wxPython-users
On Thursday, June 21, 2018 at 12:14:14 AM UTC-7, Anders Munch wrote:
What Robin said.  Here's a concrete example (off the top of my head, so bugs++, but you should get the idea).

id = wx.Window.NewControlId() 
item = wx.MenuItem(..., id=id, ...)
menu.Append(item)
window.PopupMenu(menu)
# 'item' is destroyed when the popup is torn down, and 'id' is automagically passed to wx.IdManager.UnreserveId at that time

Anders Munch

unread,
Jun 22, 2018, 8:36:27 AM6/22/18
to wxpytho...@googlegroups.com
> Robin: NewControlId values being deallocated automatically when the widget is destroyed gives rise to a new problem. 
> Me: Wait, how does that work? NewControlId is a staticmethod, so how can any widget being destroyed deallocate it?
> efahl: What Robin said.  Here's a concrete example (off the top of my head, so bugs++, but you should get the idea).

If I understand you correctly, wxMenuItem::~wxMenuItem will happily unreserve an id that the wxMenuItem did not reserve itself in the first place. And presumably wxTimer::~wxTimer and wxWindow::~wxWindow and anything else that takes an id also does this.

That then means that every time I called wx.NewId from module scope thinking I was being frugal and using the same id for all instances, I was actually creating a bug because after the first instance using the id is destroyed, the id becomes unreserved.

There must be some kind of exception for ID_OK et al then, otherwise they would be unreserved in a heartbeat.

I'm beginning to see why wx.NewId was deprecated. It's basically impossible to use correctly, and code that uses it needs to be rewritten to fetch an auto-allocated value with GetId instead. Unless Robin can come up with something clever using wxIdManager, but I don't see how: No matter how you call wxIdManager::ReserveId, it does not stop controls from calling wxIdManager.UnreserveId one time too many.

regards, Anders

efahl

unread,
Jun 23, 2018, 5:09:54 PM6/23/18
to wxPython-users


On Friday, June 22, 2018 at 5:36:27 AM UTC-7, Anders Munch wrote:
That then means that every time I called wx.NewId from module scope thinking I was being frugal and using the same id for all instances, I was actually creating a bug because after the first instance using the id is destroyed, the id becomes unreserved.

No, the ids returned by wx.NewId are not reserved and are outside the pool managed by wx.IdManager, so they cannot be "unreserved." 

> python -c 'import wx ; print(wx.NewId(), wx.Window.NewControlId())'
100 -32000

Anders Munch

unread,
Jun 25, 2018, 8:06:05 AM6/25/18
to wxpytho...@googlegroups.com
efahl:
> No, the ids returned by wx.NewId are not reserved and are outside the pool managed by wx.IdManager, so they cannot be "unreserved." 

I thought as much, but of course that means NewControlId is not a viable replacement for NewId.

regards, Anders

Roland King

unread,
Jun 25, 2018, 8:27:19 AM6/25/18
to wxpytho...@googlegroups.com
I don’t understand your conclusion. NewControlId seems a perfect replacement for NewId. NewId comes from a small pool which, once used, cannot be reused. That makes it unsuitable for a fairly common use case where each new screen/panel makes a load of new widgets each with an ANY id, so NewIds used to run out even as the panels containing them were destroyed. Now ANY draws from a pool (NewControlID) which has the property of ids being returned which means in that case where panels are created and destroyed, the IDs go back in the pool. Unless you make more than the entire universe of IDs at the same time (as a poster here last week was trying to do), you don’t run out.

So the replacement for NewID is to reserve an ID, or more, from NewControlID. It won’t conflict with legacy code which uses NewID, it won’t conflict with any control which now uses a NewControlID. If you never want to release the IDs you choose, you don’t need to do so. If you are done with them, you can release them. You may still conflict with people using static IDs but if that happens they did something wrong in using them and there’s nothing you can do about this in any case.

The only use case I currently can think of by the way for actually bothering to reserve and set your own IDs at all is if you want to guarantee a consecutive range for ease of processing, in all other cases I’d just use ANY and bind to the value the framework gave me.

What sequence of events makes you think that NewControlId is not a viable replacement for NewID?

Anders Munch

unread,
Jun 25, 2018, 11:44:50 AM6/25/18
to wxpytho...@googlegroups.com
Roland King:
> So the replacement for NewID is to reserve an ID, or more, from NewControlID.
> It won’t conflict with legacy code which uses NewID [...]
> What sequence of events makes you think that NewControlId is not a viable replacement for NewID?

I can't very well use NewId in code originally written prior to last week (a.k.a. "legacy code") if it doesn't exist any more.
If wx.NewId is going out, then I have to do something about the >100 occurrences of wx.NewId in the code I'm maintaining. Timers and custom events, mostly.

So the question I was looking for an answer to is this: Can I simply search-replace NewId into NewControlId? And my conclusion was no, that will not work, because the id's will go back into the pool when the first wx thing using them is destroyed, and the second time I create a wx thing using the same id, it's a bug because that id is not actually reserved any more.

For example:

EVT_SYNC_ID = wx.NewId()
class MyFrame(wx.Frame):
def __init__(self, ...):
self._sync_timer = wx.Timer(self, EVT_SYNC_ID)
self.Bind(wx.EVT_TIMER, self.OnSyncTimer, id=EVT_SYNC_ID)

I can't replace wx.NewId with wx.Window.NewControlId here, because when the first MyFrame instance is destroyed, and the timer with it, then EVT_SYNC_ID will be unreserved, and when the second instance is created, it will be using a rogue unreserved id.

NewId is not strictly needed here, there is a rewrite to avoid any explicit allocation:

class MyFrame(wx.Frame):
def __init__(self, ...):
self._sync_timer = wx.Timer(self, -1)
self.Bind(wx.EVT_TIMER, self.OnSyncTimer, id=self._sync_timer.GetId())

I don't like to rewrite working code, but it may come to that.

Wrt. the other typical use, custom events, I think they are safe because in this case the id is never actually passed to wx.Window.__init__ or similar, they're only passed to wx.PyEvent.SetEventType and wx.Window.Connect, and I don't see them unreserving anything.

regards, Anders

Robin Dunn

unread,
Jun 25, 2018, 11:46:52 AM6/25/18
to wxPython-users
On Monday, June 18, 2018 at 12:30:18 AM UTC-7, Marco Prosperi wrote:


Great work! I've just installed and looking around: I get a lot of deprecation warning about NewId. Which is the recommended way of managing ids? Passing wx.ANY in the constructor of objects and then object.GetId() to get the id?

Yes. There is also a wx.NewId replacement on the way for when you need to pre-allocate IDs that may be used more than once.
 
--
Robin

Robin Dunn

unread,
Jun 25, 2018, 11:53:21 AM6/25/18
to wxPython-users
On Monday, June 25, 2018 at 8:44:50 AM UTC-7, Anders Munch wrote:
So the question I was looking for an answer to is this: Can I simply search-replace NewId into NewControlId? And my conclusion was no, that will not work, because the id's will go back into the pool when the first wx thing using them is destroyed, and the second time I create a wx thing using the same id, it's a bug because that id is not actually reserved any more.

For example:

EVT_SYNC_ID = wx.NewId()
class MyFrame(wx.Frame):
     def __init__(self, ...):
        self._sync_timer = wx.Timer(self, EVT_SYNC_ID)
        self.Bind(wx.EVT_TIMER, self.OnSyncTimer, id=EVT_SYNC_ID)

I can't replace wx.NewId with wx.Window.NewControlId here, because when the first MyFrame instance is destroyed, and the timer with it, then EVT_SYNC_ID will be unreserved, and when the second instance is created, it will be using a rogue unreserved id.

NewId is not strictly needed here, there is a rewrite to avoid any explicit allocation:

class MyFrame(wx.Frame):
     def __init__(self, ...):
        self._sync_timer = wx.Timer(self, -1)
        self.Bind(wx.EVT_TIMER, self.OnSyncTimer, id=self._sync_timer.GetId())


Since timers have a GetId method then they can be used as the source argument for Bind. So another style of rewrite could be this, which IMO is a little cleaner:

class MyFrame(wx.Frame): 
     def __init__(self, ...): 
        self._sync_timer = wx.Timer(self, -1) 
        self.Bind(wx.EVT_TIMER, self.OnSyncTimer, self._sync_timer) 


But there is a change on the way (4.0.3 in a day or two) that will provide a better replacement for wx.NewId, so your migration pain will be alleviated a little more with that. See my forthcoming message in this thread for more info about that.

 --
Robin

Robin Dunn

unread,
Jun 25, 2018, 12:00:58 PM6/25/18
to wxPython-users
Hi All,

Last night's snapshot build includes a change for this issue. Please give it a quick test if you are able. I'd like to do a new release ASAP, but I'd like to make sure that it doesn't somehow make things worse before building and finalizing 4.0.3. There are still a couple more minor things on the way related to the new class and function, but what's in the snapshot should work fine for replacing the typical usage of wx.NewId. I still recommend using wx.ID_ANY and obj.GetId() when you can, but wx.NewIdRef will work well when you want to reuse IDs. Here is the changelog item for the change:

* Added wrappers for the wx.WindowIDRef class and added the wx.NewIdRef
  function. These will make it possible to create reserved Window IDs using the
  same mechanism which is used when passing wx.ID_ANY to a widget constructor.
  The object returned by wx.NewIdRef will automatically convert to an int when
  passing it to a window constructor, and can also be used as the source in a
  Bind(). (#896)

Basically, wx.NewIdRef returns a wx.WindowIDRef that has been initialized with wx.IdManager.ReserveId() (same as what wx.Window.NewControlId() uses.) The returned WindowIDRef can be used and reused the same as the integers returned by wx.NewId, but they are safe from ID conflicts with anything else that uses the wx.IdManager or uses wx.ID_ANY.

For the curious, all the details of the change are here: https://github.com/wxWidgets/Phoenix/pull/897

Please post any followups about this to wxPython-dev.

--
Robin
 

Roland King

unread,
Jun 25, 2018, 5:41:33 PM6/25/18
to wxpytho...@googlegroups.com
I just looked at the wx source to see if it really was dumb enough to release window ids which it didn’t assign itself and .. it actually is .. which was an unfortunate design choice. 

http://docs.wxwidgets.org/3.1/overview_windowids.html

That led me to wxWindowIDRef and it was clear you needed to  create and hold one of those with the id if you wanted to use it in wx windows and not have it released for you (ie you effectively manually keep a refcount to it) and then I just saw Robin’s mail this morning saying he’d implemented wxNewID to do exactly that. 

Still don’t see a lot of use cases for not just using wxID_ANY (the only one came to my mind was if you wanted a contiguous range to bind) but that certainly seems to address the issue. I assume if you do allocate yourself a range and want to use them for window ids you’ll have to create a window id ref for them, ie do what the wxNewID does. 

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Paul Keating

unread,
Feb 19, 2019, 3:45:46 PM2/19/19
to wxPython-users
I found this thread looking for solutions to the deprecation message. I was heartened by this exchange:

Before I go on a mass editing spree to replace wx.NewId, can you confirm that wx.Window.NewControlId is the proper replacement?
It is. It uses an internal management class to reserve IDs when used and release them when the widget is destroyed, so even if the internal counter wraps around those IDs that are still active will not be reused.

Perhaps I took this too literally, but when I replaced calls to wx.NewId() with calls to wx.Window.NewControlId() (which is what I thought that meant) I got something far worse than a deprecation warning:


Traceback (most recent call last):
 
File "E:/Users/paulk/Documents/Python Projects/SteelSeriesMacroEditor/ss3me.py", line 177, in on_grid_cell_ctx_menu_click
    menu
.Append(self.menu_popup_ins_keycap, "Insert keystroke event below")
wx
._core.wxAssertionError: C++ assertion "gs_autoIdsRefCount[winid] != ID_FREE" failed at ..\..\src\common\windowid.cpp(110) in `anonymous-namespace'::IncIdRefCount(): id should first be reserved

So clearly there is some intermediate step that I have failed to appreciate. What is it?

Ed Hynan

unread,
Feb 19, 2019, 6:07:40 PM2/19/19
to wxPython-users
On Tue, 19 Feb 2019, Paul Keating wrote:

> I found this thread looking for solutions to the deprecation message. I was
> heartened by this exchange:
>
> Before I go on a mass editing spree to replace wx.NewId, can you confirm
> > that wx.Window.NewControlId is the proper replacement?
>
> It is. It uses an internal management class to reserve IDs when used and
> release them when the widget is destroyed, so even if the internal counter
> wraps around those IDs that are still active will not be reused.
>
>
> Perhaps I took this too literally, but when I replaced calls to wx.NewId()
> with calls to wx.Window.NewControlId() (which is what I thought that meant)
> I got something far worse than a deprecation warning:

Crude but effective (i.e. works for me):

# As of wxPython 4.0.2 (Pheonix) wx.NewId() is deprecated --
# there was a discussion of the on the mailing list, June 2018
def new_wx_id():
try:
# this is a new replacement function for deprecated NewId();
# note that return is not int, but object w/ __int__ method
return wx.NewIdRef()
except AttributeError:
return wx.NewId()

Karsten Hilbert

unread,
Feb 19, 2019, 6:31:41 PM2/19/19
to wxpytho...@googlegroups.com
> Crude but effective (i.e. works for me):
>
> # As of wxPython 4.0.2 (Pheonix) wx.NewId() is deprecated --
> # there was a discussion of the on the mailing list, June 2018
> def new_wx_id():
> try:
> # this is a new replacement function for deprecated NewId();
> # note that return is not int, but object w/ __int__ method
> return wx.NewIdRef()
> except AttributeError:
> return wx.NewId()

Or, monkey-patching, right after import wx in the "main script" (untested):

try:
wx.NewIdRef
except AttributeError:
wx.NewIdRef = wx.NewId

Karsten

john fabiani`

unread,
Feb 19, 2019, 6:43:45 PM2/19/19
to wxpytho...@googlegroups.com
Sorry for jumping in - but a thought occurred to me.  The old wx.NewId() returned a positive int and the wx.Window.NewControlId() returns a negative int.  With that in mind does having a positive ID for a control cause any issues?  For example my menu items have very large ID numbers i.e. 140692866601464. Could the positive numbers cause seg faults or some other issues?  Mixing the positive and negative numbers sounds like an issue??????


Johnf

Robin Dunn

unread,
Feb 19, 2019, 7:47:30 PM2/19/19
to wxPython-users
On Tuesday, February 19, 2019 at 12:45:46 PM UTC-8, Paul Keating wrote:
I found this thread looking for solutions to the deprecation message. I was heartened by this exchange:

Before I go on a mass editing spree to replace wx.NewId, can you confirm that wx.Window.NewControlId is the proper replacement?
It is. It uses an internal management class to reserve IDs when used and release them when the widget is destroyed, so even if the internal counter wraps around those IDs that are still active will not be reused.

Perhaps I took this too literally, but when I replaced calls to wx.NewId() with calls to wx.Window.NewControlId() (which is what I thought that meant) I got something far worse than a deprecation warning:


There has been some other discussions since then that uncovered some more information, and prompted some new additions in newer wxPython releases. I summarized a few days ago what has changed, why, and what should be used going forward in the next release of the MigrationGuide. (The software changes were made in 4.0.3 however, so there is no need to wait for the next release.) 

See the last section of the Migration Guide in the preview build of the docs: https://wxpython.org/Phoenix/docs/html/MigrationGuide.html#wx-newid-is-deprecated

--
Robin

Robin Dunn

unread,
Feb 19, 2019, 7:49:40 PM2/19/19
to wxPython-users
On Tuesday, February 19, 2019 at 3:43:45 PM UTC-8, johnf wrote:
Sorry for
      jumping in - but a thought occurred to me.  The old wx.NewId()
      returned a positive int and the wx.Window.NewControlId() returns a
      negative int.  With that in mind does having a positive ID for a
      control cause any issues?  For example my menu items have very
      large ID numbers i.e. 140692866601464. Could the positive numbers
      cause seg faults or some other issues?  Mixing the positive and
      negative numbers sounds like an issue??????

No. That was just the initial mechanism for keeping the managed IDs and the unmanaged IDs were kept from stomping on each other for as long as possible.
 
--
Robin


Reply all
Reply to author
Forward
0 new messages