-# 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_argument(argument, curves=None, playlists=None):\r
- """Convert a :class:`~hooke.command.Argument` to a :class:`Property`.\r
- """\r
- type = argument.type\r
- if type in ['driver']: # intentionally not handled (yet)\r
- return None\r
- if argument.count != 1:\r
- raise NotImplementedError(argument)\r
- kwargs = {\r
- 'label':argument.name,\r
- 'default':argument.default,\r
- 'help':argument.help(),\r
- }\r
- # type consolidation\r
- if type == 'file':\r
- type = 'path'\r
- # type handling\r
- if type in ['string', 'bool', 'int', 'float', 'path']:\r
- _class = globals()['%sProperty' % type.capitalize()]\r
- return _class(**kwargs)\r
- elif type in ['curve', 'playlist']:\r
- if type == 'curve':\r
- choices = curves # extracted from the current playlist\r
- else:\r
- choices = playlists\r
- return ChoiceProperty(choices=choices, **kwargs)\r
- raise NotImplementedError(argument.type)\r
-\r
-def prop_from_setting(setting):\r
- """Convert a :class:`~hooke.config.Setting` 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 suitable grid editor.\r
- """\r
- raise NotImplementedError()\r
-\r
- def get_renderer(self):\r
- """Return a suitable grid renderer.\r
-\r
- Returns `None` if no special renderer is required.\r
- """\r
- return None\r
-\r
- def string_for_value(self, value):\r
- """Return a string representation of `value` for loading the table.\r
- """\r
- return str(value)\r
-\r
- def value_for_string(self, string):\r
- """Return the value represented by `string`.\r
- """\r
- return string\r
-\r
-\r
-class StringProperty (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(StringProperty, self).__init__(type='string', **kwargs)\r
-\r
- def get_editor(self):\r
- return wx.grid.GridCellTextEditor()\r
-\r
- def get_renderer(self):\r
- return wx.grid.GridCellStringRenderer()\r
-\r
-\r
-class BoolProperty (Property):\r
- """A boolean property.\r
-\r
- Notes\r
- -----\r
- Unfortunately, changing a boolean property takes two clicks:\r
-\r
- 1) create the editor\r
- 2) change the value\r
-\r
- There are `ways around this`_, but it's not pretty.\r
-\r
- .. _ways around this:\r
- http://wiki.wxpython.org/Change%20wxGrid%20CheckBox%20with%20one%20click\r
- """\r
- def __init__(self, **kwargs):\r
- assert 'type' not in kwargs, kwargs\r
- if 'default' not in kwargs:\r
- kwargs['default'] = True\r
- super(BoolProperty, self).__init__(type='bool', **kwargs)\r
-\r
- def get_editor(self):\r
- return wx.grid.GridCellBoolEditor()\r
-\r
- def get_renderer(self):\r
- return wx.grid.GridCellBoolRenderer()\r
-\r
- def string_for_value(self, value):\r
- if value == True:\r
- return '1'\r
- return ''\r
-\r
- def value_for_string(self, string):\r
- return string == '1'\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
- return wx.grid.GridCellNumberEditor()\r
-\r
- def get_renderer(self):\r
- return wx.grid.GridCellNumberRenderer()\r
-\r
- def value_for_string(self, string):\r
- return int(string)\r
-\r
-\r
-class FloatProperty (Property):\r
- def __init__(self, **kwargs):\r
- assert 'type' not in kwargs, kwargs\r
- if 'default' not in kwargs:\r
- kwargs['default'] = 0.0\r
- super(FloatProperty, self).__init__(type='float', **kwargs)\r
-\r
- def get_editor(self):\r
- return wx.grid.GridCellFloatEditor()\r
-\r
- def get_renderer(self):\r
- return wx.grid.GridCellFloatRenderer()\r
-\r
- def value_for_string(self, string):\r
- return float(string)\r
-\r
-\r
-class ChoiceProperty (Property):\r
- def __init__(self, choices, **kwargs):\r
- assert 'type' not in kwargs, kwargs\r
- if 'default' in kwargs:\r
- if kwargs['default'] not in choices:\r
- choices.insert(0, kwargs['default'])\r
- else:\r
- kwargs['default'] = choices[0]\r
- super(ChoiceProperty, self).__init__(type='choice', **kwargs)\r
- self._choices = choices\r
-\r
- def get_editor(self):\r
- choices = [self.string_for_value(c) for c in self._choices]\r
- return wx.grid.GridCellChoiceEditor(choices=choices)\r
-\r
- def get_renderer(self):\r
- return None\r
- #return wx.grid.GridCellChoiceRenderer()\r
-\r
- def string_for_value(self, value):\r
- if hasattr(value, 'name'):\r
- return value.name\r
- return str(value)\r
-\r
- def value_for_string(self, string):\r
- for choice in self._choices:\r
- if self.string_for_value(choice) == string:\r
- return choice\r
- raise ValueError(string)\r
-\r
-\r
-class PathProperty (StringProperty):\r
- """Simple file or path property.\r
-\r
- Currently there isn't a fancy file-picker popup. Perhaps in the\r
- future.\r
- """\r
- def __init__(self, **kwargs):\r
- super(PathProperty, self).__init__(**kwargs)\r
- self.type = 'path'\r
-\r
-\r
-class PropertyPanel(Panel, wx.grid.Grid):\r
- """UI to view/set config values and command argsuments.\r
- """\r
- def __init__(self, callbacks=None, **kwargs):\r
- super(PropertyPanel, self).__init__(\r
- name='property editor', 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_motion)\r
-\r
- def _on_motion(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 or ''\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 == property.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
- r = property.get_renderer()\r
- if r != None:\r
- self.SetCellRenderer(row=row, col=0, renderer=r)\r
- self.set_property(property.label, property.default)\r
-\r
- def remove_property(self, label):\r
- row,property = self._property_by_label(label)\r
- self._properties.pop(row)\r
- self.DeleteRows(pos=row)\r
-\r
- def clear(self):\r
- while(len(self._properties) > 0):\r
- self.remove_property(self._properties[-1].label)\r
-\r
- def set_property(self, label, value):\r
- row,property = self._property_by_label(label)\r
- self.SetCellValue(row=row, col=0, s=property.string_for_value(value))\r
-\r
- def get_property(self, label):\r
- row,property = self._property_by_label(label)\r
- string = self.GetCellValue(row=row, col=0)\r
- return property.value_for_string(string)\r
-\r
- def get_values(self):\r
- return dict([(p.label, self.get_property(p.label))\r
- for p in self._properties])\r
-\r
- def _property_by_label(self, label):\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
- return (row, property)\r
+# Copyright (C) 2010 Massimo Sandal <devicerandom@gmail.com>
+# 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.
+
+wxPropertyGrid is `included in wxPython >= 2.9.1 <included>`_. Until
+then, we'll avoid it because of the *nix build problems.
+
+This module hacks together a workaround to be used until 2.9.1 is
+widely installed (or at least released ;).
+
+.. _included:
+ http://wxpropgrid.sourceforge.net/cgi-bin/index?page=download
+"""
+
+import wx.grid
+
+from . import Panel
+from ....plugin import argument_to_setting
+from ....util.convert import ANALOGS, to_string, from_string
+
+
+def props_from_argument(argument, curves=None, playlists=None):
+ """Convert a :class:`~hooke.command.Argument` to a list of
+ :class:`Property`\s.
+ """
+ type = argument.type
+ if type in ['driver']: # intentionally not handled (yet)
+ return None
+ if argument.count == -1:
+ raise NotImplementedError(argument) # TODO: maybe just set count to 1?
+ kwargs = {
+ #'label':argument.name,
+ 'default':argument.default,
+ 'help':argument.help(),
+ }
+ type = ANALOGS.get(type, type) # type consolidation
+ # type handling
+ if type in ['string', 'bool', 'int', 'float', 'path']:
+ _class = globals()['%sProperty' % type.capitalize()]
+ elif type in ['curve', 'playlist']:
+ if type == 'curve':
+ choices = curves # extracted from the current playlist
+ else:
+ choices = playlists
+ properties = []
+ _class = ChoiceProperty
+ kwargs['choices'] = choices
+ else:
+ raise NotImplementedError(argument.type)
+ if argument.count == 1:
+ labels = [argument.name]
+ else:
+ labels = ['%s %d' % (argument.name, i) for i in range(argument.count)]
+ return [(label, _class(label=label, **kwargs)) for label in labels]
+
+
+def props_from_setting(setting):
+ """Convert a :class:`~hooke.config.Setting` to a list of
+ :class:`Property`\s.
+ """
+ # TODO: move props_from_argument code here and use
+ # argument_to_setting there.
+ raise NotImplementedError()
+
+
+class Property (object):
+ def __init__(self, type, label, default, help=None):
+ self.type = type
+ self.label = label
+ self.default = default
+ self.help = help
+
+ def get_editor(self):
+ """Return a suitable grid editor.
+ """
+ raise NotImplementedError()
+
+ def get_renderer(self):
+ """Return a suitable grid renderer.
+
+ Returns `None` if no special renderer is required.
+ """
+ return None
+
+ def string_for_value(self, value):
+ """Return a string representation of `value` for loading the table.
+ """
+ return to_string(value, 'string')
+
+ def value_for_string(self, string):
+ """Return the value represented by `string`.
+ """
+ return from_string(string, 'string')
+
+
+class StringProperty (Property):
+ def __init__(self, **kwargs):
+ assert 'type' not in kwargs, kwargs
+ if 'default' not in kwargs:
+ kwargs['default'] = 0
+ super(StringProperty, self).__init__(type='string', **kwargs)
+
+ def get_editor(self):
+ return wx.grid.GridCellTextEditor()
+
+ def get_renderer(self):
+ return wx.grid.GridCellStringRenderer()
+
+
+class BoolProperty (Property):
+ """A boolean property.
+
+ Notes
+ -----
+ Unfortunately, changing a boolean property takes two clicks:
+
+ 1) create the editor
+ 2) change the value
+
+ There are `ways around this`_, but it's not pretty.
+
+ .. _ways around this:
+ http://wiki.wxpython.org/Change%20wxGrid%20CheckBox%20with%20one%20click
+ """
+ def __init__(self, **kwargs):
+ assert 'type' not in kwargs, kwargs
+ if 'default' not in kwargs:
+ kwargs['default'] = True
+ super(BoolProperty, self).__init__(type='bool', **kwargs)
+
+ def get_editor(self):
+ return wx.grid.GridCellBoolEditor()
+
+ def get_renderer(self):
+ return wx.grid.GridCellBoolRenderer()
+
+ def string_for_value(self, value):
+ if value == True:
+ return '1'
+ return ''
+
+ def value_for_string(self, string):
+ return string == '1'
+
+
+class IntProperty (Property):
+ def __init__(self, **kwargs):
+ assert 'type' not in kwargs, kwargs
+ if 'default' not in kwargs:
+ kwargs['default'] = 0
+ super(IntProperty, self).__init__(type='int', **kwargs)
+
+ def get_editor(self):
+ return wx.grid.GridCellNumberEditor()
+
+ def get_renderer(self):
+ return wx.grid.GridCellNumberRenderer()
+
+ def value_for_string(self, string):
+ return from_string(string, 'int')
+
+
+class FloatProperty (Property):
+ def __init__(self, **kwargs):
+ assert 'type' not in kwargs, kwargs
+ if 'default' not in kwargs:
+ kwargs['default'] = 0.0
+ super(FloatProperty, self).__init__(type='float', **kwargs)
+
+ def get_editor(self):
+ return wx.grid.GridCellFloatEditor()
+
+ def get_renderer(self):
+ return wx.grid.GridCellFloatRenderer()
+
+ def value_for_string(self, string):
+ return from_string(string, 'float')
+
+
+class ChoiceProperty (Property):
+ def __init__(self, choices, **kwargs):
+ assert 'type' not in kwargs, kwargs
+ if 'default' in kwargs:
+ if kwargs['default'] not in choices:
+ choices.insert(0, kwargs['default'])
+ else:
+ kwargs['default'] = choices[0]
+ super(ChoiceProperty, self).__init__(type='choice', **kwargs)
+ self._choices = choices
+
+ def get_editor(self):
+ choices = [self.string_for_value(c) for c in self._choices]
+ return wx.grid.GridCellChoiceEditor(choices=choices)
+
+ def get_renderer(self):
+ return None
+ #return wx.grid.GridCellChoiceRenderer()
+
+ def string_for_value(self, value):
+ if hasattr(value, 'name'):
+ return value.name
+ return str(value)
+
+ def value_for_string(self, string):
+ for choice in self._choices:
+ if self.string_for_value(choice) == string:
+ return choice
+ raise ValueError(string)
+
+
+class PathProperty (StringProperty):
+ """Simple file or path property.
+
+ Currently there isn't a fancy file-picker popup. Perhaps in the
+ future.
+ """
+ def __init__(self, **kwargs):
+ super(PathProperty, self).__init__(**kwargs)
+ self.type = 'path'
+
+
+class PropertyPanel(Panel, wx.grid.Grid):
+ """UI to view/set config values and command argsuments.
+ """
+ def __init__(self, callbacks=None, **kwargs):
+ super(PropertyPanel, self).__init__(
+ name='property editor', callbacks=callbacks, **kwargs)
+ self._properties = []
+
+ self.CreateGrid(numRows=0, numCols=1)
+ self.SetColLabelValue(0, 'value')
+
+ self._last_tooltip = None
+ self.GetGridWindow().Bind(wx.EVT_MOTION, self._on_motion)
+
+ def _on_motion(self, event):
+ """Enable tooltips.
+ """
+ x,y = self.CalcUnscrolledPosition(event.GetPosition())
+ row,col = self.XYToCell(x, y)
+ if col == -1 or row == -1:
+ msg = ''
+ else:
+ msg = self._properties[row].help or ''
+ if msg != self._last_tooltip:
+ self._last_tooltip = msg
+ event.GetEventObject().SetToolTipString(msg)
+
+ def append_property(self, property):
+ if len([p for p in self._properties if p.label == property.label]) > 0:
+ raise ValueError(property) # property.label collision
+ self._properties.append(property)
+ row = len(self._properties) - 1
+ self.AppendRows(numRows=1)
+ self.SetRowLabelValue(row, property.label)
+ self.SetCellEditor(row=row, col=0, editor=property.get_editor())
+ r = property.get_renderer()
+ if r != None:
+ self.SetCellRenderer(row=row, col=0, renderer=r)
+ self.set_property(property.label, property.default)
+
+ def remove_property(self, label):
+ row,property = self._property_by_label(label)
+ self._properties.pop(row)
+ self.DeleteRows(pos=row)
+
+ def clear(self):
+ while(len(self._properties) > 0):
+ self.remove_property(self._properties[-1].label)
+
+ def set_property(self, label, value):
+ row,property = self._property_by_label(label)
+ self.SetCellValue(row=row, col=0, s=property.string_for_value(value))
+
+ def get_property(self, label):
+ row,property = self._property_by_label(label)
+ string = self.GetCellValue(row=row, col=0)
+ return property.value_for_string(string)
+
+ def get_values(self):
+ return dict([(p.label, self.get_property(p.label))
+ for p in self._properties])
+
+ def _property_by_label(self, label):
+ props = [(i,p) for i,p in enumerate(self._properties)
+ if p.label == label]
+ assert len(props) == 1, props
+ row,property = props[0]
+ return (row, property)