Merge pull request #239 from RobinD42/propgrid-more-fixes

Lots of propgrid fixes
This commit is contained in:
Robin Dunn
2017-03-14 19:50:08 -07:00
committed by GitHub
20 changed files with 1331 additions and 121 deletions

View File

@@ -3,13 +3,14 @@
import sys
import time
import math
import os
import os.path
import wx
import wx.adv
import wx.propgrid as wxpg
from six import exec_
_ = wx.GetTranslation
import wx.propgrid as wxpg
############################################################################
@@ -61,12 +62,12 @@ class ValueObject:
pass
class IntProperty2(wxpg.PyProperty):
class IntProperty2(wxpg.PGProperty):
"""\
This is a simple re-implementation of wxIntProperty.
"""
def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):
wxpg.PyProperty.__init__(self, label, name)
def __init__(self, label, name = wxpg.PG_LABEL, value=0):
wxpg.PGProperty.__init__(self, label, name)
self.SetValue(value)
def GetClassName(self):
@@ -119,11 +120,11 @@ class IntProperty2(wxpg.PyProperty):
return (True, value)
class SizeProperty(wxpg.PyProperty):
class SizeProperty(wxpg.PGProperty):
""" Demonstrates a property with few children.
"""
def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=wx.Size(0, 0)):
wxpg.PyProperty.__init__(self, label, name)
def __init__(self, label, name = wxpg.PG_LABEL, value=wx.Size(0, 0)):
wxpg.PGProperty.__init__(self, label, name)
value = self._ConvertValue(value)
@@ -168,7 +169,7 @@ class SizeProperty(wxpg.PyProperty):
return size
class DirsProperty(wxpg.PyArrayStringProperty):
class DirsProperty(wxpg.ArrayStringProperty):
""" Sample of a custom custom ArrayStringProperty.
Because currently some of the C++ helpers from wxArrayStringProperty
@@ -176,8 +177,8 @@ class DirsProperty(wxpg.PyArrayStringProperty):
a bit 'manually'. Which is not too bad since Python has excellent
string and list manipulation facilities.
"""
def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):
wxpg.PyArrayStringProperty.__init__(self, label, name, value)
def __init__(self, label, name = wxpg.PG_LABEL, value=[]):
wxpg.ArrayStringProperty.__init__(self, label, name, value)
# Set default delimiter
self.SetAttribute("Delimiter", ',')
@@ -192,8 +193,7 @@ class DirsProperty(wxpg.PyArrayStringProperty):
self.GenerateValueAsString()
def DoSetAttribute(self, name, value):
# Proper way to call same method from super class
retval = self.CallSuperMethod("DoSetAttribute", name, value)
retval = super(DirsProperty, self).DoSetAttribute(name, value)
#
# Must re-generate cached string when delimiter changes
@@ -225,7 +225,7 @@ class DirsProperty(wxpg.PyArrayStringProperty):
delim = self.GetAttribute("Delimiter")
if delim == '"' or delim == "'":
# Proper way to call same method from super class
return self.CallSuperMethod("StringToValue", text, 0)
return super(DirsProperty, self).StringToValue(text, 0)
v = [a.strip() for a in text.split(delim)]
return (True, v)
@@ -269,7 +269,7 @@ class PyObjectPropertyValue:
return ' - '.join(self.ls)
class PyObjectProperty(wxpg.PyProperty):
class PyObjectProperty(wxpg.PGProperty):
"""\
Another simple example. This time our value is a PyObject.
@@ -278,8 +278,8 @@ class PyObjectProperty(wxpg.PyProperty):
or wxObject based. Dictionary, None, or any user-specified Python
class is allowed.
"""
def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):
wxpg.PyProperty.__init__(self, label, name)
def __init__(self, label, name = wxpg.PG_LABEL, value=None):
wxpg.PGProperty.__init__(self, label, name)
self.SetValue(value)
def GetClassName(self):
@@ -299,9 +299,9 @@ class PyObjectProperty(wxpg.PyProperty):
return (True, v)
class SampleMultiButtonEditor(wxpg.PyTextCtrlEditor):
class SampleMultiButtonEditor(wxpg.PGTextCtrlEditor):
def __init__(self):
wxpg.PyTextCtrlEditor.__init__(self)
wxpg.PGTextCtrlEditor.__init__(self)
def CreateControls(self, propGrid, property, pos, sz):
# Create and populate buttons-subwindow
@@ -314,7 +314,7 @@ class SampleMultiButtonEditor(wxpg.PyTextCtrlEditor):
buttons.AddBitmapButton(wx.ArtProvider.GetBitmap(wx.ART_FOLDER))
# Create the 'primary' editor control (textctrl in this case)
wnd = self.CallSuperMethod("CreateControls",
wnd = super(SampleMultiButtonEditor, self).CreateControls(
propGrid,
property,
pos,
@@ -340,25 +340,25 @@ class SampleMultiButtonEditor(wxpg.PyTextCtrlEditor):
if evtId == buttons.GetButtonId(0):
# Do something when the first button is pressed
wx.LogDebug("First button pressed");
wx.LogDebug("First button pressed")
return False # Return false since value did not change
if evtId == buttons.GetButtonId(1):
# Do something when the second button is pressed
wx.MessageBox("Second button pressed");
wx.MessageBox("Second button pressed")
return False # Return false since value did not change
if evtId == buttons.GetButtonId(2):
# Do something when the third button is pressed
wx.MessageBox("Third button pressed");
wx.MessageBox("Third button pressed")
return False # Return false since value did not change
return self.CallSuperMethod("OnEvent", propGrid, prop, ctrl, event)
return super(SampleMultiButtonEditor, self).OnEvent(propGrid, prop, ctrl, event)
class SingleChoiceDialogAdapter(wxpg.PyEditorDialogAdapter):
""" This demonstrates use of wxpg.PyEditorDialogAdapter.
class SingleChoiceDialogAdapter(wxpg.PGEditorDialogAdapter):
""" This demonstrates use of wxpg.PGEditorDialogAdapter.
"""
def __init__(self, choices):
wxpg.PyEditorDialogAdapter.__init__(self)
wxpg.PGEditorDialogAdapter.__init__(self)
self.choices = choices
def DoShowDialog(self, propGrid, property):
@@ -371,16 +371,16 @@ class SingleChoiceDialogAdapter(wxpg.PyEditorDialogAdapter):
return False;
class SingleChoiceProperty(wxpg.PyStringProperty):
def __init__(self, label, name=wxpg.LABEL_AS_NAME, value=''):
wxpg.PyStringProperty.__init__(self, label, name, value)
class SingleChoiceProperty(wxpg.StringProperty):
def __init__(self, label, name=wxpg.PG_LABEL, value=''):
wxpg.StringProperty.__init__(self, label, name, value)
# Prepare choices
dialog_choices = []
dialog_choices.append("Cat");
dialog_choices.append("Dog");
dialog_choices.append("Gibbon");
dialog_choices.append("Otter");
dialog_choices.append("Cat")
dialog_choices.append("Dog")
dialog_choices.append("Gibbon")
dialog_choices.append("Otter")
self.dialog_choices = dialog_choices
@@ -393,14 +393,14 @@ class SingleChoiceProperty(wxpg.PyStringProperty):
return SingleChoiceDialogAdapter(self.dialog_choices)
class TrivialPropertyEditor(wxpg.PyEditor):
class TrivialPropertyEditor(wxpg.PGEditor):
"""\
This is a simple re-creation of TextCtrlWithButton. Note that it does
not take advantage of wx.TextCtrl and wx.Button creation helper functions
in wx.PropertyGrid.
"""
def __init__(self):
wxpg.PyEditor.__init__(self)
wxpg.PGEditor.__init__(self)
def CreateControls(self, propgrid, property, pos, sz):
""" Create the actual wxPython controls here for editing the
@@ -564,12 +564,12 @@ class LargeImagePickerCtrl(wx.Panel):
return self.tc.GetLastPosition()
class LargeImageEditor(wxpg.PyEditor):
class LargeImageEditor(wxpg.PGEditor):
"""\
Double-height text-editor with image in front.
"""
def __init__(self):
wxpg.PyEditor.__init__(self)
wxpg.PGEditor.__init__(self)
def CreateControls(self, propgrid, property, pos, sz):
try:
@@ -723,14 +723,17 @@ class TestPanel( wx.Panel ):
pg.Append( wxpg.IntProperty("Int",value=100) )
pg.Append( wxpg.FloatProperty("Float",value=100.0) )
pg.Append( wxpg.BoolProperty("Bool",value=True) )
pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )
pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)
boolprop = pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )
pg.SetPropertyAttribute(
"Bool_with_Checkbox", # You can find the property by name,
#boolprop, # or give the property object itself.
"UseCheckbox", True) # The attribute name and value
pg.Append( wxpg.PropertyCategory("2 - More Properties") )
pg.Append( wxpg.LongStringProperty("LongString",
value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin."))
pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )
pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )
value="This is a\nmulti-line string\nwith\ttabs\nmixed\tin."))
pg.Append( wxpg.DirProperty("Dir",value=r"C:\Windows") )
pg.Append( wxpg.FileProperty("File",value=r"C:\Windows\system.ini") )
pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )
pg.Append( wxpg.EnumProperty("Enum","Enum",
@@ -763,9 +766,9 @@ class TestPanel( wx.Panel ):
pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )
pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH,
"C:\\Program Files\\Internet Explorer" )
r"C:\Program Files\Internet Explorer" )
pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE,
wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )
wx.adv.DP_DROPDOWN|wx.adv.DP_SHOWCENTURY )
pg.Append( wxpg.PropertyCategory("5 - Custom Properties and Editors") )
pg.Append( IntProperty2("IntProperty2", value=1024) )
@@ -779,8 +782,8 @@ class TestPanel( wx.Panel ):
pg.SetPropertyAttribute("Dirs2", "Delimiter", '"')
# SampleMultiButtonEditor
pg.Append( wxpg.LongStringProperty("MultipleButtons") );
pg.SetPropertyEditor("MultipleButtons", "SampleMultiButtonEditor");
pg.Append( wxpg.LongStringProperty("MultipleButtons") )
pg.SetPropertyEditor("MultipleButtons", "SampleMultiButtonEditor")
pg.Append( SingleChoiceProperty("SingleChoiceProperty") )
# Custom editor samples
@@ -857,7 +860,7 @@ class TestPanel( wx.Panel ):
d = self.pg.GetPropertyValues(inc_attributes=True)
ss = []
for k,v in d.iteritems():
for k,v in d.items():
v = repr(v)
if not v or v[0] != '<':
if k.startswith('@'):
@@ -865,22 +868,22 @@ class TestPanel( wx.Panel ):
else:
ss.append('obj.%s = %s'%(k,v))
dlg = MemoDialog(self,
with MemoDialog(self,
"Enter Content for Object Used in SetPropertyValues",
'\n'.join(ss)) # default_object_content1
'\n'.join(ss)) as dlg: # default_object_content1
if dlg.ShowModal() == wx.ID_OK:
import datetime
sandbox = {'obj':ValueObject(),
'wx':wx,
'datetime':datetime}
exec_(dlg.tc.GetValue(), sandbox)
t_start = time.time()
#print(sandbox['obj'].__dict__)
self.pg.SetPropertyValues(sandbox['obj'])
t_end = time.time()
self.log.write('SetPropertyValues finished in %.0fms\n' %
((t_end-t_start)*1000.0))
if dlg.ShowModal() == wx.ID_OK:
import datetime
sandbox = {'obj':ValueObject(),
'wx':wx,
'datetime':datetime}
exec_(dlg.tc.GetValue(), sandbox)
t_start = time.time()
#print(sandbox['obj'].__dict__)
self.pg.SetPropertyValues(sandbox['obj'])
t_end = time.time()
self.log.write('SetPropertyValues finished in %.0fms\n' %
((t_end-t_start)*1000.0))
except:
import traceback
traceback.print_exc()
@@ -892,10 +895,11 @@ class TestPanel( wx.Panel ):
t_end = time.time()
self.log.write('GetPropertyValues finished in %.0fms\n' %
((t_end-t_start)*1000.0))
ss = ['%s: %s'%(k,repr(v)) for k,v in d.iteritems()]
dlg = MemoDialog(self,"GetPropertyValues Result",
'Contents of resulting dictionary:\n\n'+'\n'.join(ss))
dlg.ShowModal()
ss = ['%s: %s'%(k,repr(v)) for k,v in d.items()]
with MemoDialog(self,"GetPropertyValues Result",
'Contents of resulting dictionary:\n\n'+'\n'.join(ss)) as dlg:
dlg.ShowModal()
except:
import traceback
traceback.print_exc()
@@ -907,25 +911,25 @@ class TestPanel( wx.Panel ):
t_end = time.time()
self.log.write('GetPropertyValues(as_strings=True) finished in %.0fms\n' %
((t_end-t_start)*1000.0))
ss = ['%s: %s'%(k,repr(v)) for k,v in d.iteritems()]
dlg = MemoDialog(self,"GetPropertyValues Result",
'Contents of resulting dictionary:\n\n'+'\n'.join(ss))
dlg.ShowModal()
ss = ['%s: %s'%(k,repr(v)) for k,v in d.items()]
with MemoDialog(self,"GetPropertyValues Result",
'Contents of resulting dictionary:\n\n'+'\n'.join(ss)) as dlg:
dlg.ShowModal()
except:
import traceback
traceback.print_exc()
def OnAutoFill(self,event):
try:
dlg = MemoDialog(self,"Enter Content for Object Used for AutoFill",default_object_content1)
if dlg.ShowModal() == wx.ID_OK:
sandbox = {'object':ValueObject(),'wx':wx}
exec_(dlg.tc.GetValue(), sandbox)
t_start = time.time()
self.pg.AutoFill(sandbox['object'])
t_end = time.time()
self.log.write('AutoFill finished in %.0fms\n' %
((t_end-t_start)*1000.0))
with MemoDialog(self,"Enter Content for Object Used for AutoFill",default_object_content1) as dlg:
if dlg.ShowModal() == wx.ID_OK:
sandbox = {'object':ValueObject(),'wx':wx}
exec_(dlg.tc.GetValue(), sandbox)
t_start = time.time()
self.pg.AutoFill(sandbox['object'])
t_end = time.time()
self.log.write('AutoFill finished in %.0fms\n' %
((t_end-t_start)*1000.0))
except:
import traceback
traceback.print_exc()
@@ -995,7 +999,7 @@ class TestPanel( wx.Panel ):
class MemoDialog(wx.Dialog):
"""\
"""
Dialog for multi-line text editing.
"""
def __init__(self,parent=None,title="",text="",pos=None,size=(500,500)):

View File

@@ -2557,6 +2557,7 @@
"PGArrayEditorDialog":"wx.propgrid.",
"PGArrayStringEditorDialog":"wx.propgrid.",
"PGAttributeStorage":"wx.propgrid.",
"PGCachedString":"wx.propgrid.",
"PGCell":"wx.propgrid.",
"PGCellData":"wx.propgrid.",
"PGCellRenderer":"wx.propgrid.",
@@ -2589,6 +2590,7 @@
"PGPropArgCls":"wx.propgrid.",
"PGProperty":"wx.propgrid.",
"PGPropertyFlags":"wx.propgrid.",
"PGSortCallback":"wx.propgrid.",
"PGSpinCtrlEditor":"wx.propgrid.",
"PGTextCtrlAndButtonEditor":"wx.propgrid.",
"PGTextCtrlEditor":"wx.propgrid.",
@@ -2607,15 +2609,25 @@
"PG_ACTION_PREV_PROPERTY":"wx.propgrid.",
"PG_ALPHABETIC_MODE":"wx.propgrid.",
"PG_AUTO_SORT":"wx.propgrid.",
"PG_BASE_DEC":"wx.propgrid.",
"PG_BASE_HEX":"wx.propgrid.",
"PG_BASE_HEXL":"wx.propgrid.",
"PG_BASE_OCT":"wx.propgrid.",
"PG_BOLD_MODIFIED":"wx.propgrid.",
"PG_CAPRECTXMARGIN":"wx.propgrid.",
"PG_CAPRECTYMARGIN":"wx.propgrid.",
"PG_COLOUR":"wx.propgrid.",
"PG_COLOUR_BLACK":"wx.propgrid.",
"PG_COLOUR_CUSTOM":"wx.propgrid.",
"PG_COLOUR_UNSPECIFIED":"wx.propgrid.",
"PG_COLOUR_WEB_BASE":"wx.propgrid.",
"PG_COMPOSITE_FRAGMENT":"wx.propgrid.",
"PG_CUSTOM_IMAGE_SPACINGY":"wx.propgrid.",
"PG_DEFAULT_IMAGE_SIZE":"wx.propgrid.",
"PG_DEFAULT_STYLE":"wx.propgrid.",
"PG_DESCRIPTION":"wx.propgrid.",
"PG_DONT_RECURSE":"wx.propgrid.",
"PG_EDITABLE_VALUE":"wx.propgrid.",
"PG_EX_AUTO_UNSPECIFIED_VALUES":"wx.propgrid.",
"PG_EX_ENABLE_TLP_TRACKING":"wx.propgrid.",
"PG_EX_HELP_AS_TOOLTIPS":"wx.propgrid.",
@@ -2629,8 +2641,13 @@
"PG_EX_TOOLBAR_SEPARATOR":"wx.propgrid.",
"PG_EX_WINDOW_STYLES":"wx.propgrid.",
"PG_EX_WRITEONLY_BUILTIN_ATTRIBUTES":"wx.propgrid.",
"PG_FORCE":"wx.propgrid.",
"PG_FULL_VALUE":"wx.propgrid.",
"PG_GETPROPERTYVALUES_FLAGS":"wx.propgrid.",
"PG_HIDE_CATEGORIES":"wx.propgrid.",
"PG_HIDE_MARGIN":"wx.propgrid.",
"PG_INC_ATTRIBUTES":"wx.propgrid.",
"PG_INVALID_VALUE":"wx.propgrid.",
"PG_ITERATE_ALL":"wx.propgrid.",
"PG_ITERATE_ALL_PARENTS":"wx.propgrid.",
"PG_ITERATE_ALL_PARENTS_RECURSIVELY":"wx.propgrid.",
@@ -2645,9 +2662,19 @@
"PG_ITERATOR_FLAGS_ALL":"wx.propgrid.",
"PG_ITERATOR_MASK_OP_ITEM":"wx.propgrid.",
"PG_ITERATOR_MASK_OP_PARENT":"wx.propgrid.",
"PG_KEEP_STRUCTURE":"wx.propgrid.",
"PG_KEYBOARD_ACTIONS":"wx.propgrid.",
"PG_LABEL":"wx.propgrid.",
"PG_LABEL_STRING":"wx.propgrid.",
"PG_LIMITED_EDITING":"wx.propgrid.",
"PG_MISC_ARG_FLAGS":"wx.propgrid.",
"PG_NO_INTERNAL_BORDER":"wx.propgrid.",
"PG_NULL_BITMAP":"wx.propgrid.",
"PG_PREFIX_0x":"wx.propgrid.",
"PG_PREFIX_DOLLAR_SIGN":"wx.propgrid.",
"PG_PREFIX_NONE":"wx.propgrid.",
"PG_PROGRAMMATIC_VALUE":"wx.propgrid.",
"PG_PROPERTY_SPECIFIC":"wx.propgrid.",
"PG_PROPERTY_VALIDATION_ERROR_MESSAGE":"wx.propgrid.",
"PG_PROPERTY_VALIDATION_SATURATE":"wx.propgrid.",
"PG_PROPERTY_VALIDATION_WRAP":"wx.propgrid.",
@@ -2680,12 +2707,23 @@
"PG_PROP_USE_CHECKBOX":"wx.propgrid.",
"PG_PROP_USE_DCC":"wx.propgrid.",
"PG_PROP_WAS_MODIFIED":"wx.propgrid.",
"PG_RECURSE":"wx.propgrid.",
"PG_RECURSE_STARTS":"wx.propgrid.",
"PG_REPORT_ERROR":"wx.propgrid.",
"PG_SETVALUE_FLAGS":"wx.propgrid.",
"PG_SETVAL_AGGREGATED":"wx.propgrid.",
"PG_SETVAL_BY_USER":"wx.propgrid.",
"PG_SETVAL_FROM_PARENT":"wx.propgrid.",
"PG_SETVAL_REFRESH_EDITOR":"wx.propgrid.",
"PG_SORT_TOP_LEVEL_ONLY":"wx.propgrid.",
"PG_SPLITTER_AUTO_CENTER":"wx.propgrid.",
"PG_STATIC_LAYOUT":"wx.propgrid.",
"PG_STATIC_SPLITTER":"wx.propgrid.",
"PG_TOOLBAR":"wx.propgrid.",
"PG_TOOLTIPS":"wx.propgrid.",
"PG_UNEDITABLE_COMPOSITE_FRAGMENT":"wx.propgrid.",
"PG_VALIDATION_FAILURE_BEHAVIOR_FLAGS":"wx.propgrid.",
"PG_VALUE_IS_CURRENT":"wx.propgrid.",
"PG_VFB_BEEP":"wx.propgrid.",
"PG_VFB_DEFAULT":"wx.propgrid.",
"PG_VFB_MARK_CELL":"wx.propgrid.",
@@ -2819,12 +2857,14 @@
"PropertyCategory":"wx.propgrid.",
"PropertyGrid":"wx.propgrid.",
"PropertyGridConstIterator":"wx.propgrid.",
"PropertyGridEvent":"wx.propgrid.",
"PropertyGridHitTestResult":"wx.propgrid.",
"PropertyGridInterface":"wx.propgrid.",
"PropertyGridIterator":"wx.propgrid.",
"PropertyGridIteratorBase":"wx.propgrid.",
"PropertyGridManager":"wx.propgrid.",
"PropertyGridManagerNameStr":"wx.propgrid.",
"PropertyGridNameStr":"wx.propgrid.",
"PropertyGridPage":"wx.propgrid.",
"PropertyGridPageState":"wx.propgrid.",
"PropertySheetDialog":"wx.adv.",
@@ -3137,6 +3177,7 @@
"Region":"wx.",
"RegionContain":"wx.",
"RegionIterator":"wx.",
"RegisterEditor":"wx.propgrid.",
"RegisterId":"wx.",
"Relationship":"wx.",
"RendererNative":"wx.",
@@ -6478,6 +6519,20 @@
"wxEVT_NULL":"wx.",
"wxEVT_PAINT":"wx.",
"wxEVT_PALETTE_CHANGED":"wx.",
"wxEVT_PG_CHANGED":"wx.propgrid.",
"wxEVT_PG_CHANGING":"wx.propgrid.",
"wxEVT_PG_COL_BEGIN_DRAG":"wx.propgrid.",
"wxEVT_PG_COL_DRAGGING":"wx.propgrid.",
"wxEVT_PG_COL_END_DRAG":"wx.propgrid.",
"wxEVT_PG_DOUBLE_CLICK":"wx.propgrid.",
"wxEVT_PG_HIGHLIGHTED":"wx.propgrid.",
"wxEVT_PG_ITEM_COLLAPSED":"wx.propgrid.",
"wxEVT_PG_ITEM_EXPANDED":"wx.propgrid.",
"wxEVT_PG_LABEL_EDIT_BEGIN":"wx.propgrid.",
"wxEVT_PG_LABEL_EDIT_ENDING":"wx.propgrid.",
"wxEVT_PG_PAGE_CHANGED":"wx.propgrid.",
"wxEVT_PG_RIGHT_CLICK":"wx.propgrid.",
"wxEVT_PG_SELECTED":"wx.propgrid.",
"wxEVT_POWER_RESUME":"wx.",
"wxEVT_POWER_SUSPENDED":"wx.",
"wxEVT_POWER_SUSPENDING":"wx.",

View File

@@ -23,7 +23,9 @@ ITEMS = [ ]
# The list of other ETG scripts and back-end generator modules that are
# included as part of this module. These should all be items that are put in
# the wxWidgets "propgrid" library in a multi-lib build.
INCLUDES = [ 'propgridproperty',
INCLUDES = [ 'pgvariant',
'propgriddefs',
'propgridproperty',
'propgrideditors',
'propgridpagestate',
'propgridiface',
@@ -62,6 +64,37 @@ def run():
module.addInclude(INCLUDES)
# Deprecated aliases for the various helper classes in Classic
module.addPyCode("""\
PyArrayStringProperty = wx.deprecated(ArrayStringProperty, "Use ArrayStringProperty instead.")
PyChoiceEditor = wx.deprecated(PGChoiceEditor, "Use PGChoiceEditor instead.")
PyColourProperty = wx.deprecated(ColourProperty, "Use ColourProperty instead.")
PyComboBoxEditor = wx.deprecated(PGComboBoxEditor, "Use PGComboBoxEditor instead.")
PyEditEnumProperty = wx.deprecated(EditEnumProperty, "Use PGEditEnumProperty instead.")
PyEditor = wx.deprecated(PGEditor, "Use PGEditor instead.")
PyEditorDialogAdapter = wx.deprecated(PGEditorDialogAdapter, "Use PGEditorDialogAdapter instead.")
PyEnumProperty = wx.deprecated(EnumProperty, "Use EnumProperty instead.")
PyFileDialogAdapter = wx.deprecated(PGFileDialogAdapter, "Use PGFileDialogAdapter instead.")
PyFileProperty = wx.deprecated(FileProperty, "Use FileProperty instead.")
PyFlagsProperty = wx.deprecated(FlagsProperty, "Use FlagsProperty instead.")
PyFloatProperty = wx.deprecated(FloatProperty, "Use FloatProperty instead.")
PyFontProperty = wx.deprecated(FontProperty, "Use FontProperty instead.")
PyIntProperty = wx.deprecated(IntProperty, "Use IntProperty instead.")
PyLongStringDialogAdapter = wx.deprecated(PGLongStringDialogAdapter, "Use PGLongStringDialogAdapter instead.")
PyLongStringProperty = wx.deprecated(LongStringProperty, "Use LongStringProperty instead.")
PyProperty = wx.deprecated(PGProperty, "Use PGProperty instead.")
PyStringProperty = wx.deprecated(StringProperty, "Use StringProperty instead.")
PySystemColourProperty = wx.deprecated(SystemColourProperty, "Use SystemColourProperty instead.")
PyTextCtrlEditor = wx.deprecated(PGTextCtrlEditor, "Use PGTextCtrlEditor instead.")
PyUIntProperty = wx.deprecated(UIntProperty, "Use UIntProperty instead.")
""")
module.addPyFunction('RegisterEditor', '(editor, editorName)',
deprecated='Use PropertyGrid.DoRegisterEditor instead',
body='return PropertyGrid.DoRegisterEditorClass(editor, editorName)',
)
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -20,6 +20,7 @@ DOCSTRING = ""
ITEMS = [ 'interface_2wx_2propgrid_2propgrid_8h.xml',
'wxPGValidationInfo',
'wxPropertyGrid',
'wxPropertyGridEvent',
]
#---------------------------------------------------------------------------
@@ -33,7 +34,6 @@ def run():
# Tweak the parsed meta objects in the module object as needed for
# customizing the generated code and docstrings.
c = module.find('wxPGValidationInfo')
assert isinstance(c, etgtools.ClassDef)
@@ -42,12 +42,31 @@ def run():
assert isinstance(c, etgtools.ClassDef)
c.bases.remove('wxScrollHelper')
tools.fixWindowClass(c)
module.addGlobalStr('wxPropertyGridNameStr', c)
for m in c.find('RegisterEditorClass').all():
m.find('editor').transfer = True
# TODO: provide a way to use a Python callable as a sort function
c.find('GetSortFunction').ignore()
c.find('SetSortFunction').ignore()
module.find('wxPGSortCallback').ignore()
# Add some extra Python code to be executed when a wxPropertyGrid is
# constructed. In Classic with SWIG we did this with %pythonAppend, is
# there any better way to do it with sip than monkey-patching?
c.addPyCode("""\
_PropertyGrid__init__orig = PropertyGrid.__init__
def _PropertyGrid__init__(self, *args, **kw):
_PropertyGrid__init__orig(self, *args, **kw)
self.DoDefaultTypeMappings()
self.edited_objects = {}
self.DoDefaultValueTypeMappings()
if not hasattr(self.__class__, '_vt2setter'):
self.__class__._vt2setter = {}
PropertyGrid.__init__ = _PropertyGrid__init__
""")
# See note in propgridiface.py
for item in module.allItems():
@@ -61,6 +80,34 @@ def run():
td.noTypeName = True
c = module.find('wxPropertyGridEvent')
tools.fixEventClass(c)
c.addPyCode("""\
EVT_PG_CHANGED = wx.PyEventBinder( wxEVT_PG_CHANGED, 1 )
EVT_PG_CHANGING = wx.PyEventBinder( wxEVT_PG_CHANGING, 1 )
EVT_PG_SELECTED = wx.PyEventBinder( wxEVT_PG_SELECTED, 1 )
EVT_PG_HIGHLIGHTED = wx.PyEventBinder( wxEVT_PG_HIGHLIGHTED, 1 )
EVT_PG_RIGHT_CLICK = wx.PyEventBinder( wxEVT_PG_RIGHT_CLICK, 1 )
EVT_PG_PAGE_CHANGED = wx.PyEventBinder( wxEVT_PG_PAGE_CHANGED, 1 )
EVT_PG_ITEM_COLLAPSED = wx.PyEventBinder( wxEVT_PG_ITEM_COLLAPSED, 1 )
EVT_PG_ITEM_EXPANDED = wx.PyEventBinder( wxEVT_PG_ITEM_EXPANDED, 1 )
EVT_PG_DOUBLE_CLICK = wx.PyEventBinder( wxEVT_PG_DOUBLE_CLICK, 1 )
EVT_PG_LABEL_EDIT_BEGIN = wx.PyEventBinder( wxEVT_PG_LABEL_EDIT_BEGIN, 1 )
EVT_PG_LABEL_EDIT_ENDING = wx.PyEventBinder( wxEVT_PG_LABEL_EDIT_ENDING, 1 )
EVT_PG_COL_BEGIN_DRAG = wx.PyEventBinder( wxEVT_PG_COL_BEGIN_DRAG, 1 )
EVT_PG_COL_DRAGGING = wx.PyEventBinder( wxEVT_PG_COL_DRAGGING, 1 )
EVT_PG_COL_END_DRAG = wx.PyEventBinder( wxEVT_PG_COL_END_DRAG, 1 )
""")
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -40,9 +40,24 @@ def run():
# Tweak the parsed meta objects in the module object as needed for
# customizing the generated code and docstrings.
#c = module.find('')
#assert isinstance(c, etgtools.ClassDef)
c = module.find('wxMultiChoiceProperty')
assert isinstance(c, etgtools.ClassDef)
# Fix up the ctor taking a wxArrayString to be the one with the easier and
# expected API
m = c.find('wxMultiChoiceProperty').findOverload('strings')
m.find('name').default = 'wxPG_LABEL'
m.find('strings').default = 'wxArrayString()'
m.find('strings').name = 'choices'
m.find('value').default = 'wxArrayString()'
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)

60
etg/propgriddefs.py Normal file
View File

@@ -0,0 +1,60 @@
#---------------------------------------------------------------------------
# Name: etg/propgriddefs.py
# Author: Robin Dunn
#
# Created: 14-Feb-2017
# Copyright: (c) 2017 by Total Control Software
# License: wxWindows License
#---------------------------------------------------------------------------
import etgtools
import etgtools.tweaker_tools as tools
PACKAGE = "wx"
MODULE = "_propgrid"
NAME = "propgriddefs" # Base name of the file to generate to for this script
DOCSTRING = ""
# The classes and/or the basename of the Doxygen XML files to be processed by
# this script.
ITEMS = [ 'propgriddefs_8h.xml',
]
#---------------------------------------------------------------------------
def run():
# Parse the XML file(s) building a collection of Extractor objects
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
etgtools.parseDoxyXML(module, ITEMS)
#-----------------------------------------------------------------
# Tweak the parsed meta objects in the module object as needed for
# customizing the generated code and docstrings.
module.find('wxPG_LABEL').ignore()
module.find('wxPG_LABEL_STRING').ignore()
module.find('wxPG_NULL_BITMAP').ignore()
module.find('wxPG_COLOUR_BLACK').ignore()
module.find('wxPG_COLOUR').ignore()
module.find('wxPG_DEFAULT_IMAGE_SIZE').ignore()
module.find('wxPGSortCallback').ignore()
module.addPyCode(
code="""\
PG_LABEL = "@!"
PG_LABEL_STRING = PG_LABEL
PG_NULL_BITMAP = wx.NullBitmap
PG_COLOUR_BLACK = wx.BLACK
PG_DEFAULT_IMAGE_SIZE = wx.Size(-1, -1)
""",
order=15)
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)
#---------------------------------------------------------------------------
if __name__ == '__main__':
run()

View File

@@ -45,6 +45,13 @@ def run():
tools.fixWindowClass(c)
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -34,8 +34,75 @@ def run():
c = module.find('wxPGPropArgCls')
assert isinstance(c, etgtools.ClassDef)
c.find('wxPGPropArgCls').findOverload('wxString &').ignore()
c.find('wxPGPropArgCls').findOverload('char *').ignore()
c.find('wxPGPropArgCls').findOverload('wchar_t *').ignore()
c.find('wxPGPropArgCls').findOverload('int').ignore()
c.find('wxPGPropArgCls').findOverload('deallocPtr').ignore()
# Make a string ctor that uses the wxPython-specific version of
# the C++ class' ctor
newCtor = c.addCppCtor('(const wxString& str)',
doc="Creates a PGPropArgCls from a string.",
body="""\
wxString* name = new wxString(*str);
return new wxPGPropArgCls(name, true);
"""
)
# Make it be the first overload instead of the last
ctor = c.find('wxPGPropArgCls')
overloads = list(ctor.overloads)
del overloads[overloads.index(newCtor)]
overloads.insert(0, newCtor)
ctor.overloads = overloads
c.find('GetPtr').overloads[0].ignore()
c.convertFromPyObject = """\
// Code to test a PyObject for compatibility with wxPGPropArgCls
if (!sipIsErr) {
if (sipCanConvertToType(sipPy, sipType_wxPGPropArgCls, SIP_NO_CONVERTORS))
return TRUE;
if (PyBytes_Check(sipPy) || PyUnicode_Check(sipPy))
return TRUE;
if (sipPy == Py_None)
return TRUE;
if (sipCanConvertToType(sipPy, sipType_wxPGProperty, SIP_NO_CONVERTORS))
return TRUE;
return FALSE;
}
// Code to convert a compatible PyObject to a wxPGPropArgCls
if (PyBytes_Check(sipPy) || PyUnicode_Check(sipPy)) {
wxString* name = new wxString(Py2wxString(sipPy));
*sipCppPtr = new wxPGPropArgCls(name, true);
return sipGetState(sipTransferObj);
}
else if (sipCanConvertToType(sipPy, sipType_wxPGProperty, SIP_NO_CONVERTORS)) {
int state = 0;
wxPGProperty* prop = reinterpret_cast<wxPGProperty*>(
sipConvertToType(sipPy, sipType_wxPGProperty, sipTransferObj, SIP_NO_CONVERTORS, &state, sipIsErr));
*sipCppPtr = new wxPGPropArgCls(prop);
sipReleaseType(prop, sipType_wxPGProperty, state);
return sipGetState(sipTransferObj);
}
else if (sipPy == Py_None) {
*sipCppPtr = new wxPGPropArgCls(reinterpret_cast< wxPGProperty * >(NULL));
return sipGetState(sipTransferObj);
}
else {
// It's already a wxPGPropArgCls, just fetch the pointer and return
*sipCppPtr = reinterpret_cast<wxPGPropArgCls*>(sipConvertToType(
sipPy, sipType_wxPGPropArgCls, sipTransferObj,
SIP_NO_CONVERTORS, 0, sipIsErr));
return 0; // not a new instance
}
"""
#----------------------------------------------------------
c = module.find('wxPropertyGridInterface')
c.abstract = True
@@ -49,13 +116,371 @@ def run():
c.find('SetPropertyValue').findOverload('wxULongLong_t value').ignore()
c.find('SetPropertyValue').findOverload('wxObject *value').ignore()
c.find('Append.property').transfer = True
c.find('AppendIn.newProperty').transfer = True
for m in c.find('Insert').all():
m.find('newProperty').transfer = True
# Tons of Python method implementations ported from Classic...
module.addPyCode("""\
_type2property = None
_vt2getter = None
""")
c.addPyMethod('MapType', '(self, class_, factory)',
doc="""\
Registers Python type/class to property mapping.
:param `factory`: Property builder function/class.
""",
body="""\
global _type2property
if _type2property is None:
raise AssertionError("call only after a propertygrid or "
"manager instance constructed")
_type2property[class_] = factory
""")
c.addPyMethod('DoDefaultTypeMappings', '(self)',
doc="Add built-in properties to the map.",
body="""\
import sys
global _type2property
if _type2property is not None:
return
_type2property = dict()
_type2property[str] = StringProperty
if sys.version_info.major < 2:
_type2property[unicode] = StringProperty
_type2property[int] = IntProperty
_type2property[float] = FloatProperty
_type2property[bool] = BoolProperty
_type2property[list] = ArrayStringProperty
_type2property[tuple] = ArrayStringProperty
_type2property[wx.Font] = FontProperty
_type2property[wx.Colour] = ColourProperty
#_type2property[wx.Size] = SizeProperty
#_type2property[wx.Point] = PointProperty
#_type2property[wx.FontData] = FontDataProperty
""")
# TODO: is this still needed?
c.addPyMethod('DoDefaultValueTypeMappings', '(self)',
doc="Map pg value type ids to getter methods.",
body="""\
global _vt2getter
if _vt2getter is not None:
return
_vt2getter = dict()
""")
c.find('GetPropertyValues').ignore()
c.addPyMethod('GetPropertyValues',
'(self, dict_=None, as_strings=False, inc_attributes=False)',
doc="""\
Returns all property values in the grid.\n
:param `dict_`: A to fill with the property values. If not given,
then a new one is created. The dict_ can be an object as well,
in which case it's __dict__ is used.
:param `as_strings`: if True, then string representations of values
are fetched instead of native types. Useful for config and such.
:param `inc_attributes`: if True, then property attributes are added
in the form of "@<propname>@<attr>".
:returns: A dictionary with values. It is always a dictionary,
so if dict_ was and object with __dict__ attribute, then that
attribute is returned.
""",
body="""\
if dict_ is None:
dict_ = {}
elif hasattr(dict_,'__dict__'):
dict_ = dict_.__dict__
getter = self.GetPropertyValue if not as_strings else self.GetPropertyValueAsString
it = self.GetVIterator(PG_ITERATE_PROPERTIES)
while not it.AtEnd():
p = it.GetProperty()
name = p.GetName()
dict_[name] = getter(p)
if inc_attributes:
attrs = p.GetAttributes()
if attrs and len(attrs):
dict_['@%s@attr'%name] = attrs
it.Next()
return dict_
""")
for m in c.find('SetPropertyValues').all():
m.ignore()
c.addPyMethod('SetPropertyValues', '(self, dict_, autofill=False)',
doc="""\
Sets property values from a dictionary.\n
:param `dict_`: the source of the property values to set, which can be
either a dictionary or an object with a __dict__ attribute.
:param `autofill`: If true, keys with not relevant properties are
auto-created. For more info, see :method:`AutoFill`.
:note:
* Keys starting with underscore are ignored.
* Attributes can be set with entries named like "@<propname>@<attr>".
""",
body="""\
if dict_ is None:
dict_ = {}
elif hasattr(dict_,'__dict__'):
dict_ = dict_.__dict__
attr_dicts = []
def set_sub_obj(k0, dict_):
for k,v in dict_.items():
if k[0] != '_':
if k.endswith('@attr'):
attr_dicts.append((k[1:-5],v))
else:
try:
self.SetPropertyValue(k,v)
except:
try:
if autofill:
self._AutoFillOne(k0,k,v)
continue
except:
if isinstance(v,dict):
set_sub_obj(k,v)
elif hasattr(v,'__dict__'):
set_sub_obj(k,v.__dict__)
for k,v in attr_dicts:
p = self.GetPropertyByName(k)
if not p:
raise AssertionError("No such property: '%s'"%k)
for an,av in v.items():
p.SetAttribute(an, av)
cur_page = False
is_manager = isinstance(self, PropertyGridManager)
try:
set_sub_obj(self.GetGrid().GetRoot(), dict_)
except:
import traceback
traceback.print_exc()
self.Refresh()
""")
# TODO: should these be marked as deprecated?
module.addPyCode("""\
PropertyGridInterface.GetValues = PropertyGridInterface.GetPropertyValues
PropertyGridInterface.SetValues = PropertyGridInterface.SetPropertyValues
""")
c.addPyMethod('_AutoFillMany', '(self,cat,dict_)',
body="""\
for k,v in dict_.items():
self._AutoFillOne(cat,k,v)
""")
c.addPyMethod('_AutoFillOne', '(self,cat,k,v)',
body="""\
global _type2property
factory = _type2property.get(v.__class__,None)
if factory:
self.AppendIn(cat, factory(k,k,v))
elif hasattr(v,'__dict__'):
cat2 = self.AppendIn(cat, PropertyCategory(k))
self._AutoFillMany(cat2, v.__dict__)
elif isinstance(v, dict):
cat2 = self.AppendIn(cat, PropertyCategory(k))
self._AutoFillMany(cat2, v)
elif not k.startswith('_'):
raise AssertionError("member '%s' is of unregistered type/"
"class '%s'"%(k,v.__class__))
""")
c.addPyMethod('AutoFill', '(self, obj, parent=None)',
doc="""\
"Clears properties and re-fills to match members and values of
the given object or dictionary obj.
""",
body="""\
self.edited_objects[parent] = obj
cur_page = False
is_manager = isinstance(self, PropertyGridManager)
if not parent:
if is_manager:
page = self.GetCurrentPage()
page.Clear()
parent = page.GetRoot()
else:
self.Clear()
parent = self.GetGrid().GetRoot()
else:
it = self.GetIterator(PG_ITERATE_PROPERTIES, parent)
it.Next() # Skip the parent
while not it.AtEnd():
p = it.GetProperty()
if not p.IsSomeParent(parent):
break
self.DeleteProperty(p)
name = p.GetName()
it.Next()
if not is_manager or page == self.GetCurrentPage():
self.Freeze()
cur_page = True
try:
self._AutoFillMany(parent,obj.__dict__)
except:
import traceback
traceback.print_exc()
if cur_page:
self.Thaw()
""")
c.addPyMethod('RegisterEditor', '(self, editor, editorName=None)',
doc="Register a new editor, either an instance or a class.",
body="""\
if not isinstance(editor, PGEditor):
editor = editor()
if not editorName:
editorName = editor.__class__.__name__
try:
self._editor_instances.append(editor)
except:
self._editor_instances = [editor]
return PropertyGrid.DoRegisterEditorClass(editor, editorName)
"""
)
c.find('GetPropertyClientData').ignore()
c.addPyMethod('GetPropertyClientData', '(self, p)',
body="""\
if isinstance(p, str):
p = self.GetPropertyByName(p)
return p.GetClientData()
""")
c.find('SetPropertyClientData').ignore()
c.addPyMethod('SetPropertyClientData', '(self, p, data)',
body="""\
if isinstance(p, str):
p = self.GetPropertyByName(p)
return p.SetClientData(data)
""")
c.addPyMethod('GetPyIterator', '(self, flags=PG_ITERATE_DEFAULT, firstProperty=None)',
doc="""\
Returns a pythonic property iterator for a single :ref:`PropertyGrid`
or page in :ref:`PropertyGridManager`. Arguments are same as for
:ref:`GetIterator`.
The following example demonstrates iterating absolutely all items in
a single grid::
iterator = propGrid.GetPyIterator(wx.propgrid.PG_ITERATE_ALL)
for prop in iterator:
print(prop)
:see: `wx.propgrid.PropertyGridInterface.Properties`
`wx.propgrid.PropertyGridInterface.Items`
""",
body="""\
it = self.GetIterator(flags, firstProperty)
while not it.AtEnd():
yield it.GetProperty()
it.Next()
""")
c.addPyMethod('GetPyVIterator', '(self, flags=PG_ITERATE_DEFAULT)',
doc="""\
Similar to :ref:`GetVIterator` but returns a pythonic iterator.
""",
body="""\
it = self.GetVIterator(flags)
while not it.AtEnd():
yield it.GetProperty()
it.Next()
""")
c.addPyMethod('_Properties', '(self)',
doc="""\
This attribute is a pythonic iterator over all properties in
this `PropertyGrid` property container. It will only skip
categories and private child properties. Usage is simple::
for prop in propGrid.Properties:
print(prop)
:see: `wx.propgrid.PropertyGridInterface.Items`
`wx.propgrid.PropertyGridInterface.GetPyIterator`
""",
body="""\
it = self.GetIterator(PG_ITERATE_NORMAL)
while not it.AtEnd():
yield it.GetProperty()
it.Next()
""")
c.addPyProperty('Properties', '_Properties')
c.addPyMethod('_Items', '(self)',
doc="""\
This attribute is a pythonic iterator over all items in this
`PropertyGrid` property container, excluding only private child
properties. Usage is simple::
for prop in propGrid.Items:
print(prop)
:see: `wx.propgrid.PropertyGridInterface.Properties`
`wx.propgrid.PropertyGridInterface.GetPyVIterator`
""",
body="""\
it = self.GetVIterator(PG_ITERATE_NORMAL | PG_ITERATE_CATEGORIES)
while not it.AtEnd():
yield it.GetProperty()
it.Next()
""")
c.addPyProperty('Items', '_Items')
#----------------------------------------------------------
module.addItem(
tools.wxArrayPtrWrapperTemplate('wxArrayPGProperty', 'wxPGProperty', module))
# wxPGPropArg is a typedef for "const wxPGPropArgCls&" so having the
# wrappers treat it as a normal type can be problematic. ("new cannot be
# applied to a reference type", etc.) Let's just ignore it an replace it
# applied to a reference type", etc.) Let's just ignore it and replace it
# everywhere for the real type.
module.find('wxPGPropArg').ignore()
for item in module.allItems():
@@ -63,6 +488,14 @@ def run():
item.type = 'const wxPGPropArgCls &'
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -45,6 +45,23 @@ def run():
tools.ignoreConstOverloads(c)
# Add some extra Python code to be executed when a wxPropertyGridManager
# is constructed. In Classic with SWIG we did this with %pythonAppend, is
# there any better way to do it with sip than monkey-patching?
c.addPyCode("""\
_PropertyGridManager__init__orig = PropertyGridManager.__init__
def _PropertyGridManager__init__(self, *args, **kw):
_PropertyGridManager__init__orig(self, *args, **kw)
self.DoDefaultTypeMappings()
self.edited_objects = {}
self.DoDefaultValueTypeMappings()
if not hasattr(self.__class__, '_vt2setter'):
self.__class__._vt2setter = {}
PropertyGridManager.__init__ = _PropertyGridManager__init__
""")
# wxPGPropArg is a typedef for "const wxPGPropArgCls&" so having the
# wrappers treat it as a normal type can be problematic. ("new cannot be
# applied to a reference type", etc.) Let's just ignore it an replace it

View File

@@ -20,7 +20,6 @@ DOCSTRING = ""
ITEMS = [ 'wxPropertyGridHitTestResult',
'wxPropertyGridIteratorBase',
'wxPropertyGridIterator',
#'wxPropertyGridConstIterator', # probably not needed in Python...
'wxPGVIterator',
'wxPropertyGridPageState',
]
@@ -52,6 +51,13 @@ def run():
module.find('wxPG_IT_CHILDREN').ignore()
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -22,7 +22,6 @@ ITEMS = [ 'wxPGPaintData',
'wxPGDefaultRenderer',
'wxPGCellData',
'wxPGCell',
'wxPGAttributeStorage',
'wxPGProperty',
'wxPropertyCategory',
@@ -45,13 +44,10 @@ def run():
c = module.find('wxPGCellData')
assert isinstance(c, etgtools.ClassDef)
c.find('~wxPGCellData').ignore(False)
c.bases = ['wxRefCounter']
c = module.find('wxPGAttributeStorage')
# TODO: Add methods to add a Python iterator using these methods
c.find('StartIteration').ignore()
c.find('GetNext').ignore()
c.find('const_iterator').ignore()
c = module.find('wxPGCellRenderer')
c.bases = ['wxRefCounter']
c = module.find('wxPGProperty')
@@ -59,18 +55,62 @@ def run():
c.find('StringToValue.variant').out = True
c.find('IntToValue.variant').out = True
# TODO: Some other wxPGProperty methods should be pythonized a bit...
# SIP needs to be able to make a copy of the wxPGAttributeStorage value
# but the C++ class doesn't have a copy ctor and the default will cause it
# to lose references to the variants it contains, so let's just override
# the use of the MappedType and convert it to a Python dictionary here
# instead.
m = c.find('GetAttributes')
m.type = 'PyObject*'
m.setCppCode("""\
const wxPGAttributeStorage& attrs = self->GetAttributes();
wxPGAttributeStorage::const_iterator it = attrs.StartIteration();
wxVariant v;
wxPyThreadBlocker blocker;
PyObject* dict = PyDict_New();
if ( !dict ) return NULL;
while ( attrs.GetNext( it, v ) ) {
const wxString& name = v.GetName();
PyObject* pyStr = wx2PyString(name);
PyObject* pyVal = wxPGVariant_out_helper(v);
int res = PyDict_SetItem( dict, pyStr, pyVal );
}
return dict;
""")
# SetAttributes uses wxPGAttributeStorage too, but we'll just replace it
# with a simple Python method.
c.find('SetAttributes').ignore()
c.addPyMethod('SetAttributes', '(self, attributes)',
doc="Set the property's attributes from a Python dictionary.",
body="""\
for name,value in attributes.items():
self.SetAttribute(name, value)
""")
c = module.find('wxPGChoicesData')
tools.ignoreConstOverloads(c)
#c.addDtor()
c.bases = ['wxRefCounter']
c.find('~wxPGChoicesData').ignore(False)
c = module.find('wxPGChoices')
c.find('wxPGChoices').findOverload('wxChar **').ignore()
tools.ignoreConstOverloads(c)
c.find('operator[]').ignore()
c.addPyMethod('__getitem__', '(self, index)',
doc="Returns a reference to a :class:PGChoiceEntry using Python list syntax.",
body="return self.Item(index)",
)
c.addPyMethod('__len__', '(self)',
doc="",
body="return self.GetCount()",
)
# Ignore some string constants (#defines) coming from dox, and add them
# back in Python code. They are wchar_t* values and this seemed the
@@ -147,6 +187,13 @@ def run():
""")
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -91,6 +91,13 @@ def run():
tools.fixWindowClass(c, hideVirtuals=False, ignoreProtected=False)
# Switch all wxVariant types to wxPGVariant, so the propgrid-specific
# version of the MappedType will be used for converting to/from Python
# objects.
for item in module.allItems():
if hasattr(item, 'type') and 'wxVariant' in item.type:
item.type = item.type.replace('wxVariant', 'wxPGVariant')
#-----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)

View File

@@ -301,8 +301,8 @@ class FunctionDef(BaseDef, FixWxPrefix):
info we've already received from the source XML such as the argument
types and names, docstring, etc.
The code generated for this verison will expect the given code to use
SIP specfic variable names, etc. For example::
The code generated for this version will expect the given code to use
SIP specific variable names, etc. For example::
sipRes = sipCpp->Foo();
"""
@@ -918,7 +918,7 @@ class ClassDef(BaseDef):
monkey-patched into the class. (This property will also be
jammed in to the class in like manner.)
"""
# Read the nice comment in the function above. Ditto.
# Read the nice comment in the method above. Ditto.
if len(args) == 1:
name = getter = setter = ''
split = args[0].split()
@@ -1525,11 +1525,11 @@ class ModuleDef(BaseDef):
return md
def addPyCode(self, code, order=None):
def addPyCode(self, code, order=None, **kw):
"""
Add a snippet of Python code to the wrapper module.
"""
pc = PyCodeDef(code, order)
pc = PyCodeDef(code, order, **kw)
self.items.append(pc)
return pc

209
src/pgvariant.sip Normal file
View File

@@ -0,0 +1,209 @@
//--------------------------------------------------------------------------
// Name: pgvariant.sip
// Purpose: MappedType for wxPGVariant
//
// Author: Robin Dunn
//
// Created: 24-Feb-2017
// Copyright: (c) 2017 by Total Control Software
// Licence: wxWindows license
//--------------------------------------------------------------------------
%ModuleHeaderCode
// A wxPGVariant is really the same thing as a wxVariant. We just create
// this new type so a different %MappedType can be created for it that
// also supports the variant data types that are in the propgrid APIs
// instead of core.
typedef wxVariant wxPGVariant;
typedef wxVariantList wxPGVariantList;
wxVariant wxPGVariant_in_helper(PyObject* source);
PyObject* wxPGVariant_out_helper(const wxVariant& value);
%End
%ModuleCode
wxVariant wxPGVariant_in_helper(PyObject* obj)
{
wxVariant value;
int state = 0;
int isErr = 0;
// Always check for None first so we don't just get NULL pointers for the
// other types.
if (obj == Py_None)
value.MakeNull();
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxFont"))) {
wxFont* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxFont"));
value << *ptr;
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxPoint"))) {
wxPoint* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxPoint"));
value << *ptr;
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxSize"))) {
wxSize* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxSize"));
value << *ptr;
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxColourPropertyValue"))) {
wxColourPropertyValue* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxColourPropertyValue"));
value << *ptr;
}
else if (sipCanConvertToType(obj, sipType_wxArrayInt, 0)) {
wxArrayInt* ptr;
ptr = (wxArrayInt*)sipConvertToType(obj, sipType_wxArrayInt, NULL, 0, &state, &isErr);
value << *ptr;
sipReleaseType(ptr, sipType_wxArrayInt, state);
}
else
value = wxVariant_in_helper(obj);
return value;
}
PyObject* wxPGVariant_out_helper(const wxVariant& value)
{
PyObject* obj;
if (value.IsNull()) {
obj = Py_None;
Py_INCREF(obj);
}
else if ( value.IsType("wxFont") ) {
wxFont val;
val << value;
obj = wxPyConstructObject(new wxFont(val), "wxFont", true);
}
else if ( value.IsType("wxPoint") ) {
const wxPoint& val = wxPointRefFromVariant(value);
obj = wxPyConstructObject(new wxPoint(val), "wxPoint", true);
}
else if ( value.IsType("wxSize") ) {
const wxSize& val = wxSizeRefFromVariant(value);
obj = wxPyConstructObject(new wxSize(val), "wxSize", true);
}
else if ( value.IsType("wxColourPropertyValue") ) {
wxColourPropertyValue val;
val << value;
obj = wxPyConstructObject(new wxColourPropertyValue(val), "wxColourPropertyValue", true);
}
else if ( value.IsType("wxArrayInt") ) {
const wxArrayInt& arr = wxArrayIntRefFromVariant(value);
obj = sipConvertFromType((void*)&arr, sipType_wxArrayInt, NULL);
}
else
obj = wxVariant_out_helper(value);
return obj;
}
%End
%MappedType wxPGVariant /AllowNone/
{
%ConvertToTypeCode
// Code to test a PyObject for compatibility.
if (!sipIsErr) {
// Any type should work since we'll just use the PyObject directly
// if the type is not one that is explicitly supported.
return TRUE;
}
// Code to create a new wxVariant from the PyObject
wxVariant* value = new wxVariant(wxPGVariant_in_helper(sipPy));
*sipCppPtr = value;
return sipGetState(sipTransferObj);
%End
%ConvertFromTypeCode
// Code to convert a wxVariant to a PyObject.
if (sipCpp == NULL) {
return Py_None;
} else {
return wxPGVariant_out_helper(*sipCpp);
}
%End
};
// Add a typemap for wxVariantList
%MappedType wxPGVariantList
{
%ConvertToTypeCode
// Code to test a PyObject for compatibility.
if (!sipIsErr) {
// Any type sequence type is okay.
int success = PySequence_Check(sipPy);
if (!success)
PyErr_SetString(PyExc_TypeError, "Sequence type expected.");
return success;
}
// Code to create a new wxVariantList from the PyObject sequence
wxVariantList* value = new wxVariantList();
Py_ssize_t len = PySequence_Length(sipPy);
Py_ssize_t idx = 0;
while (idx < len) {
PyObject* item = PySequence_GetItem(sipPy, idx);
value->Append(new wxVariant(wxPGVariant_in_helper(item)));
Py_DECREF(item);
}
*sipCppPtr = value;
return sipGetState(sipTransferObj);
%End
%ConvertFromTypeCode
// Code to convert a wxVariantList to a Python list.
if (sipCpp == NULL) {
return Py_None;
} else {
size_t idx = 0;
PyObject* value = PyList_New(0);
for (idx=0; idx < sipCpp->GetCount(); idx++) {
PyObject* item = wxPGVariant_out_helper(sipCpp->Item(idx));
PyList_Append(value, item);
}
return value;
}
%End
};
// Used just for unit testing the MappedType code, it can be removed later
%ModuleCode
wxPGVariant testPGVariantTypemap(const wxPGVariant& var)
{
wxVariant local = var; // force a copy
return local;
}
wxString testPGVariantTypeName(const wxPGVariant& var)
{
return var.GetType();
}
%End
wxPGVariant testPGVariantTypemap(const wxPGVariant& var);
wxString testPGVariantTypeName(const wxPGVariant& var);

View File

@@ -12,7 +12,7 @@
// We always convert wxVariant to / from the native Python types since its purpose
// is basically to allow a variable to be multiple types in C++
%MappedType wxVariant
%MappedType wxVariant /AllowNone/
{
%ConvertToTypeCode
// Code to test a PyObject for compatibility.
@@ -88,12 +88,18 @@
// Used just for unittesting the MappedType code, it can be removed later
// Used just for unit testing the MappedType code, it can be removed later
%ModuleCode
wxVariant testVariantTypemap(const wxVariant& var)
{
wxVariant local = var; // force a copy
return local;
}
wxString testVariantTypeName(const wxVariant& var)
{
return var.GetType();
}
%End
wxVariant testVariantTypemap(const wxVariant& var);
wxString testVariantTypeName(const wxVariant& var);

View File

@@ -95,6 +95,10 @@ static wxString i_Py2wxString(PyObject* source)
//--------------------------------------------------------------------------
// Wrapped object checks and converters
//
// TODO: Add some versions of these helpers that take a sipTypeDef
// instead of a name? They're accessible everywhere we need them, and
// it may be enough of an efficiency boost to make it worth it.
// Create a PyObject of the requested type from a void* and a class name.
static PyObject* i_wxPyConstructObject(void* ptr,
@@ -121,6 +125,7 @@ static bool i_wxPyWrappedPtr_Check(PyObject* obj)
return PyObject_TypeCheck(obj, sipWrapper_Type);
}
// Check if a PyObject is a specific wrapped class or subclass
static bool i_wxPyWrappedPtr_TypeCheck(PyObject* obj, const wxString& className)
{
@@ -130,6 +135,7 @@ static bool i_wxPyWrappedPtr_TypeCheck(PyObject* obj, const wxString& className)
return sipCanConvertToType(obj, td, SIP_NO_CONVERTORS);
}
// Convert a wrapped SIP object to its C++ pointer, ensuring that it is of the expected type
static bool i_wxPyConvertWrappedPtr(PyObject* obj, void **ptr, const wxString& className)
{
@@ -143,6 +149,7 @@ static bool i_wxPyConvertWrappedPtr(PyObject* obj, void **ptr, const wxString& c
return true;
}
//--------------------------------------------------------------------------
// Deal with the GIL
@@ -269,7 +276,7 @@ bool wxVariantDataPyObject::Eq(wxVariantData& data) const
//
// These functions are here in the API so they can be used by both the
// wxVariant MappedType and by other classes or types that want to add support
// for additional kinds of nativly supported types (see dataview for example.)
// for additional kinds of natively supported types (see dataview for example.)
// PyObject --> wxVariant
wxVariant i_wxVariant_in_helper(PyObject* obj)
@@ -278,30 +285,66 @@ wxVariant i_wxVariant_in_helper(PyObject* obj)
if (PyBytes_Check(obj) || PyUnicode_Check(obj))
value = Py2wxString(obj);
else if (PyBool_Check(obj))
value = (obj == Py_True);
else if (wxPyInt_Check(obj))
value = (long)wxPyInt_AS_LONG(obj);
else if (PyLong_Check(obj))
value = (long)PyLong_AsLong(obj);
else if (PyFloat_Check(obj))
value = PyFloat_AS_DOUBLE(obj);
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxDateTime"))) {
else if (obj == Py_None)
value.MakeNull();
else if (sipCanConvertToType(obj, sipType_wxDateTime, 0)) {
wxDateTime* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxDateTime"));
int state = 0;
int isErr = 0;
ptr = (wxDateTime*)sipConvertToType(obj, sipType_wxDateTime, NULL, 0, &state, &isErr);
value = *ptr;
sipReleaseType(ptr, sipType_wxDateTime, state);
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxBitmap"))) {
wxBitmap* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxBitmap"));
value << *ptr;
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxImage"))) {
wxImage* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxImage"));
value << *ptr;
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxIcon"))) {
wxIcon* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxIcon"));
value <<*ptr;
value << *ptr;
}
else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxColour"))) {
wxColour* ptr;
wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxColour"));
value << *ptr;
}
else if (sipCanConvertToType(obj, sipType_wxArrayString, 0)) {
wxArrayString* ptr;
int state = 0;
int isErr = 0;
ptr = (wxArrayString*)sipConvertToType(obj, sipType_wxArrayString, NULL, 0, &state, &isErr);
value = *ptr;
sipReleaseType(ptr, sipType_wxArrayString, state);
}
else
// Just use the PyObject itself
value = new wxVariantDataPyObject(obj);
return value;
@@ -313,42 +356,68 @@ PyObject* i_wxVariant_out_helper(const wxVariant& value)
{
PyObject* obj;
if (value.IsType("string"))
if (value.IsNull()) {
obj = Py_None;
Py_INCREF(obj);
}
else if (value.IsType("string"))
obj = wx2PyString(value.GetString());
else if (value.IsType("bool"))
obj = PyBool_FromLong((long)value.GetBool());
else if (value.IsType("long"))
obj = PyLong_FromLong(value.GetLong());
else if (value.IsType("double"))
obj = PyFloat_FromDouble(value.GetDouble());
else if ( value.IsType("datetime") )
{
else if ( value.IsType("datetime") ) {
wxDateTime val = value.GetDateTime();
obj = wxPyConstructObject(new wxDateTime(val), wxT("wxDateTime"));
obj = wxPyConstructObject(new wxDateTime(val), "wxDateTime", true);
}
else if ( value.IsType("wxBitmap") )
{
else if ( value.IsType("wxBitmap") ) {
wxBitmap val;
val << value;
obj = wxPyConstructObject(new wxBitmap(val), wxT("wxBitmap"));
obj = wxPyConstructObject(new wxBitmap(val), "wxBitmap", true);
}
else if ( value.IsType("wxIcon") )
{
else if ( value.IsType("wxImage") ) {
wxImage val;
val << value;
obj = wxPyConstructObject(new wxImage(val), "wxImage", true);
}
else if ( value.IsType("wxIcon") ) {
wxIcon val;
val << value;
obj = wxPyConstructObject(new wxIcon(val), wxT("wxIcon"));
obj = wxPyConstructObject(new wxIcon(val), "wxIcon", true);
}
else if ( value.IsType("PyObject") )
{
else if ( value.IsType("wxColour") ) {
wxColour val;
val << value;
obj = wxPyConstructObject(new wxColour(val), "wxColour", true);
}
else if ( value.IsType("arrstring") ) {
wxArrayString arr = value.GetArrayString();
obj = sipConvertFromType(&arr, sipType_wxArrayString, NULL);
}
else if ( value.IsType("PyObject") ) {
wxVariantDataPyObject* data = (wxVariantDataPyObject*)value.GetData();
obj = data->GetData();
}
else
{
else {
wxString msg = "Unexpected type (\"" + value.GetType() + "\") in wxVariant.";
wxPyErr_SetString(PyExc_TypeError, msg.mb_str());
obj = NULL;
}
return obj;
}

View File

@@ -0,0 +1,87 @@
import unittest
from unittests import wtc
import wx
import wx.propgrid as pg
import six
#---------------------------------------------------------------------------
class pgvariant_Tests(wtc.WidgetTestCase):
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '')
def test_pgvariant1(self):
d1 = wx.Point(123,456)
d2 = pg.testPGVariantTypemap(d1)
assert isinstance(d2, wx.Point)
assert d1 == d2
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '')
def test_pgvariant2(self):
d1 = wx.Point(123,456)
assert pg.testPGVariantTypeName(d1) == 'wxPoint'
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '')
def test_pgvariant3(self):
d1 = wx.Size(123,456)
d2 = pg.testPGVariantTypemap(d1)
assert isinstance(d2, wx.Size)
assert d1 == d2
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '')
def test_pgvariant4(self):
d1 = wx.Size(123,456)
assert pg.testPGVariantTypeName(d1) == 'wxSize'
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '')
def test_pgvariant5(self):
d1 = wx.Font( wx.FontInfo(10).Bold().Underlined() )
d2 = pg.testPGVariantTypemap(d1)
assert isinstance(d2, wx.Font)
assert d2.PointSize == 10
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '')
def test_pgvariant6(self):
d1 = wx.Font( wx.FontInfo(10).Bold().Underlined() )
assert pg.testPGVariantTypeName(d1) == 'wxFont'
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '')
def test_pgvariant7(self):
d1 = [0,1,2,3,4,5,6,7,8,9]
d2 = pg.testPGVariantTypemap(d1)
assert isinstance(d2, list)
assert d1 == d2
d1 = (123,456)
d2 = pg.testPGVariantTypemap(d1)
assert isinstance(d2, list)
assert list(d1) == d2
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypeName'), '')
def test_pgvariant8(self):
d1 = [0,1,2,3,4,5,6,7,8,9]
assert pg.testPGVariantTypeName(d1) == 'wxArrayInt'
d1 = (123,456)
assert pg.testPGVariantTypeName(d1) == 'wxArrayInt'
@unittest.skipIf(not hasattr(pg, 'testPGVariantTypemap'), '')
def test_pgvariant9(self):
d1 = None
d2 = pg.testPGVariantTypemap(d1)
self.assertTrue(d2 is None)
#---------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,51 @@
import unittest
from unittests import wtc
import wx
import wx.propgrid as pg
#---------------------------------------------------------------------------
class propgriddefs_Tests(wtc.WidgetTestCase):
def test_propgriddefs1(self):
pg.PG_INVALID_VALUE
pg.PG_DONT_RECURSE
pg.PG_BASE_OCT
pg.PG_BASE_DEC
pg.PG_BASE_HEX
pg.PG_BASE_HEXL
pg.PG_PREFIX_NONE
pg.PG_PREFIX_0x
pg.PG_PREFIX_DOLLAR_SIGN
pg.PG_KEEP_STRUCTURE
pg.PG_RECURSE
pg.PG_INC_ATTRIBUTES
pg.PG_RECURSE_STARTS
pg.PG_FORCE
pg.PG_SORT_TOP_LEVEL_ONLY
pg.PG_FULL_VALUE
pg.PG_REPORT_ERROR
pg.PG_PROPERTY_SPECIFIC
pg.PG_EDITABLE_VALUE
pg.PG_COMPOSITE_FRAGMENT
pg.PG_UNEDITABLE_COMPOSITE_FRAGMENT
pg.PG_VALUE_IS_CURRENT
pg.PG_PROGRAMMATIC_VALUE
pg.PG_SETVAL_REFRESH_EDITOR
pg.PG_SETVAL_AGGREGATED
pg.PG_SETVAL_FROM_PARENT
pg.PG_SETVAL_BY_USER
pg.PG_LABEL
pg.PG_LABEL_STRING
pg.PG_NULL_BITMAP
pg.PG_COLOUR_BLACK
pg.PG_DEFAULT_IMAGE_SIZE
#---------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()

View File

@@ -27,6 +27,63 @@ class variant_Tests(wtc.WidgetTestCase):
d2 = wx.testVariantTypemap(d1)
self.assertEqual(d1, d2)
@unittest.skipIf(not hasattr(wx, 'testVariantTypemap'), '')
def test_variantNone(self):
d1 = None
d2 = wx.testVariantTypemap(d1)
self.assertTrue(d2 is None)
@unittest.skipIf(not hasattr(wx, 'testVariantTypemap'), '')
def test_variantPyObject(self):
class Data(object):
def __init__(self):
self.a = 123
d1 = Data()
d2 = wx.testVariantTypemap(d1)
self.assertTrue(isinstance(d2, Data))
self.assertEqual(d2.a, 123)
self.assertTrue(d1 is d2)
@unittest.skipIf(not hasattr(wx, 'testVariantTypemap'), '')
def test_variantDateTime1(self):
d1 = wx.DateTime(1,2,2003, 4,5,6)
d2 = wx.testVariantTypemap(d1)
self.assertEqual(d1, d2)
self.assertTrue(isinstance(d2, wx.DateTime))
@unittest.skipIf(not hasattr(wx, 'testVariantTypemap'), '')
def test_variantDateTime2(self):
import datetime
d1 = datetime.datetime(2003,2,1, 4,5,6)
d2 = wx.testVariantTypemap(d1)
#print(wx.testVariantTypeName(d1))
self.assertTrue(isinstance(d2, wx.DateTime))
self.assertEqual(d2.year, 2003)
self.assertEqual(d2.month, wx.DateTime.Feb)
self.assertEqual(d2.day, 1)
self.assertEqual(d2.hour, 4)
self.assertEqual(d2.minute, 5)
self.assertEqual(d2.second, 6)
@unittest.skipIf(not hasattr(wx, 'testVariantTypemap'), '')
def test_variantArrayString1(self):
a1 = "This is a test".split()
a2 = wx.testVariantTypemap(a1)
self.assertEqual(a1, a2)
self.assertTrue(isinstance(a2, list))
@unittest.skipIf(not hasattr(wx, 'testVariantTypeName'), '')
def test_variantArrayString2(self):
a = "This is a test".split()
s = wx.testVariantTypeName(a)
self.assertEqual(s, "arrstring")
#---------------------------------------------------------------------------