Source code for director.propertyset

from director import callbacks
from director.fieldcontainer import FieldContainer
from director.timercallback import TimerCallback

import re
import numpy as np
from collections import OrderedDict


[docs]def cleanPropertyName(s): """ Generate a valid python property name by replacing all non-alphanumeric characters with underscores and adding an initial underscore if the first character is a digit """ return re.sub(r'\W|^(?=\d)','_',s).lower() # \W matches non-alphanumeric, ^(?=\d) matches the first position if followed by a digit
[docs]class PropertyAttributes(FieldContainer): def __init__(self, **kwargs): self._add_fields( decimals = 5, minimum = -1e4, maximum = 1e4, singleStep = 1, hidden = False, enumNames = None, readOnly = False, ) self._set_fields(**kwargs)
from PythonQt import QtGui
[docs]def fromQColor(propertyName, propertyValue): if isinstance(propertyValue, QtGui.QColor): return [propertyValue.red()/255.0, propertyValue.green()/255.0, propertyValue.blue()/255.0] else: return propertyValue
[docs]def toQProperty(propertyName, propertyValue): if 'color' in propertyName.lower() and isinstance(propertyValue, (list, tuple)) and len(propertyValue) == 3: return QtGui.QColor(propertyValue[0]*255.0, propertyValue[1]*255.0, propertyValue[2]*255.0) elif isinstance(propertyValue, np.float): return float(propertyValue) elif isinstance(propertyValue, (list, tuple, np.ndarray)) and len(propertyValue) and isinstance(propertyValue[0], np.float): return [float(x) for x in propertyValue] else: return propertyValue
[docs]class PropertySet(object): PROPERTY_CHANGED_SIGNAL = 'PROPERTY_CHANGED_SIGNAL' PROPERTY_ADDED_SIGNAL = 'PROPERTY_ADDED_SIGNAL' PROPERTY_ATTRIBUTE_CHANGED_SIGNAL = 'PROPERTY_ATTRIBUTE_CHANGED_SIGNAL' def __getstate__(self): d = dict(_properties=self._properties, _attributes=self._attributes) return d def __setstate__(self, state): self.__init__() attrs = state['_attributes'] for propName, propValue in state['_properties'].iteritems(): self.addProperty(propName, propValue, attributes=attrs.get(propName)) def __init__(self): self.callbacks = callbacks.CallbackRegistry([self.PROPERTY_CHANGED_SIGNAL, self.PROPERTY_ADDED_SIGNAL, self.PROPERTY_ATTRIBUTE_CHANGED_SIGNAL]) self._properties = OrderedDict() self._attributes = {} self._alternateNames = {}
[docs] def propertyNames(self): return self._properties.keys()
[docs] def hasProperty(self, propertyName): return propertyName in self._properties
[docs] def assertProperty(self, propertyName): assert self.hasProperty(propertyName), "Missing property: {:s}".format(propertyName)
[docs] def connectPropertyChanged(self, func): return self.callbacks.connect(self.PROPERTY_CHANGED_SIGNAL, func)
[docs] def disconnectPropertyChanged(self, callbackId): self.callbacks.disconnect(callbackId)
[docs] def connectPropertyAdded(self, func): return self.callbacks.connect(self.PROPERTY_ADDED_SIGNAL, func)
[docs] def disconnectPropertyAdded(self, callbackId): self.callbacks.disconnect(callbackId)
[docs] def connectPropertyAttributeChanged(self, func): return self.callbacks.connect(self.PROPERTY_ATTRIBUTE_CHANGED_SIGNAL, func)
[docs] def disconnectPropertyAttributeChanged(self, callbackId): self.callbacks.disconnect(callbackId)
[docs] def getProperty(self, propertyName): self.assertProperty(propertyName) return self._properties[propertyName]
[docs] def getPropertyEnumValue(self, propertyName): self.assertProperty(propertyName) return self._attributes[propertyName].enumNames[self._properties[propertyName]]
[docs] def removeProperty(self, propertyName): assert self.hasProperty(propertyName) alternateName = cleanPropertyName(propertyName) del self._alternateNames[alternateName] del self._properties[propertyName] del self._attributes[propertyName]
[docs] def addProperty(self, propertyName, propertyValue, attributes=None): alternateName = cleanPropertyName(propertyName) if propertyName not in self._properties and alternateName in self._alternateNames: raise ValueError('Adding this property would conflict with a different existing property with alternate name {:s}'.format(alternateName)) propertyValue = fromQColor(propertyName, propertyValue) self._alternateNames[alternateName] = propertyName self._properties[propertyName] = propertyValue self._attributes[propertyName] = attributes or PropertyAttributes() self.callbacks.process(self.PROPERTY_ADDED_SIGNAL, self, propertyName)
[docs] def setPropertyIndex(self, propertyName, newIndex): assert self.hasProperty(propertyName) currentIndex = self._properties.keys().index(propertyName) inds = range(len(self._properties)) inds.remove(currentIndex) inds.insert(newIndex, currentIndex) items = self._properties.items() self._properties = OrderedDict([items[i] for i in inds])
[docs] def setProperty(self, propertyName, propertyValue): self.assertProperty(propertyName) propertyValue = fromQColor(propertyName, propertyValue) names = self.getPropertyAttribute(propertyName, 'enumNames') if names and type(propertyValue) != int: propertyValue = names.index(propertyValue) self.oldPropertyValue = (propertyName, self.getProperty(propertyName)) self._properties[propertyName] = propertyValue self.oldPropertyValue = None self.callbacks.process(self.PROPERTY_CHANGED_SIGNAL, self, propertyName)
[docs] def getPropertyAttribute(self, propertyName, propertyAttribute): self.assertProperty(propertyName) return getattr(self._attributes[propertyName], propertyAttribute)
[docs] def setPropertyAttribute(self, propertyName, propertyAttribute, value): self.assertProperty(propertyName) attributes = self._attributes[propertyName] assert hasattr(attributes, propertyAttribute), "Missing attribute: {:s}".format(propertyAttribute) setattr(attributes, propertyAttribute, value) self.callbacks.process(self.PROPERTY_ATTRIBUTE_CHANGED_SIGNAL, self, propertyName, propertyAttribute)
def __getattribute__(self, name): try: alternateNames = object.__getattribute__(self, '_alternateNames') if name in alternateNames: return object.__getattribute__(self, 'getProperty')(alternateNames[name]) else: raise AttributeError() except AttributeError: return object.__getattribute__(self, name)
[docs]class PropertyPanelHelper(object): @staticmethod
[docs] def addPropertiesToPanel(properties, panel, propertyNamesToAdd = None): for propertyName in properties.propertyNames(): value = properties.getProperty(propertyName) attributes = properties._attributes[propertyName] if value is not None and not attributes.hidden: addThisProperty = True if (propertyNamesToAdd is not None): if (propertyName not in propertyNamesToAdd): addThisProperty = False if addThisProperty: PropertyPanelHelper._addProperty(panel, propertyName, attributes, value)
@staticmethod
[docs] def onPropertyValueChanged(panel, properties, propertyName): prop = panel.getProperty(propertyName) if prop is not None: propertyValue = properties.getProperty(propertyName) propertyValue = toQProperty(propertyName, propertyValue) if isinstance(propertyValue, list): for i, subValue in enumerate(propertyValue): panel.getSubProperty(prop, i).setValue(subValue) groupName = PropertyPanelHelper.getPropertyGroupName(propertyName, propertyValue) prop.setPropertyName(groupName) else: prop.setValue(propertyValue)
@staticmethod
[docs] def setPropertyFromPanel(prop, propertiesPanel, propertySet): if prop.isSubProperty(): if not propertiesPanel.getParentProperty(prop): return propertyIndex = propertiesPanel.getSubPropertyIndex(prop) propertyName = prop.propertyName() propertyName = propertyName[:propertyName.index('[')] propertyValue = propertySet.getProperty(propertyName) propertyValue = list(propertyValue) propertyValue[propertyIndex] = prop.value() propertySet.setProperty(propertyName, propertyValue) groupName = PropertyPanelHelper.getPropertyGroupName(propertyName, propertyValue) propertiesPanel.getParentProperty(prop).setPropertyName(groupName) else: propertyName = prop.propertyName() propertyValue = prop.value() propertyValue = fromQColor(propertyName, propertyValue) propertySet.setProperty(propertyName, propertyValue)
@staticmethod def _setPropertyAttributes(prop, attributes): prop.setAttribute('decimals', attributes.decimals) prop.setAttribute('minimum', attributes.minimum) prop.setAttribute('maximum', attributes.maximum) prop.setAttribute('singleStep', attributes.singleStep) if attributes.enumNames: prop.setAttribute('enumNames', attributes.enumNames) @staticmethod
[docs] def getPropertyGroupName(name, value): return '%s [%s]' % (name, ', '.join(['%.2f' % v if isinstance(v, float) else str(v) for v in value]))
@staticmethod def _addProperty(panel, name, attributes, value): value = toQProperty(name, value) if isinstance(value, list): groupName = PropertyPanelHelper.getPropertyGroupName(name, value) groupProp = panel.addGroup(name, groupName) for v in value: p = panel.addSubProperty(name, v, groupProp) PropertyPanelHelper._setPropertyAttributes(p, attributes) return groupProp elif attributes.enumNames: p = panel.addEnumProperty(name, value) PropertyPanelHelper._setPropertyAttributes(p, attributes) p.setValue(value) return p else: p = panel.addProperty(name, value) PropertyPanelHelper._setPropertyAttributes(p, attributes) return p
[docs]class PropertyPanelConnector(object): def __init__(self, propertySet, propertiesPanel, propertyNamesToAdd=None): self.propertySet = propertySet self.propertyNamesToAdd = propertyNamesToAdd self.propertiesPanel = propertiesPanel self.propertySet.connectPropertyAdded(self._onPropertyAdded) self.propertySet.connectPropertyChanged(self._onPropertyChanged) self.propertySet.connectPropertyAttributeChanged(self._onPropertyAttributeChanged) self.propertiesPanel.connect('propertyValueChanged(QtVariantProperty*)', self._onPanelPropertyChanged) self.timer = TimerCallback() self.timer.callback = self._rebuildNow self._blockSignals = True PropertyPanelHelper.addPropertiesToPanel(self.propertySet, self.propertiesPanel, self.propertyNamesToAdd) self._blockSignals = False
[docs] def cleanup(self): self.propertiesPanel.disconnect('propertyValueChanged(QtVariantProperty*)', self._onPanelPropertyChanged)
def _rebuild(self): if not self.timer.singleShotTimer.isActive(): self.timer.singleShot(0) def _rebuildNow(self): self._blockSignals = True self.propertiesPanel.clear() PropertyPanelHelper.addPropertiesToPanel(self.propertySet, self.propertiesPanel) self._blockSignals = False def _onPropertyAdded(self, propertySet, propertyName): self._rebuild() def _onPropertyAttributeChanged(self, propertySet, propertyName, propertyAttribute): self._rebuild() def _onPropertyChanged(self, propertySet, propertyName): self._blockSignals = True PropertyPanelHelper.onPropertyValueChanged(self.propertiesPanel, propertySet, propertyName) self._blockSignals = False def _onPanelPropertyChanged(self, panelProperty): if not self._blockSignals: PropertyPanelHelper.setPropertyFromPanel(panelProperty, self.propertiesPanel, self.propertySet)