--- /dev/null
+# Copyright\r
+\r
+"""Property editor panel for Hooke.\r
+\r
+wxPropertyGrid is `included in wxPython >= 2.9.1 <included>`_. Until\r
+then, we'll avoid it because of the *nix build problems.\r
+\r
+This module hacks together a workaround to be used until 2.9.1 is\r
+widely installed (or at least released ;).\r
+\r
+.. _included:\r
+ http://wxpropgrid.sourceforge.net/cgi-bin/index?page=download\r
+"""\r
+\r
+import wx.grid\r
+\r
+from . import Panel\r
+\r
+\r
+def prop_from_setting(setting):\r
+ """Convert a :class:`~hooke.config.Setting` to a :class:`Property`.\r
+ """\r
+ raise NotImplementedError()\r
+\r
+def prop_from_argument(argument):\r
+ """Convert a :class:`~hooke.command.Argument` to a :class:`Property`.\r
+ """\r
+ raise NotImplementedError()\r
+\r
+\r
+class Property (object):\r
+ def __init__(self, type, label, default, help=None):\r
+ self.type = type\r
+ self.label = label\r
+ self.default = default\r
+ self.help = help\r
+\r
+ def get_editor(self):\r
+ """Return a wx.Window instance containing a suitable editor.\r
+\r
+ Retains a reference to the returned editory as `._editor`.\r
+ """\r
+ raise NotImplementedError()\r
+\r
+ def value_string(self, value):\r
+ """Return a string representation of `value` for loading the table.\r
+ """\r
+ return str(value)\r
+\r
+ def get_value(self):\r
+ """Return the value of `._editor`.\r
+ """\r
+ raise NotImplementedError()\r
+\r
+\r
+class IntProperty (Property):\r
+ def __init__(self, **kwargs):\r
+ assert 'type' not in kwargs, kwargs\r
+ if 'default' not in kwargs:\r
+ kwargs['default'] = 0\r
+ super(IntProperty, self).__init__(type='int', **kwargs)\r
+\r
+ def get_editor(self):\r
+ self._editor = wx.grid.GridCellNumberEditor()\r
+ return self._editor\r
+\r
+ def get_value(self):\r
+ raise int(self._editor.GetValue())\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 PropertyPanel(Panel, wx.grid.Grid):\r
+ def __init__(self, callbacks=None, **kwargs):\r
+ super(PropertyPanel, self).__init__(\r
+ name='propertyeditor', callbacks=callbacks, **kwargs)\r
+ self._properties = []\r
+\r
+ self.CreateGrid(numRows=0, numCols=1)\r
+ self.SetColLabelValue(0, 'value')\r
+\r
+ self._last_tooltip = None\r
+ self.GetGridWindow().Bind(wx.EVT_MOTION, self._on_mouse_over)\r
+\r
+ self.append_property(IntProperty(\r
+ label='my int',\r
+ default=5,\r
+ help='help for my int',\r
+ ))\r
+\r
+ def GetRowLabelValue(self, col=0):\r
+ """Retrieve the label for a particular column.\r
+\r
+ Overrides the default labels, since 'SetRowLabelValue' seems\r
+ to be `ignored`_.\r
+\r
+ .. _ignored:\r
+ http://wiki.wxpython.org/wxPyGridTableBase#Column.2BAC8-Row_Labels\r
+ """\r
+ return self._column_labels[col]\r
+\r
+ def _on_mouse_over(self, event):\r
+ """Enable tooltips.\r
+ """\r
+ x,y = self.CalcUnscrolledPosition(event.GetPosition())\r
+ col,row = self.XYToCell(x, y)\r
+ if col == -1 or row == -1:\r
+ msg = ''\r
+ else:\r
+ msg = self._properties[row].help\r
+ if msg != self._last_tooltip:\r
+ self._last_tooltip = msg\r
+ event.GetEventObject().SetToolTipString(msg)\r
+\r
+ def append_property(self, property):\r
+ if len([p for p in self._properties if p.label == label]) > 0:\r
+ raise ValueError(property) # property.label collision\r
+ self._properties.append(property)\r
+ row = len(self._properties) - 1\r
+ self.AppendRows(numRows=1)\r
+ self.SetRowLabelValue(row, property.label)\r
+ self.SetCellEditor(row=row, col=0, editor=property.get_editor())\r
+ self.set_property(property.label, property.default)\r
+\r
+ def set_property(self, label, value):\r
+ props = [(i,p) for i,p in enumerate(self._properties)\r
+ if p.label == label]\r
+ assert len(props) == 1, props\r
+ row,property = props[0]\r
+ self.SetCellValue(row=row, col=0, s=property.value_string(value))\r
+\r
+ def remove_property(self, property):\r
+ pass\r
+\r
+ def GetPropertyValues(self):\r
+ raise NotImplementedError()\r
+ return self.pg.GetPropertyValues()\r
+\r
+ def Initialize(self, properties):\r
+ raise NotImplementedError()\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
+ raise NotImplementedError()\r
+ pass\r