diff --git a/demo/PropertyGrid.py b/demo/PropertyGrid.py index 99463bd8..6815e05a 100644 --- a/demo/PropertyGrid.py +++ b/demo/PropertyGrid.py @@ -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)): diff --git a/docs/sphinx/itemToModuleMap.json b/docs/sphinx/itemToModuleMap.json index 3455902b..03a0ab79 100644 --- a/docs/sphinx/itemToModuleMap.json +++ b/docs/sphinx/itemToModuleMap.json @@ -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.", diff --git a/etg/_propgrid.py b/etg/_propgrid.py index 8467a263..706b2cc9 100644 --- a/etg/_propgrid.py +++ b/etg/_propgrid.py @@ -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) diff --git a/etg/propgrid.py b/etg/propgrid.py index 1a71fe8a..530aea03 100644 --- a/etg/propgrid.py +++ b/etg/propgrid.py @@ -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) diff --git a/etg/propgridadvprops.py b/etg/propgridadvprops.py index e4023f51..c0ff20ae 100644 --- a/etg/propgridadvprops.py +++ b/etg/propgridadvprops.py @@ -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) diff --git a/etg/propgriddefs.py b/etg/propgriddefs.py new file mode 100644 index 00000000..279b3770 --- /dev/null +++ b/etg/propgriddefs.py @@ -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() + diff --git a/etg/propgrideditors.py b/etg/propgrideditors.py index c96a59f7..ef7dc83d 100644 --- a/etg/propgrideditors.py +++ b/etg/propgrideditors.py @@ -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) diff --git a/etg/propgridiface.py b/etg/propgridiface.py index 8c7ea658..306f6b7d 100644 --- a/etg/propgridiface.py +++ b/etg/propgridiface.py @@ -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( + 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(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 "@@". + :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 "@@". + """, + 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) diff --git a/etg/propgridmanager.py b/etg/propgridmanager.py index e086ff1e..825749ed 100644 --- a/etg/propgridmanager.py +++ b/etg/propgridmanager.py @@ -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 diff --git a/etg/propgridpagestate.py b/etg/propgridpagestate.py index ccc75741..b92d5f94 100644 --- a/etg/propgridpagestate.py +++ b/etg/propgridpagestate.py @@ -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) diff --git a/etg/propgridproperty.py b/etg/propgridproperty.py index a5b980d4..256d773d 100644 --- a/etg/propgridproperty.py +++ b/etg/propgridproperty.py @@ -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) diff --git a/etg/propgridprops.py b/etg/propgridprops.py index 3826685d..2dfe9b1b 100644 --- a/etg/propgridprops.py +++ b/etg/propgridprops.py @@ -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) diff --git a/etgtools/extractors.py b/etgtools/extractors.py index 486e48a3..5022a22a 100644 --- a/etgtools/extractors.py +++ b/etgtools/extractors.py @@ -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 diff --git a/ext/wxWidgets b/ext/wxWidgets index dc7acca2..6eb5943e 160000 --- a/ext/wxWidgets +++ b/ext/wxWidgets @@ -1 +1 @@ -Subproject commit dc7acca2cd4c87fb2541df17d8b4d80e0f607cc8 +Subproject commit 6eb5943ed012cf870d125259920f75866d9ec8c2 diff --git a/src/pgvariant.sip b/src/pgvariant.sip new file mode 100644 index 00000000..39be3d56 --- /dev/null +++ b/src/pgvariant.sip @@ -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); diff --git a/src/variant.sip b/src/variant.sip index e76cf094..e2cd2907 100644 --- a/src/variant.sip +++ b/src/variant.sip @@ -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); diff --git a/src/wxpy_api.sip b/src/wxpy_api.sip index 7b0b3592..fe59c72d 100644 --- a/src/wxpy_api.sip +++ b/src/wxpy_api.sip @@ -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; } diff --git a/unittests/test_pgvariant.py b/unittests/test_pgvariant.py new file mode 100644 index 00000000..686698c1 --- /dev/null +++ b/unittests/test_pgvariant.py @@ -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() diff --git a/unittests/test_propgriddefs.py b/unittests/test_propgriddefs.py new file mode 100644 index 00000000..ba6c6774 --- /dev/null +++ b/unittests/test_propgriddefs.py @@ -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() diff --git a/unittests/test_variant.py b/unittests/test_variant.py index 36c59391..d4f9563c 100644 --- a/unittests/test_variant.py +++ b/unittests/test_variant.py @@ -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") + + + #---------------------------------------------------------------------------