mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-04 11:00:07 +01:00
Merge pull request #897 from RobinD42/add-windowidref
Add wx.WindowIDRef and wx.NewIdRef
This commit is contained in:
10
CHANGES.rst
10
CHANGES.rst
@@ -53,7 +53,7 @@ Pip: ``pip install wxPython==4.0.3``
|
|||||||
Changes in this release include the following:
|
Changes in this release include the following:
|
||||||
|
|
||||||
* Fixed a linking problem on macOS. The new waf added an explicit link to the
|
* Fixed a linking problem on macOS. The new waf added an explicit link to the
|
||||||
Python shared library which menat that it would try to load it at runtime,
|
Python shared library which meant that it would try to load it at runtime,
|
||||||
even if a different Python (such as Anaconda, EDM or Homebrew) was used to
|
even if a different Python (such as Anaconda, EDM or Homebrew) was used to
|
||||||
import wxPython. This, of course, caused runtime errors. (#892)
|
import wxPython. This, of course, caused runtime errors. (#892)
|
||||||
|
|
||||||
@@ -63,6 +63,14 @@ Changes in this release include the following:
|
|||||||
|
|
||||||
* Added Vagrant configuration for Fedora-28. Removed Fedora-23 (#884)
|
* Added Vagrant configuration for Fedora-28. Removed Fedora-23 (#884)
|
||||||
|
|
||||||
|
* 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().
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
4.0.2 "Cute as a June bug!"
|
4.0.2 "Cute as a June bug!"
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|||||||
@@ -290,6 +290,7 @@ def main(wxDir, args):
|
|||||||
"--enable-optimise",
|
"--enable-optimise",
|
||||||
"--disable-debugreport",
|
"--disable-debugreport",
|
||||||
"--enable-uiactionsim",
|
"--enable-uiactionsim",
|
||||||
|
"--enable-autoidman",
|
||||||
]
|
]
|
||||||
|
|
||||||
if sys.platform.startswith("darwin"):
|
if sys.platform.startswith("darwin"):
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class AppI18N(sc.SizedFrame):
|
|||||||
|
|
||||||
# file menu
|
# file menu
|
||||||
fileMenu = wx.Menu()
|
fileMenu = wx.Menu()
|
||||||
closeMenuItem = fileMenu.Append(wx.NewId(),
|
closeMenuItem = fileMenu.Append(wx.ID_ANY,
|
||||||
_(u"Close"),
|
_(u"Close"),
|
||||||
_(u"Close the application"))
|
_(u"Close the application"))
|
||||||
self.Bind(wx.EVT_MENU, self.onClose, closeMenuItem)
|
self.Bind(wx.EVT_MENU, self.onClose, closeMenuItem)
|
||||||
@@ -32,7 +32,7 @@ class AppI18N(sc.SizedFrame):
|
|||||||
|
|
||||||
# edit menu
|
# edit menu
|
||||||
manageMenu = wx.Menu()
|
manageMenu = wx.Menu()
|
||||||
manageSomethingMenuItem = manageMenu.Append(wx.NewId(),
|
manageSomethingMenuItem = manageMenu.Append(wx.ID_ANY,
|
||||||
_(u"Edit something"),
|
_(u"Edit something"),
|
||||||
_(u"Edit an entry of something"))
|
_(u"Edit an entry of something"))
|
||||||
self.Bind(wx.EVT_MENU, self.doEditSomething, manageSomethingMenuItem)
|
self.Bind(wx.EVT_MENU, self.doEditSomething, manageSomethingMenuItem)
|
||||||
@@ -41,7 +41,7 @@ class AppI18N(sc.SizedFrame):
|
|||||||
|
|
||||||
# help menu
|
# help menu
|
||||||
helpMenu = wx.Menu()
|
helpMenu = wx.Menu()
|
||||||
aboutMenuItem = helpMenu.Append(wx.NewId(),
|
aboutMenuItem = helpMenu.Append(wx.ID_ANY,
|
||||||
_(u"&About"),
|
_(u"&About"),
|
||||||
_(u"About the program"))
|
_(u"About the program"))
|
||||||
self.Bind(wx.EVT_MENU, self.doAboutBox, aboutMenuItem)
|
self.Bind(wx.EVT_MENU, self.doAboutBox, aboutMenuItem)
|
||||||
|
|||||||
@@ -2378,6 +2378,7 @@
|
|||||||
"NavigationKeyEventFlags":"wx.NavigationKeyEvent.",
|
"NavigationKeyEventFlags":"wx.NavigationKeyEvent.",
|
||||||
"NewEventType":"wx.",
|
"NewEventType":"wx.",
|
||||||
"NewId":"wx.",
|
"NewId":"wx.",
|
||||||
|
"NewIdRef":"wx.",
|
||||||
"NonOwnedWindow":"wx.",
|
"NonOwnedWindow":"wx.",
|
||||||
"Notebook":"wx.",
|
"Notebook":"wx.",
|
||||||
"NotebookNameStr":"wx.",
|
"NotebookNameStr":"wx.",
|
||||||
@@ -6674,6 +6675,7 @@
|
|||||||
"WindowDestroyEvent":"wx.",
|
"WindowDestroyEvent":"wx.",
|
||||||
"WindowDisabler":"wx.",
|
"WindowDisabler":"wx.",
|
||||||
"WindowID":"wx.",
|
"WindowID":"wx.",
|
||||||
|
"WindowIDRef":"wx.",
|
||||||
"WindowModalDialogEvent":"wx.",
|
"WindowModalDialogEvent":"wx.",
|
||||||
"WindowVariant":"wx.",
|
"WindowVariant":"wx.",
|
||||||
"WithImages":"wx.",
|
"WithImages":"wx.",
|
||||||
|
|||||||
@@ -408,6 +408,6 @@ user-specified identifiers which must be always positive.
|
|||||||
You can use ``wx.ID_HIGHEST`` to determine the number above which it is
|
You can use ``wx.ID_HIGHEST`` to determine the number above which it is
|
||||||
safe to define your own identifiers. Or, you can use identifiers below
|
safe to define your own identifiers. Or, you can use identifiers below
|
||||||
``wx.ID_LOWEST``. Finally, you can allocate identifiers dynamically
|
``wx.ID_LOWEST``. Finally, you can allocate identifiers dynamically
|
||||||
using :func:`wx.NewId` function too. If you use :func:`wx.NewId`
|
using :func:`wx.NewIdRef` function too. If you use :func:`wx.NewIdRef`
|
||||||
consistently in your application, you can be sure that your
|
consistently in your application, you can be sure that your
|
||||||
identifiers don't conflict accidentally.
|
identifiers don't conflict accidentally.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ used by the user code and also are sometimes used by wxPython
|
|||||||
itself. These reserved identifiers are all in the range between
|
itself. These reserved identifiers are all in the range between
|
||||||
``wx.ID_LOWEST`` and ``wx.ID_HIGHEST`` and, accordingly, the user code
|
``wx.ID_LOWEST`` and ``wx.ID_HIGHEST`` and, accordingly, the user code
|
||||||
should avoid defining its own constants in this range (e.g. by using
|
should avoid defining its own constants in this range (e.g. by using
|
||||||
:func:`wx.NewId` ()).
|
:func:`wx.NewIdRef` ()).
|
||||||
|
|
||||||
Refer to :ref:`the list of stock items <stock items>` for the subset
|
Refer to :ref:`the list of stock items <stock items>` for the subset
|
||||||
of standard IDs which are stock IDs as well.
|
of standard IDs which are stock IDs as well.
|
||||||
|
|||||||
14
etg/utils.py
14
etg/utils.py
@@ -87,6 +87,15 @@ def run():
|
|||||||
c.addPyMethod('__enter__', '(self)', 'return self')
|
c.addPyMethod('__enter__', '(self)', 'return self')
|
||||||
c.addPyMethod('__exit__', '(self, exc_type, exc_val, exc_tb)', 'pass')
|
c.addPyMethod('__exit__', '(self, exc_type, exc_val, exc_tb)', 'pass')
|
||||||
|
|
||||||
|
f = module.find('wxNewId')
|
||||||
|
f.clearDeprecated()
|
||||||
|
f.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 :func:`wx.NewIdRef`.""".replace('\n', ' ')
|
||||||
|
|
||||||
for funcname in ['wxBell',
|
for funcname in ['wxBell',
|
||||||
'wxBeginBusyCursor',
|
'wxBeginBusyCursor',
|
||||||
@@ -98,9 +107,10 @@ def run():
|
|||||||
'wxGetKeyState',
|
'wxGetKeyState',
|
||||||
'wxGetMouseState',
|
'wxGetMouseState',
|
||||||
]:
|
]:
|
||||||
c = module.find(funcname)
|
f = module.find(funcname)
|
||||||
c.mustHaveApp()
|
f.mustHaveApp()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
tools.doCommonTweaks(module)
|
tools.doCommonTweaks(module)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import etgtools
|
import etgtools
|
||||||
import etgtools.tweaker_tools as tools
|
import etgtools.tweaker_tools as tools
|
||||||
|
from etgtools import ClassDef, MethodDef, ParamDef
|
||||||
|
|
||||||
PACKAGE = "wx"
|
PACKAGE = "wx"
|
||||||
MODULE = "_core"
|
MODULE = "_core"
|
||||||
@@ -30,10 +31,82 @@ def run():
|
|||||||
# Tweak the parsed meta objects in the module object as needed for
|
# Tweak the parsed meta objects in the module object as needed for
|
||||||
# customizing the generated code and docstrings.
|
# customizing the generated code and docstrings.
|
||||||
|
|
||||||
|
c = module.find('wxIdManager')
|
||||||
|
assert isinstance(c, etgtools.ClassDef)
|
||||||
|
# no tweaks needed for this class
|
||||||
|
|
||||||
|
|
||||||
|
# wxWindowIDRef is not documented (and probably rightly so) but we're going
|
||||||
|
# to use it from Python anyway to help with preallocating IDs in a way that
|
||||||
|
# allows them to be reused and be also be protected from conflicts from
|
||||||
|
# other auto allocated IDs.
|
||||||
|
|
||||||
|
# First, add defintions of the existing C++ class and its elements
|
||||||
|
klass = ClassDef(name='wxWindowIDRef', bases = [],
|
||||||
|
briefDoc="""\
|
||||||
|
A wxWindowIDRef object wraps an ID value and marks it as being in-use until all references to that ID are gone.
|
||||||
|
""",
|
||||||
|
items = [
|
||||||
|
MethodDef(name='wxWindowIDRef', className='wxWindowIDRef', isCtor=True,
|
||||||
|
briefDoc='Default constructor',
|
||||||
|
overloads=[
|
||||||
|
MethodDef(name='wxWindowIDRef', className='wxWindowIDRef', isCtor=True,
|
||||||
|
briefDoc='Create reference from an ID',
|
||||||
|
items=[ ParamDef(type='int', name='id') ]),
|
||||||
|
|
||||||
|
MethodDef(name='wxWindowIDRef', className='wxWindowIDRef', isCtor=True,
|
||||||
|
briefDoc='Copy an ID reference',
|
||||||
|
items=[ ParamDef(type='const wxWindowIDRef&', name='idref') ]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
MethodDef(name='~wxWindowIDRef', className='wxWindowIDRef', isDtor=True),
|
||||||
|
|
||||||
|
MethodDef(type='int', name='GetValue',
|
||||||
|
briefDoc='Get the ID value'),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Now tweak it a bit
|
||||||
|
klass.addCppMethod('int', 'GetId', '()',
|
||||||
|
doc="Alias for GetValue allowing the IDRef to be passed as the source parameter to :meth:`wx.EvtHandler.Bind`.",
|
||||||
|
body="""\
|
||||||
|
return self->GetValue();
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
klass.addCppMethod('int', '__int__', '()',
|
||||||
|
doc="Alias for GetValue allowing the IDRef to be passed as the WindowID parameter when creating widgets or etc.",
|
||||||
|
body="""\
|
||||||
|
return self->GetValue();
|
||||||
|
""")
|
||||||
|
|
||||||
|
klass.addPyMethod('__repr__', '(self)', 'return "WindowIDRef: {}".format(self.GetId())')
|
||||||
|
|
||||||
|
# and finish it up by adding it to the module
|
||||||
|
module.addItem(klass)
|
||||||
|
|
||||||
|
# Now, let's add a new Python function to the global scope that reserves an
|
||||||
|
# ID (or range) and returns a ref object for it.
|
||||||
|
module.addPyFunction('NewIdRef', '(count=1)',
|
||||||
|
doc="""\
|
||||||
|
Reserves a new Window ID (or range of WindowIDs) and returns a
|
||||||
|
:class:`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 :func:`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.
|
||||||
|
""",
|
||||||
|
body="""\
|
||||||
|
if count == 1:
|
||||||
|
return WindowIDRef(IdManager.ReserveId())
|
||||||
|
else:
|
||||||
|
start = IdManager.ReserveId(count)
|
||||||
|
IDRefs = []
|
||||||
|
for id in range(start, start+count):
|
||||||
|
IDRefs.append(WindowIDRef(id))
|
||||||
|
return IDRefs
|
||||||
|
""")
|
||||||
|
|
||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
tools.doCommonTweaks(module)
|
tools.doCommonTweaks(module)
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ from sphinxtools.utilities import findDescendants
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# These classes simply hold various bits of information about the classes,
|
# These classes simply hold various bits of information about the classes,
|
||||||
# methods, functions and other items in the C/C++ API being wrapped.
|
# methods, functions and other items in the C/C++ API being wrapped.
|
||||||
#
|
|
||||||
# NOTE: Currently very little is being done with the docstrings. They can
|
|
||||||
# either be reprocessed later by the document generator or we can do more
|
|
||||||
# tinkering with them here. It just depends on decisions not yet made...
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
class BaseDef(object):
|
class BaseDef(object):
|
||||||
@@ -83,10 +79,21 @@ class BaseDef(object):
|
|||||||
itemid = item.get('id')
|
itemid = item.get('id')
|
||||||
if itemid and itemid.startswith('deprecated'):
|
if itemid and itemid.startswith('deprecated'):
|
||||||
self.deprecated = True
|
self.deprecated = True
|
||||||
break
|
return
|
||||||
|
|
||||||
if self.deprecated:
|
|
||||||
break
|
def clearDeprecated(self):
|
||||||
|
"""
|
||||||
|
Remove the deprecation notice from the detailedDoc, if any, and reset
|
||||||
|
self.deprecated to False.
|
||||||
|
"""
|
||||||
|
self.deprecated = False
|
||||||
|
for para in self.detailedDoc:
|
||||||
|
for item in para.iter():
|
||||||
|
itemid = item.get('id')
|
||||||
|
if itemid and itemid.startswith('deprecated'):
|
||||||
|
self.detailedDoc.remove(para)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def ignore(self, val=True):
|
def ignore(self, val=True):
|
||||||
|
|||||||
@@ -1979,13 +1979,13 @@ class XMLDocString(object):
|
|||||||
else:
|
else:
|
||||||
raise Exception('Unhandled docstring kind for %s'%xml_item.__class__.__name__)
|
raise Exception('Unhandled docstring kind for %s'%xml_item.__class__.__name__)
|
||||||
|
|
||||||
# Some of the Exctractors (xml item) will set deprecated themselves, in which case it is set as a
|
# Some of the Extractors (xml item) will set deprecated themselves, in which case it is set as a
|
||||||
# non-empty string. In such cases, this branch will insert a deprecated section into the xml tree
|
# non-empty string. In such cases, this branch will insert a deprecated section into the xml tree
|
||||||
# so that the Node Tree (see classes above) will generate the deprecated tag on their own in self.RecurseXML
|
# so that the Node Tree (see classes above) will generate the deprecated tag on their own in self.RecurseXML
|
||||||
if hasattr(xml_item, 'deprecated') and xml_item.deprecated and isinstance(xml_item.deprecated, string_base):
|
if hasattr(xml_item, 'deprecated') and xml_item.deprecated and isinstance(xml_item.deprecated, string_base):
|
||||||
element = et.Element('deprecated', kind='deprecated')
|
element = et.Element('deprecated', kind='deprecated')
|
||||||
element.text = xml_item.deprecated
|
element.text = xml_item.deprecated
|
||||||
|
|
||||||
deprecated_section = Section(element, None, self.kind, self.is_overload, self.share_docstrings)
|
deprecated_section = Section(element, None, self.kind, self.is_overload, self.share_docstrings)
|
||||||
self.root.AddSection(deprecated_section)
|
self.root.AddSection(deprecated_section)
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,10 @@ void wxPyCoreModuleInject(PyObject* moduleDict)
|
|||||||
PyList_Append(PlatformInfo, obj);
|
PyList_Append(PlatformInfo, obj);
|
||||||
Py_DECREF(obj);
|
Py_DECREF(obj);
|
||||||
|
|
||||||
|
#if wxUSE_AUTOID_MANAGEMENT
|
||||||
|
_AddInfoString("autoidman");
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef _AddInfoString
|
#undef _AddInfoString
|
||||||
|
|
||||||
PyObject* PlatformInfoTuple = PyList_AsTuple(PlatformInfo);
|
PyObject* PlatformInfoTuple = PyList_AsTuple(PlatformInfo);
|
||||||
|
|||||||
@@ -1,18 +1,64 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import wx
|
import wx
|
||||||
|
from unittests import wtc
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
class IdManagerTest(unittest.TestCase):
|
class IdManagerTest(wtc.WidgetTestCase):
|
||||||
|
|
||||||
def test_idManager(self):
|
def test_idManager(self):
|
||||||
id = wx.IdManager.ReserveId(5)
|
id = wx.IdManager.ReserveId(5)
|
||||||
self.assertTrue(id != wx.ID_NONE)
|
self.assertTrue(id != wx.ID_NONE)
|
||||||
|
|
||||||
wx.IdManager.UnreserveId(id, 5)
|
wx.IdManager.UnreserveId(id, 5)
|
||||||
|
|
||||||
|
|
||||||
|
def test_newIdRef01(self):
|
||||||
|
id = wx.NewIdRef()
|
||||||
|
assert isinstance(id, wx.WindowIDRef)
|
||||||
|
id.GetValue()
|
||||||
|
id.GetId()
|
||||||
|
id.__int__()
|
||||||
|
|
||||||
|
|
||||||
|
def test_newIdRef02(self):
|
||||||
|
refs = wx.NewIdRef(5)
|
||||||
|
assert len(refs) == 5
|
||||||
|
for ref in refs:
|
||||||
|
assert isinstance(ref, wx.WindowIDRef)
|
||||||
|
|
||||||
|
|
||||||
|
def test_newIdRef03(self):
|
||||||
|
"""Check that Auto ID Management is enabled (--enable-autoidman)"""
|
||||||
|
# This test is expected to fail if autoID mangagement is turned on
|
||||||
|
# because a reference to the ID is not being saved, so it will be
|
||||||
|
# unreserved when the first widget is destroyed.
|
||||||
|
|
||||||
|
id = wx.Window.NewControlId()
|
||||||
|
b = wx.Button(self.frame, id, 'button')
|
||||||
|
b.Destroy()
|
||||||
|
|
||||||
|
self.myYield()
|
||||||
|
|
||||||
|
with self.assertRaises(wx.wxAssertionError):
|
||||||
|
b = wx.Button(self.frame, id, 'button')
|
||||||
|
b.Destroy()
|
||||||
|
|
||||||
|
|
||||||
|
def test_newIdRef04(self):
|
||||||
|
"""Ensure that an ID can be used more than once"""
|
||||||
|
id = wx.NewIdRef() # but using this one should succeed
|
||||||
|
|
||||||
|
b = wx.Button(self.frame, id, 'button')
|
||||||
|
b.Destroy()
|
||||||
|
|
||||||
|
self.myYield()
|
||||||
|
|
||||||
|
b = wx.Button(self.frame, id, 'button')
|
||||||
|
b.Destroy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user