Ran update_copyright.py.
[hooke.git] / hooke / ui / gui / panel / propertyeditor-propgrid.py
index edf0968ab8ace51886c548f0530683874dbd51d7..51b38c86126e320c17c4ab7ac86938151e479e40 100644 (file)
-# Copyright\r
-\r
-"""Property editor panel for Hooke.\r
-"""\r
-\r
-import sys\r
-import os.path\r
-\r
-import wx\r
-import wx.propgrid as wxpg\r
-\r
-# There are many comments and code fragments in here from the demo app.\r
-# They should come in handy to expand the functionality in the future.\r
-\r
-class Display (object):\r
-    property_descriptor = []\r
-    def __init__(self):\r
-        pass\r
-\r
-class ValueObject (object):\r
-    def __init__(self):\r
-        pass\r
-\r
-\r
-class IntProperty2 (wxpg.PyProperty):\r
-    """This is a simple re-implementation of wxIntProperty.\r
-    """\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):\r
-        wxpg.PyProperty.__init__(self, label, name)\r
-        self.SetValue(value)\r
-\r
-    def GetClassName(self):\r
-        return "IntProperty2"\r
-\r
-    def GetEditor(self):\r
-        return "TextCtrl"\r
-\r
-    def GetValueAsString(self, flags):\r
-        return str(self.GetValue())\r
-\r
-    def PyStringToValue(self, s, flags):\r
-        try:\r
-            v = int(s)\r
-            if self.GetValue() != v:\r
-                return v\r
-        except TypeError:\r
-            if flags & wxpg.PG_REPORT_ERROR:\r
-                wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")\r
-        return False\r
-\r
-    def PyIntToValue(self, v, flags):\r
-        if (self.GetValue() != v):\r
-            return v\r
-\r
-\r
-class PyFilesProperty(wxpg.PyArrayStringProperty):\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):\r
-        wxpg.PyArrayStringProperty.__init__(self, label, name, value)\r
-        self.SetValue(value)\r
-\r
-    def OnSetValue(self, v):\r
-        self.value = v\r
-        self.display = ', '.join(self.value)\r
-\r
-    def GetValueAsString(self, argFlags):\r
-        return self.display\r
-\r
-    def PyStringToValue(self, s, flags):\r
-        return [a.strip() for a in s.split(',')]\r
-\r
-    def OnEvent(self, propgrid, ctrl, event):\r
-        if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:\r
-            # Show dialog to select a string, call DoSetValue and\r
-            # return True, if value changed.\r
-            return True\r
-\r
-        return False\r
-\r
-\r
-class PyObjectPropertyValue:\r
-    """\\r
-    Value type of our sample PyObjectProperty. We keep a simple dash-delimited\r
-    list of string given as argument to constructor.\r
-    """\r
-    def __init__(self, s=None):\r
-        try:\r
-            self.ls = [a.strip() for a in s.split('-')]\r
-        except:\r
-            self.ls = []\r
-\r
-    def __repr__(self):\r
-        return ' - '.join(self.ls)\r
-\r
-\r
-class PyObjectProperty(wxpg.PyProperty):\r
-    """\\r
-    Another simple example. This time our value is a PyObject (NOTE: we can't\r
-    return an arbitrary python object in DoGetValue. It cannot be a simple\r
-    type such as int, bool, double, or string, nor an array or wxObject based.\r
-    Dictionary, None, or any user-specified Python object is allowed).\r
-    """\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):\r
-        wxpg.PyProperty.__init__(self, label, name)\r
-        self.SetValue(value)\r
-\r
-    def GetClassName(self):\r
-        return self.__class__.__name__\r
-\r
-    def GetEditor(self):\r
-        return "TextCtrl"\r
-\r
-    def GetValueAsString(self, flags):\r
-        return repr(self.GetValue())\r
-\r
-    def PyStringToValue(self, s, flags):\r
-        return PyObjectPropertyValue(s)\r
-\r
-\r
-class ShapeProperty(wxpg.PyEnumProperty):\r
-    """\\r
-    Demonstrates use of OnCustomPaint method.\r
-    """\r
-    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):\r
-        wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)\r
-\r
-    def OnMeasureImage(self, index):\r
-        return wxpg.DEFAULT_IMAGE_SIZE\r
-\r
-    def OnCustomPaint(self, dc, rect, paint_data):\r
-        """\\r
-        paint_data.m_choiceItem is -1 if we are painting the control,\r
-        in which case we need to get the drawn item using DoGetValue.\r
-        """\r
-        item = paint_data.m_choiceItem\r
-        if item == -1:\r
-            item = self.DoGetValue()\r
-\r
-        dc.SetPen(wx.Pen(wx.BLACK))\r
-        dc.SetBrush(wx.Brush(wx.BLACK))\r
-\r
-        if item == 0:\r
-            dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)\r
-        elif item == 1:\r
-            half_width = rect.width / 2\r
-            dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)\r
-        elif item == 2:\r
-            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)\r
-\r
-\r
-class LargeImagePickerCtrl(wx.Window):\r
-    """\\r
-    Control created and used by LargeImageEditor.\r
-    """\r
-    def __init__(self):\r
-        pre = wx.PreWindow()\r
-        self.PostCreate(pre)\r
-\r
-    def Create(self, parent, id_, pos, size, style = 0):\r
-        wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)\r
-        img_spc = size[1]\r
-        self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)\r
-        self.SetBackgroundColour(wx.WHITE)\r
-        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)\r
-        self.property = None\r
-        self.bmp = None\r
-        self.Bind(wx.EVT_PAINT, self.OnPaint)\r
-\r
-    def OnPaint(self, event):\r
-        dc = wx.BufferedPaintDC(self)\r
-\r
-        whiteBrush = wx.Brush(wx.WHITE)\r
-        dc.SetBackground(whiteBrush)\r
-        dc.Clear()\r
-\r
-        bmp = self.bmp\r
-        if bmp:\r
-            dc.DrawBitmap(bmp, 2, 2)\r
-        else:\r
-            dc.SetPen(wx.Pen(wx.BLACK))\r
-            dc.SetBrush(whiteBrush)\r
-            dc.DrawRectangle(2, 2, 64, 64)\r
-\r
-    def RefreshThumbnail(self):\r
-        """\\r
-        We use here very simple image scaling code.\r
-        """\r
-        if not self.property:\r
-            self.bmp = None\r
-            return\r
-\r
-        path = self.property.DoGetValue()\r
-\r
-        if not os.path.isfile(path):\r
-            self.bmp = None\r
-            return\r
-\r
-        image = wx.Image(path)\r
-        image.Rescale(64, 64)\r
-        self.bmp = wx.BitmapFromImage(image)\r
-\r
-    def SetProperty(self, property):\r
-        self.property = property\r
-        self.tc.SetValue(property.GetDisplayedString())\r
-        self.RefreshThumbnail()\r
-\r
-    def SetValue(self, s):\r
-        self.RefreshThumbnail()\r
-        self.tc.SetValue(s)\r
-\r
-    def GetLastPosition(self):\r
-        return self.tc.GetLastPosition()\r
-\r
-\r
-class LargeImageEditor(wxpg.PyEditor):\r
-    """\\r
-    Double-height text-editor with image in front.\r
-    """\r
-    def __init__(self):\r
-        wxpg.PyEditor.__init__(self)\r
-\r
-    def CreateControls(self, propgrid, property, pos, sz):\r
-        try:\r
-            h = 64 + 6\r
-            x = propgrid.GetSplitterPosition()\r
-            x2 = propgrid.GetClientSize().x\r
-            bw = propgrid.GetRowHeight()\r
-            lipc = LargeImagePickerCtrl()\r
-            if sys.platform == 'win32':\r
-                lipc.Hide()\r
-            lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))\r
-            lipc.SetProperty(property)\r
-            # Hmmm.. how to have two-stage creation without subclassing?\r
-            #btn = wx.PreButton()\r
-            #pre = wx.PreWindow()\r
-            #self.PostCreate(pre)\r
-            #if sys.platform == 'win32':\r
-            #    btn.Hide()\r
-            #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)\r
-            btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)\r
-            return (lipc, btn)\r
-        except:\r
-            import traceback\r
-            print traceback.print_exc()\r
-\r
-    def UpdateControl(self, property, ctrl):\r
-        ctrl.SetValue(property.GetDisplayedString())\r
-\r
-    def DrawValue(self, dc, property, rect):\r
-        if not (property.GetFlags() & wxpg.PG_PROP_AUTO_UNSPECIFIED):\r
-            dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );\r
-\r
-    def OnEvent(self, propgrid, ctrl, event):\r
-        if not ctrl:\r
-            return False\r
-\r
-        evtType = event.GetEventType()\r
-\r
-        if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:\r
-            if propgrid.IsEditorsValueModified():\r
-                return True\r
-\r
-        elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:\r
-            if not property.HasFlag(wxpg.PG_PROP_AUTO_UNSPECIFIED) or not ctrl or \\r
-               ctrl.GetLastPosition() > 0:\r
-\r
-                # We must check this since an 'empty' text event\r
-                # may be triggered when creating the property.\r
-                PG_FL_IN_SELECT_PROPERTY = 0x00100000\r
-                if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):\r
-                    event.Skip();\r
-                    event.SetId(propgrid.GetId());\r
-\r
-                propgrid.EditorsValueWasModified();\r
-\r
-        return False\r
-\r
-\r
-    def CopyValueFromControl(self, property, ctrl):\r
-        tc = ctrl.tc\r
-        res = property.SetValueFromString(tc.GetValue(),0)\r
-        # Changing unspecified always causes event (returning\r
-        # true here should be enough to trigger it).\r
-        if not res and property.IsFlagSet(wxpg.PG_PROP_AUTO_UNSPECIFIED):\r
-            res = True\r
-\r
-        return res\r
-\r
-    def SetValueToUnspecified(self, ctrl):\r
-        ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));\r
-\r
-    def SetControlStringValue(self, ctrl, txt):\r
-        ctrl.SetValue(txt)\r
-\r
-    def OnFocus(self, property, ctrl):\r
-        ctrl.tc.SetSelection(-1,-1)\r
-        ctrl.tc.SetFocus()\r
-\r
-\r
-class PropertyEditor(wx.Panel):\r
-\r
-    def __init__(self, parent):\r
-        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.\r
-        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))\r
-\r
-        sizer = wx.BoxSizer(wx.VERTICAL)\r
-\r
-        self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)\r
-\r
-        # Show help as tooltips\r
-        self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)\r
-\r
-        #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)\r
-        #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)\r
-        #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)\r
-\r
-        # Needed by custom image editor\r
-        wx.InitAllImageHandlers()\r
-\r
-        #\r
-        # Let's create a simple custom editor\r
-        #\r
-        # NOTE: Editor must be registered *before* adding a property that uses it.\r
-        self.pg.RegisterEditor(LargeImageEditor)\r
-\r
-        '''\r
-        #\r
-        # Add properties\r
-        #\r
-\r
-        pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )\r
-        pg.Append( wxpg.StringProperty("String",value="Some Text") )\r
-        pg.Append( wxpg.IntProperty("Int",value=100) )\r
-        pg.Append( wxpg.FloatProperty("Float",value=100.0) )\r
-        pg.Append( wxpg.BoolProperty("Bool",value=True) )\r
-        pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )\r
-        pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)\r
-\r
-        pg.Append( wxpg.PropertyCategory("2 - More Properties") )\r
-        pg.Append( wxpg.LongStringProperty("LongString",value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin.") )\r
-        pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )\r
-        pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )\r
-        pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )\r
-\r
-        pg.Append( wxpg.EnumProperty("Enum","Enum",\r
-                                     ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],\r
-                                     [10,11,12],0) )\r
-        pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )\r
-\r
-        pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )\r
-        pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )\r
-        pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )\r
-        pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )\r
-        pg.Append( wxpg.SystemColourProperty("SystemColour") )\r
-        pg.Append( wxpg.ImageFileProperty("ImageFile") )\r
-        pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )\r
-\r
-        pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )\r
-        pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )\r
-        pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )\r
-        pg.Append( wxpg.FontDataProperty("FontData") )\r
-        pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )\r
-        pg.SetPropertyEditor("IntWithSpin","SpinCtrl")\r
-        pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )\r
-        pg.SetPropertyHelpString( "String", "String Property help string!" )\r
-        pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )\r
-\r
-        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )\r
-        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )\r
-        pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )\r
-\r
-        pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )\r
-        pg.Append( IntProperty2("IntProperty2", value=1024) )\r
-\r
-        pg.Append( ShapeProperty("ShapeProperty", value=0) )\r
-        pg.Append( PyObjectProperty("PyObjectProperty") )\r
-\r
-        pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )\r
-        pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")\r
-\r
-\r
-        pg.SetPropertyClientData( "Point", 1234 )\r
-        if pg.GetPropertyClientData( "Point" ) != 1234:\r
-            raise ValueError("Set/GetPropertyClientData() failed")\r
-\r
-        # Test setting unicode string\r
-        pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")\r
-\r
-        #\r
-        # Test some code that *should* fail (but not crash)\r
-        #try:\r
-            #a_ = pg.GetPropertyValue( "NotARealProperty" )\r
-            #pg.EnableProperty( "NotAtAllRealProperty", False )\r
-            #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )\r
-        #except:\r
-            #pass\r
-            #raise\r
-\r
-        '''\r
-        sizer.Add(self.pg, 1, wx.EXPAND)\r
-        self.SetSizer(sizer)\r
-        sizer.SetSizeHints(self)\r
-\r
-        self.SelectedTreeItem = None\r
-\r
-    def GetPropertyValues(self):\r
-        return self.pg.GetPropertyValues()\r
-\r
-    def Initialize(self, properties):\r
-        pg = self.pg\r
-        pg.Clear()\r
-\r
-        if properties:\r
-            for element in properties:\r
-                if element[1]['type'] == 'arraystring':\r
-                    elements = element[1]['elements']\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    #retrieve individual strings\r
-                    property_value = split(property_value, ' ')\r
-                    #remove " delimiters\r
-                    values = [value.strip('"') for value in property_value]\r
-                    pg.Append(wxpg.ArrayStringProperty(element[0], value=values))\r
-\r
-                if element[1]['type'] == 'boolean':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1].as_bool('value')\r
-                    else:\r
-                        property_value = element[1].as_bool('default')\r
-                    property_control = wxpg.BoolProperty(element[0], value=property_value)\r
-                    pg.Append(property_control)\r
-                    pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)\r
-\r
-                #if element[0] == 'category':\r
-                    #pg.Append(wxpg.PropertyCategory(element[1]))\r
-\r
-                if element[1]['type'] == 'color':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    property_value = eval(property_value)\r
-                    pg.Append(wxpg.ColourProperty(element[0], value=property_value))\r
-\r
-                if element[1]['type'] == 'enum':\r
-                    elements = element[1]['elements']\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.EnumProperty(element[0], element[0], elements, [], elements.index(property_value)))\r
-\r
-                if element[1]['type'] == 'filename':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.FileProperty(element[0], value=property_value))\r
-\r
-                if element[1]['type'] == 'float':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1].as_float('value')\r
-                    else:\r
-                        property_value = element[1].as_float('default')\r
-                    property_control = wxpg.FloatProperty(element[0], value=property_value)\r
-                    pg.Append(property_control)\r
-\r
-                if element[1]['type'] == 'folder':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.DirProperty(element[0], value=property_value))\r
-\r
-                if element[1]['type'] == 'integer':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1].as_int('value')\r
-                    else:\r
-                        property_value = element[1].as_int('default')\r
-                    property_control = wxpg.IntProperty(element[0], value=property_value)\r
-                    if 'maximum' in element[1]:\r
-                        property_control.SetAttribute('Max', element[1].as_int('maximum'))\r
-                    if 'minimum' in element[1]:\r
-                        property_control.SetAttribute('Min', element[1].as_int('minimum'))\r
-                    property_control.SetAttribute('Wrap', True)\r
-                    pg.Append(property_control)\r
-                    pg.SetPropertyEditor(element[0], 'SpinCtrl')\r
-\r
-                if element[1]['type'] == 'string':\r
-                    if 'value' in element[1]:\r
-                        property_value = element[1]['value']\r
-                    else:\r
-                        property_value = element[1]['default']\r
-                    pg.Append(wxpg.StringProperty(element[0], value=property_value))\r
-\r
-        pg.Refresh()\r
-\r
-    def OnReserved(self, event):\r
-        pass\r
+# Copyright (C) 2010-2012 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of Hooke.
+#
+# Hooke is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Hooke is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Hooke.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Property editor panel for Hooke.
+"""
+
+import sys
+import os.path
+
+import wx
+import wx.propgrid as wxpg
+
+# There are many comments and code fragments in here from the demo app.
+# They should come in handy to expand the functionality in the future.
+
+class Display (object):
+    property_descriptor = []
+    def __init__(self):
+        pass
+
+class ValueObject (object):
+    def __init__(self):
+        pass
+
+
+class IntProperty2 (wxpg.PyProperty):
+    """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)
+        self.SetValue(value)
+
+    def GetClassName(self):
+        return "IntProperty2"
+
+    def GetEditor(self):
+        return "TextCtrl"
+
+    def GetValueAsString(self, flags):
+        return str(self.GetValue())
+
+    def PyStringToValue(self, s, flags):
+        try:
+            v = int(s)
+            if self.GetValue() != v:
+                return v
+        except TypeError:
+            if flags & wxpg.PG_REPORT_ERROR:
+                wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")
+        return False
+
+    def PyIntToValue(self, v, flags):
+        if (self.GetValue() != v):
+            return v
+
+
+class PyFilesProperty(wxpg.PyArrayStringProperty):
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):
+        wxpg.PyArrayStringProperty.__init__(self, label, name, value)
+        self.SetValue(value)
+
+    def OnSetValue(self, v):
+        self.value = v
+        self.display = ', '.join(self.value)
+
+    def GetValueAsString(self, argFlags):
+        return self.display
+
+    def PyStringToValue(self, s, flags):
+        return [a.strip() for a in s.split(',')]
+
+    def OnEvent(self, propgrid, ctrl, event):
+        if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:
+            # Show dialog to select a string, call DoSetValue and
+            # return True, if value changed.
+            return True
+
+        return False
+
+
+class PyObjectPropertyValue:
+    """\
+    Value type of our sample PyObjectProperty. We keep a simple dash-delimited
+    list of string given as argument to constructor.
+    """
+    def __init__(self, s=None):
+        try:
+            self.ls = [a.strip() for a in s.split('-')]
+        except:
+            self.ls = []
+
+    def __repr__(self):
+        return ' - '.join(self.ls)
+
+
+class PyObjectProperty(wxpg.PyProperty):
+    """\
+    Another simple example. This time our value is a PyObject (NOTE: we can't
+    return an arbitrary python object in DoGetValue. It cannot be a simple
+    type such as int, bool, double, or string, nor an array or wxObject based.
+    Dictionary, None, or any user-specified Python object is allowed).
+    """
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):
+        wxpg.PyProperty.__init__(self, label, name)
+        self.SetValue(value)
+
+    def GetClassName(self):
+        return self.__class__.__name__
+
+    def GetEditor(self):
+        return "TextCtrl"
+
+    def GetValueAsString(self, flags):
+        return repr(self.GetValue())
+
+    def PyStringToValue(self, s, flags):
+        return PyObjectPropertyValue(s)
+
+
+class ShapeProperty(wxpg.PyEnumProperty):
+    """\
+    Demonstrates use of OnCustomPaint method.
+    """
+    def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):
+        wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)
+
+    def OnMeasureImage(self, index):
+        return wxpg.DEFAULT_IMAGE_SIZE
+
+    def OnCustomPaint(self, dc, rect, paint_data):
+        """\
+        paint_data.m_choiceItem is -1 if we are painting the control,
+        in which case we need to get the drawn item using DoGetValue.
+        """
+        item = paint_data.m_choiceItem
+        if item == -1:
+            item = self.DoGetValue()
+
+        dc.SetPen(wx.Pen(wx.BLACK))
+        dc.SetBrush(wx.Brush(wx.BLACK))
+
+        if item == 0:
+            dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)
+        elif item == 1:
+            half_width = rect.width / 2
+            dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)
+        elif item == 2:
+            dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
+
+
+class LargeImagePickerCtrl(wx.Window):
+    """\
+    Control created and used by LargeImageEditor.
+    """
+    def __init__(self):
+        pre = wx.PreWindow()
+        self.PostCreate(pre)
+
+    def Create(self, parent, id_, pos, size, style = 0):
+        wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)
+        img_spc = size[1]
+        self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)
+        self.SetBackgroundColour(wx.WHITE)
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+        self.property = None
+        self.bmp = None
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+    def OnPaint(self, event):
+        dc = wx.BufferedPaintDC(self)
+
+        whiteBrush = wx.Brush(wx.WHITE)
+        dc.SetBackground(whiteBrush)
+        dc.Clear()
+
+        bmp = self.bmp
+        if bmp:
+            dc.DrawBitmap(bmp, 2, 2)
+        else:
+            dc.SetPen(wx.Pen(wx.BLACK))
+            dc.SetBrush(whiteBrush)
+            dc.DrawRectangle(2, 2, 64, 64)
+
+    def RefreshThumbnail(self):
+        """\
+        We use here very simple image scaling code.
+        """
+        if not self.property:
+            self.bmp = None
+            return
+
+        path = self.property.DoGetValue()
+
+        if not os.path.isfile(path):
+            self.bmp = None
+            return
+
+        image = wx.Image(path)
+        image.Rescale(64, 64)
+        self.bmp = wx.BitmapFromImage(image)
+
+    def SetProperty(self, property):
+        self.property = property
+        self.tc.SetValue(property.GetDisplayedString())
+        self.RefreshThumbnail()
+
+    def SetValue(self, s):
+        self.RefreshThumbnail()
+        self.tc.SetValue(s)
+
+    def GetLastPosition(self):
+        return self.tc.GetLastPosition()
+
+
+class LargeImageEditor(wxpg.PyEditor):
+    """\
+    Double-height text-editor with image in front.
+    """
+    def __init__(self):
+        wxpg.PyEditor.__init__(self)
+
+    def CreateControls(self, propgrid, property, pos, sz):
+        try:
+            h = 64 + 6
+            x = propgrid.GetSplitterPosition()
+            x2 = propgrid.GetClientSize().x
+            bw = propgrid.GetRowHeight()
+            lipc = LargeImagePickerCtrl()
+            if sys.platform == 'win32':
+                lipc.Hide()
+            lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))
+            lipc.SetProperty(property)
+            # Hmmm.. how to have two-stage creation without subclassing?
+            #btn = wx.PreButton()
+            #pre = wx.PreWindow()
+            #self.PostCreate(pre)
+            #if sys.platform == 'win32':
+            #    btn.Hide()
+            #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
+            btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
+            return (lipc, btn)
+        except:
+            import traceback
+            print traceback.print_exc()
+
+    def UpdateControl(self, property, ctrl):
+        ctrl.SetValue(property.GetDisplayedString())
+
+    def DrawValue(self, dc, property, rect):
+        if not (property.GetFlags() & wxpg.PG_PROP_AUTO_UNSPECIFIED):
+            dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );
+
+    def OnEvent(self, propgrid, ctrl, event):
+        if not ctrl:
+            return False
+
+        evtType = event.GetEventType()
+
+        if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:
+            if propgrid.IsEditorsValueModified():
+                return True
+
+        elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:
+            if not property.HasFlag(wxpg.PG_PROP_AUTO_UNSPECIFIED) or not ctrl or \
+               ctrl.GetLastPosition() > 0:
+
+                # We must check this since an 'empty' text event
+                # may be triggered when creating the property.
+                PG_FL_IN_SELECT_PROPERTY = 0x00100000
+                if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):
+                    event.Skip();
+                    event.SetId(propgrid.GetId());
+
+                propgrid.EditorsValueWasModified();
+
+        return False
+
+
+    def CopyValueFromControl(self, property, ctrl):
+        tc = ctrl.tc
+        res = property.SetValueFromString(tc.GetValue(),0)
+        # Changing unspecified always causes event (returning
+        # true here should be enough to trigger it).
+        if not res and property.IsFlagSet(wxpg.PG_PROP_AUTO_UNSPECIFIED):
+            res = True
+
+        return res
+
+    def SetValueToUnspecified(self, ctrl):
+        ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));
+
+    def SetControlStringValue(self, ctrl, txt):
+        ctrl.SetValue(txt)
+
+    def OnFocus(self, property, ctrl):
+        ctrl.tc.SetSelection(-1,-1)
+        ctrl.tc.SetFocus()
+
+
+class PropertyEditor(wx.Panel):
+
+    def __init__(self, parent):
+        # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
+        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)
+
+        # Show help as tooltips
+        self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)
+
+        #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)
+        #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
+        #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)
+
+        # Needed by custom image editor
+        wx.InitAllImageHandlers()
+
+        #
+        # Let's create a simple custom editor
+        #
+        # NOTE: Editor must be registered *before* adding a property that uses it.
+        self.pg.RegisterEditor(LargeImageEditor)
+
+        '''
+        #
+        # Add properties
+        #
+
+        pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )
+        pg.Append( wxpg.StringProperty("String",value="Some Text") )
+        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)
+
+        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") )
+        pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )
+
+        pg.Append( wxpg.EnumProperty("Enum","Enum",
+                                     ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],
+                                     [10,11,12],0) )
+        pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )
+
+        pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )
+        pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )
+        pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )
+        pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )
+        pg.Append( wxpg.SystemColourProperty("SystemColour") )
+        pg.Append( wxpg.ImageFileProperty("ImageFile") )
+        pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )
+
+        pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )
+        pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )
+        pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )
+        pg.Append( wxpg.FontDataProperty("FontData") )
+        pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )
+        pg.SetPropertyEditor("IntWithSpin","SpinCtrl")
+        pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )
+        pg.SetPropertyHelpString( "String", "String Property help string!" )
+        pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )
+
+        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )
+        pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )
+        pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )
+
+        pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )
+        pg.Append( IntProperty2("IntProperty2", value=1024) )
+
+        pg.Append( ShapeProperty("ShapeProperty", value=0) )
+        pg.Append( PyObjectProperty("PyObjectProperty") )
+
+        pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )
+        pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")
+
+
+        pg.SetPropertyClientData( "Point", 1234 )
+        if pg.GetPropertyClientData( "Point" ) != 1234:
+            raise ValueError("Set/GetPropertyClientData() failed")
+
+        # Test setting unicode string
+        pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")
+
+        #
+        # Test some code that *should* fail (but not crash)
+        #try:
+            #a_ = pg.GetPropertyValue( "NotARealProperty" )
+            #pg.EnableProperty( "NotAtAllRealProperty", False )
+            #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )
+        #except:
+            #pass
+            #raise
+
+        '''
+        sizer.Add(self.pg, 1, wx.EXPAND)
+        self.SetSizer(sizer)
+        sizer.SetSizeHints(self)
+
+        self.SelectedTreeItem = None
+
+    def GetPropertyValues(self):
+        return self.pg.GetPropertyValues()
+
+    def Initialize(self, properties):
+        pg = self.pg
+        pg.Clear()
+
+        if properties:
+            for element in properties:
+                if element[1]['type'] == 'arraystring':
+                    elements = element[1]['elements']
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    #retrieve individual strings
+                    property_value = split(property_value, ' ')
+                    #remove " delimiters
+                    values = [value.strip('"') for value in property_value]
+                    pg.Append(wxpg.ArrayStringProperty(element[0], value=values))
+
+                if element[1]['type'] == 'boolean':
+                    if 'value' in element[1]:
+                        property_value = element[1].as_bool('value')
+                    else:
+                        property_value = element[1].as_bool('default')
+                    property_control = wxpg.BoolProperty(element[0], value=property_value)
+                    pg.Append(property_control)
+                    pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)
+
+                #if element[0] == 'category':
+                    #pg.Append(wxpg.PropertyCategory(element[1]))
+
+                if element[1]['type'] == 'color':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    property_value = eval(property_value)
+                    pg.Append(wxpg.ColourProperty(element[0], value=property_value))
+
+                if element[1]['type'] == 'enum':
+                    elements = element[1]['elements']
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.EnumProperty(element[0], element[0], elements, [], elements.index(property_value)))
+
+                if element[1]['type'] == 'filename':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.FileProperty(element[0], value=property_value))
+
+                if element[1]['type'] == 'float':
+                    if 'value' in element[1]:
+                        property_value = element[1].as_float('value')
+                    else:
+                        property_value = element[1].as_float('default')
+                    property_control = wxpg.FloatProperty(element[0], value=property_value)
+                    pg.Append(property_control)
+
+                if element[1]['type'] == 'folder':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.DirProperty(element[0], value=property_value))
+
+                if element[1]['type'] == 'integer':
+                    if 'value' in element[1]:
+                        property_value = element[1].as_int('value')
+                    else:
+                        property_value = element[1].as_int('default')
+                    property_control = wxpg.IntProperty(element[0], value=property_value)
+                    if 'maximum' in element[1]:
+                        property_control.SetAttribute('Max', element[1].as_int('maximum'))
+                    if 'minimum' in element[1]:
+                        property_control.SetAttribute('Min', element[1].as_int('minimum'))
+                    property_control.SetAttribute('Wrap', True)
+                    pg.Append(property_control)
+                    pg.SetPropertyEditor(element[0], 'SpinCtrl')
+
+                if element[1]['type'] == 'string':
+                    if 'value' in element[1]:
+                        property_value = element[1]['value']
+                    else:
+                        property_value = element[1]['default']
+                    pg.Append(wxpg.StringProperty(element[0], value=property_value))
+
+        pg.Refresh()
+
+    def OnReserved(self, event):
+        pass
+
+
+
+#        #property editor
+#        self.panelProperties.pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChanged)
+#    def OnPropGridChanged (self, event):
+#        prop = event.GetProperty()
+#        if prop:
+#            item_section = self.panelProperties.SelectedTreeItem
+#            item_plugin = self._c['commands']._c['tree'].GetItemParent(item_section)
+#            plugin = self._c['commands']._c['tree'].GetItemText(item_plugin)
+#            config = self.gui.config[plugin]
+#            property_section = self._c['commands']._c['tree'].GetItemText(item_section)
+#            property_key = prop.GetName()
+#            property_value = prop.GetDisplayedString()
+#
+#            config[property_section][property_key]['value'] = property_value
+