def inspectDisplay(self, display): self.display = display # Get the subset of visibles that are paths. self.paths = ObjectList() self.visibles = display.selection() for visible in self.visibles: if visible.isPath(): self.paths.append(visible) if len(self.paths) == 0: self._flowToLabel.Hide() self._flowToCheckBox.Hide() self._sizer.Hide(self._flowFromBoxSizer, True) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowColor'), self.display) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowSpread'), self.display) else: self._flowToLabel.Show() self._flowToCheckBox.Show() self._sizer.Show(self._flowFromBoxSizer, True) for path in self.paths: for attributeName in [ 'flowTo', 'flowToColor', 'flowToSpread', 'flowFrom', 'flowFromColor', 'flowFromSpread' ]: dispatcher.connect(self.refreshGUI, ('set', attributeName), visible) self.refreshGUI()
def inspectDisplay(self, display): # Object inspectors are supposed to work purely at the network/biological layer so they don't need to know what the display is. self.display = display self.objects = ObjectList() self.updatingObjects = False for visible in display.selection(): if visible.client.__class__ == self.__class__.objectClass(): self.objects.append(visible.client) dispatcher.connect(self.nameChanged, ('set', 'name'), visible.client) for attributePath in self.inspectedAttributes(): if '.' in attributePath: (subObject, attribute) = attributePath.split('.') object = getattr(visible.client, subObject) else: object = visible.client attribute = attributePath dispatcher.connect(self.inspectedAttributeChanged, ('set', attribute), object) # Set the icon image = self.objects[0].__class__.image() if image == None: pass else: scaledImage = image.Rescale(32, 32, wx.IMAGE_QUALITY_HIGH) self.iconField.SetBitmap(wx.BitmapFromImage(scaledImage)) self.populateNameField() self.populateObjectSizer() self._window.Layout()
def willBeClosed(self): for visible in self.visibles: for attributeName in [ 'color', 'opacity', 'shape', 'texture', 'weight' ]: dispatcher.disconnect(self.refreshGUI, ('set', attributeName), visible) self.visibles = ObjectList()
def inspectDisplay(self, display): self.display = display # Get the subset of visibles that are paths. self.paths = ObjectList() self.visibles = display.selection() for visible in self.visibles: if visible.isPath(): self.paths.append(visible) if len(self.paths) == 0: self._flowToLabel.Hide() self._flowToCheckBox.Hide() self._sizer.Hide(self._flowFromBoxSizer, True) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowColor'), self.display) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowSpread'), self.display) else: self._flowToLabel.Show() self._flowToCheckBox.Show() self._sizer.Show(self._flowFromBoxSizer, True) for path in self.paths: for attributeName in ['flowTo', 'flowToColor', 'flowToSpread', 'flowFrom', 'flowFromColor', 'flowFromSpread']: dispatcher.connect(self.refreshGUI, ('set', attributeName), visible) self.refreshGUI()
class ObjectInspector(Inspector): @classmethod def name(cls): return cls.objectClass().displayName() @classmethod def shouldBeRegistered(cls): return cls != ObjectInspector.__class__ @classmethod def objectClass(cls): return Object @classmethod def inspectedAttributes(cls): return [] @classmethod def bitmap(cls, size = 32): image = cls.objectClass().image() if image == None: return Inspector.bitmap(cls) else: scaledImage = image.Rescale(size, size, wx.IMAGE_QUALITY_HIGH) return wx.BitmapFromImage(scaledImage) @classmethod def canInspectDisplay(cls, display): # This inspector can be used if there is at least one object of this type selected. if display: for visible in display.selection(): if visible.client.__class__ == cls.objectClass(): return True return False def window(self, parentWindow=None): if not hasattr(self, '_window'): self._window = wx.Window(parentWindow, wx.ID_ANY) self.iconField = wx.StaticBitmap(self._window, wx.ID_ANY) self.iconField.SetMinSize(wx.Size(32, 32)) self.iconField.SetMaxSize(wx.Size(32, 32)) self.titleField = wx.StaticText(self._window, wx.ID_ANY, "") headerBox = wx.BoxSizer(wx.HORIZONTAL) headerBox.Add(self.iconField, 0, wx.EXPAND) headerBox.AddSpacer(8) headerBox.Add(self.titleField, 1, wx.EXPAND) objectSizer = self.objectSizer(self._window) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(headerBox, 0, wx.EXPAND | wx.ALL, 5) if objectSizer is not None: mainSizer.Add(objectSizer, 1, wx.ALL, 5) self._window.SetSizer(mainSizer) return self._window def inspectDisplay(self, display): # Object inspectors are supposed to work purely at the network/biological layer so they don't need to know what the display is. self.display = display self.objects = ObjectList() self.updatingObjects = False for visible in display.selection(): if visible.client.__class__ == self.__class__.objectClass(): self.objects.append(visible.client) dispatcher.connect(self.nameChanged, ('set', 'name'), visible.client) for attributePath in self.inspectedAttributes(): if '.' in attributePath: (subObject, attribute) = attributePath.split('.') object = getattr(visible.client, subObject) else: object = visible.client attribute = attributePath dispatcher.connect(self.inspectedAttributeChanged, ('set', attribute), object) # Set the icon image = self.objects[0].__class__.image() if image == None: pass else: scaledImage = image.Rescale(32, 32, wx.IMAGE_QUALITY_HIGH) self.iconField.SetBitmap(wx.BitmapFromImage(scaledImage)) self.populateNameField() self.populateObjectSizer() self._window.Layout() def objectSizer(self, parentWindow): return None def populateNameField(self): if self.objects.haveEqualAttr('name'): self.titleField.SetLabel(self.objects[0].name or ('<' + gettext('unnamed ') + self.__class__.objectClass().displayName().lower() + '>')) else: self.titleField.SetLabel(gettext('Multiple names')) def populateObjectSizer(self, attribute = None): pass def nameChanged(self, signal, sender, event=None, value=None, **arguments): self.populateNameField() def inspectedAttributeChanged(self, signal, sender, **arguments): if not self.updatingObjects: self.populateObjectSizer(signal[1]) def selectObject(self, object): self.display.selectObject(object)
class PathInspector(Inspector): @classmethod def name(cls): return gettext('Path') @classmethod def canInspectDisplay(cls, display): if display: for visible in display.selectedVisibles: if visible.isPath(): return True return display and not any(display.selectedVisibles) def window(self, parentWindow=None): if not hasattr(self, '_window'): self._window = wx.Window(parentWindow, wx.ID_ANY) self._sizer = wx.BoxSizer(wx.VERTICAL) self._flowToBoxSizer = wx.StaticBoxSizer(wx.StaticBox(self._window, wx.ID_ANY, gettext('Flow to')), wx.HORIZONTAL) flowToGridSizer = wx.FlexGridSizer(6, 2, 8, 0) flowToGridSizer.SetFlexibleDirection(wx.HORIZONTAL) flowToGridSizer.AddGrowableCol(1, 1) self._flowToCheckBox = wx.CheckBox(self._window, wx.ID_ANY, '', style=wx.CHK_3STATE) self._window.Bind(wx.EVT_CHECKBOX, self.onSetAnimateFlowTo, self._flowToCheckBox) self._flowToLabel = wx.StaticText(self._window, wx.ID_ANY, gettext('Animate:')) flowToGridSizer.Add(self._flowToLabel, 0) flowToGridSizer.Add(self._flowToCheckBox, 1) self._flowToColorPicker = wx.lib.colourselect.ColourSelect(self._window, wx.ID_ANY) self._window.Bind(wx.lib.colourselect.EVT_COLOURSELECT, self.onSetFlowToColor, self._flowToColorPicker) flowToGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Color:')), 0) flowToGridSizer.Add(self._flowToColorPicker, 1) self._flowToSpacingSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowToSpacingSlider.Bind(wx.EVT_SCROLL, self.onSetFlowToSpacing, self._flowToSpacingSlider) flowToGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Spacing:')), 0) flowToGridSizer.Add(self._flowToSpacingSlider, 1, wx.EXPAND) self._flowToSpeedSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowToSpeedSlider.Bind(wx.EVT_SCROLL, self.onSetFlowToSpeed, self._flowToSpeedSlider) flowToGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Speed:')), 0) flowToGridSizer.Add(self._flowToSpeedSlider, 1, wx.EXPAND) self._flowToSpreadSlider = wx.Slider(self._window, wx.ID_ANY, 25, 0, 100) self._flowToSpreadSlider.Bind(wx.EVT_SCROLL, self.onSetFlowToSpread, self._flowToSpreadSlider) flowToGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Spread:')), 0) flowToGridSizer.Add(self._flowToSpreadSlider, 1, wx.EXPAND) self._flowToBoxSizer.Add(flowToGridSizer, 1, wx.EXPAND) self._sizer.Add(self._flowToBoxSizer, 1, wx.EXPAND | wx.ALL, 5) self._flowFromBoxSizer = wx.StaticBoxSizer(wx.StaticBox(self._window, wx.ID_ANY, gettext('Flow to')), wx.HORIZONTAL) flowFromGridSizer = wx.FlexGridSizer(6, 2, 8, 0) flowFromGridSizer.SetFlexibleDirection(wx.HORIZONTAL) flowFromGridSizer.AddGrowableCol(1, 1) self._flowFromCheckBox = wx.CheckBox(self._window, wx.ID_ANY, '', style=wx.CHK_3STATE) self._window.Bind(wx.EVT_CHECKBOX, self.onSetAnimateFlowFrom, self._flowFromCheckBox) flowFromGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Animate:')), 0) flowFromGridSizer.Add(self._flowFromCheckBox, 1) self._flowFromColorPicker = wx.lib.colourselect.ColourSelect(self._window, wx.ID_ANY) self._window.Bind(wx.lib.colourselect.EVT_COLOURSELECT, self.onSetFlowFromColor, self._flowFromColorPicker) flowFromGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Color:')), 0) flowFromGridSizer.Add(self._flowFromColorPicker, 1) self._flowFromSpacingSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowFromSpacingSlider.Bind(wx.EVT_SCROLL, self.onSetFlowFromSpacing, self._flowFromSpacingSlider) flowFromGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Spacing:')), 0) flowFromGridSizer.Add(self._flowFromSpacingSlider, 1, wx.EXPAND) self._flowFromSpeedSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowFromSpeedSlider.Bind(wx.EVT_SCROLL, self.onSetFlowFromSpeed, self._flowFromSpeedSlider) flowFromGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Speed:')), 0) flowFromGridSizer.Add(self._flowFromSpeedSlider, 1, wx.EXPAND) self._flowFromSpreadSlider = wx.Slider(self._window, wx.ID_ANY, 25, 0, 100) self._flowFromSpreadSlider.Bind(wx.EVT_SCROLL, self.onSetFlowFromSpread, self._flowFromSpreadSlider) flowFromGridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Spread:')), 0) flowFromGridSizer.Add(self._flowFromSpreadSlider, 1, wx.EXPAND) self._flowFromBoxSizer.Add(flowFromGridSizer, 1, wx.EXPAND) self._sizer.Add(self._flowFromBoxSizer, 1, wx.EXPAND | wx.ALL, 5) self._window.SetSizer(self._sizer) return self._window def inspectDisplay(self, display): self.display = display # Get the subset of visibles that are paths. self.paths = ObjectList() self.visibles = display.selection() for visible in self.visibles: if visible.isPath(): self.paths.append(visible) if len(self.paths) == 0: self._flowToLabel.Hide() self._flowToCheckBox.Hide() self._sizer.Hide(self._flowFromBoxSizer, True) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowColor'), self.display) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowSpread'), self.display) else: self._flowToLabel.Show() self._flowToCheckBox.Show() self._sizer.Show(self._flowFromBoxSizer, True) for path in self.paths: for attributeName in ['flowTo', 'flowToColor', 'flowToSpread', 'flowFrom', 'flowFromColor', 'flowFromSpread']: dispatcher.connect(self.refreshGUI, ('set', attributeName), visible) self.refreshGUI() def reasonableSpacingOrSpeed(self): # Compute some reasonable spacing/speed based on the bounding box of the visibles in the display. self.display.computeVisiblesBound() return sqrt(self.display.visiblesSize[0] ** 2 + self.display.visiblesSize[1] ** 2 + self.display.visiblesSize[2] ** 2) / 20.0 def refreshGUI(self, signal = ('set', None), **arguments): updatedAttribute = signal[1] usingDefault = len(self.paths) == 0 # The spacing and speed sliders are mapped logarithmically so very small and very large values can be used. # The scale is set so that a reasonable spacing and speed are used when the slider is at the midpoint. # Unfortunately what is "reasonable" is dependent on the bounding box of the whole display which can change at any time. expBase = e ** (log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) if usingDefault: self._flowToBoxSizer.GetStaticBox().SetLabel(gettext('Default flow appearance')) else: if len(self.paths) == 1: (startPoint, endPoint) = self.paths[0].pathEndPoints() flowToName = gettext('anonymous object') if endPoint.client == None else endPoint.client.name or gettext('<unnamed %s>') % ( endPoint.client.__class__.displayName()) flowFromName = gettext('anonymous object') if startPoint.client == None else startPoint.client.name or gettext('<unnamed %s>') % ( startPoint.client.__class__.displayName()) else: flowToName = gettext('multiple objects') flowFromName = gettext('multiple objects') self._flowToBoxSizer.GetStaticBox().SetLabel(gettext('Flow to %s') % (flowToName)) self._flowFromBoxSizer.GetStaticBox().SetLabel(gettext('Flow to %s') % (flowFromName)) if updatedAttribute is None or updatedAttribute == 'flowTo': if usingDefault: self._flowToCheckBox.Set3StateValue(wx.CHK_UNCHECKED) self._flowToCheckBox.Disable() else: if self.paths.haveEqualAttr('flowTo'): if self.paths[0].flowTo(): self._flowToCheckBox.Set3StateValue(wx.CHK_CHECKED) else: self._flowToCheckBox.Set3StateValue(wx.CHK_UNCHECKED) else: self._flowToCheckBox.Set3StateValue(wx.CHK_UNDETERMINED) self._flowToCheckBox.Enable() if updatedAttribute is None or updatedAttribute == 'flowToColor' or updatedAttribute == 'defaultFlowColor': if usingDefault or self.paths.haveEqualAttr('flowToColor'): if usingDefault: red, green, blue, alpha = self.display.defaultFlowColor else: red, green, blue, alpha = self.paths[0].flowToColor() or self.display.defaultFlowColor self._flowToColorPicker.SetColour(wx.Colour(red * 255, green * 255, blue * 255, alpha * 255)) self._flowToColorPicker.SetLabel(gettext('')) else: self._flowToColorPicker.SetColour(wx.NamedColour('GRAY')) # TODO: be clever and pick the average color? self._flowToColorPicker.SetLabel(gettext('Multiple values')) if updatedAttribute is None or updatedAttribute == 'flowToSpacing' or updatedAttribute == 'defaultFlowSpacing': if usingDefault or self.paths.haveEqualAttr('flowToSpacing'): if usingDefault: spacing = self.display.defaultFlowSpacing else: spacing = self.paths[0].flowToSpacing() or self.display.defaultFlowSpacing self._flowToSpacingSlider.SetLabel('') self._flowToSpacingSlider.SetValue(log(spacing + 1.0, expBase)) else: self._flowToSpacingSlider.SetLabel(gettext('Multiple values')) self._flowToSpacingSlider.SetValue(0) if updatedAttribute is None or updatedAttribute == 'flowToSpeed' or updatedAttribute == 'defaultFlowSpeed': if usingDefault or self.paths.haveEqualAttr('flowToSpeed'): if usingDefault: speed = self.display.defaultFlowSpeed else: speed = self.paths[0].flowToSpeed() or self.display.defaultFlowSpeed self._flowToSpeedSlider.SetLabel('') self._flowToSpeedSlider.SetValue(log(speed + 1.0, expBase)) else: self._flowToSpeedSlider.SetLabel(gettext('Multiple values')) self._flowToSpeedSlider.SetValue(0) if updatedAttribute is None or updatedAttribute == 'flowToSpread' or updatedAttribute == 'defaultFlowSpread': if usingDefault or self.paths.haveEqualAttr('flowToSpread'): if usingDefault: spread = self.display.defaultFlowSpread else: spread = self.paths[0].flowToSpread() or self.display.defaultFlowSpread self._flowToSpreadSlider.SetLabel('') self._flowToSpreadSlider.SetValue(spread * 50.0) else: self._flowToSpreadSlider.SetLabel(gettext('Multiple values')) self._flowToSpreadSlider.SetValue(50) if (updatedAttribute is None or updatedAttribute == 'flowFrom') and not usingDefault: if self.paths.haveEqualAttr('flowFrom'): if self.paths[0].flowFrom(): self._flowFromCheckBox.Set3StateValue(wx.CHK_CHECKED) else: self._flowFromCheckBox.Set3StateValue(wx.CHK_UNCHECKED) else: self._flowFromCheckBox.Set3StateValue(wx.CHK_UNDETERMINED) self._flowFromCheckBox.Enable() if (updatedAttribute is None or updatedAttribute == 'flowFromColor') and not usingDefault: if usingDefault or self.paths.haveEqualAttr('flowFromColor'): if usingDefault: red, green, blue, alpha = self.display.defaultFlowColor else: red, green, blue, alpha = self.paths[0].flowFromColor() or self.display.defaultFlowColor self._flowFromColorPicker.SetColour(wx.Colour(red * 255, green * 255, blue * 255, alpha * 255)) self._flowFromColorPicker.SetLabel(gettext('')) else: self._flowFromColorPicker.SetColour(wx.NamedColour('GRAY')) # TODO: be clever and pick the average color? self._flowFromColorPicker.SetLabel(gettext('Multiple values')) if updatedAttribute is None or updatedAttribute == 'flowFromSpacing' or updatedAttribute == 'defaultFlowSpacing': if usingDefault or self.paths.haveEqualAttr('flowFromSpacing'): if usingDefault: spacing = self.display.defaultFlowSpacing else: spacing = self.paths[0].flowFromSpacing() or self.display.defaultFlowSpacing self._flowFromSpacingSlider.SetLabel('') self._flowFromSpacingSlider.SetValue(log(spacing + 1.0, expBase)) else: self._flowFromSpacingSlider.SetLabel(gettext('Multiple values')) self._flowFromSpacingSlider.SetValue(0) if updatedAttribute is None or updatedAttribute == 'flowFromSpeed' or updatedAttribute == 'defaultFlowSpeed': if usingDefault or self.paths.haveEqualAttr('flowFromSpeed'): if usingDefault: speed = self.display.defaultFlowSpeed else: speed = self.paths[0].flowFromSpeed() or self.display.defaultFlowSpeed self._flowFromSpeedSlider.SetLabel('') self._flowFromSpeedSlider.SetValue(log(speed + 1.0, expBase)) else: self._flowFromSpeedSlider.SetLabel(gettext('Multiple values')) self._flowFromSpeedSlider.SetValue(0) if (updatedAttribute is None or updatedAttribute == 'flowFromSpread') and not usingDefault: if usingDefault or self.paths.haveEqualAttr('flowFromSpread'): if usingDefault: spread = self.display.defaultFlowSpread else: spread = self.paths[0].flowFromSpread() or self.display.defaultFlowSpread self._flowFromSpreadSlider.SetLabel('') self._flowFromSpreadSlider.SetValue(spread * 50.0) else: self._flowFromSpreadSlider.SetLabel(gettext('Multiple values')) self._flowFromSpreadSlider.SetValue(50) self._window.Layout() def onSetAnimateFlowTo(self, event): newValue = self._flowToCheckBox.GetValue() for path in self.paths: path.setFlowTo(newValue == wx.CHK_CHECKED) def onSetFlowToColor(self, event): wxColor = self._flowToColorPicker.GetColour() newColor = (wxColor.Red() / 255.0, wxColor.Green() / 255.0, wxColor.Blue() / 255.0, wxColor.Alpha() / 255.0) if len(self.paths) == 0: self.display.setDefaultFlowColor(newColor) else: for path in self.paths: path.setFlowToColor(newColor) def onSetFlowToSpacing(self, event): expBase = e ** (log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpacing = expBase ** self._flowToSpacingSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpacing(newSpacing) else: for path in self.paths: path.setFlowToSpacing(newSpacing) wx.GetApp().Yield() def onSetFlowToSpeed(self, event): expBase = e ** (log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpeed = expBase ** self._flowToSpeedSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpeed(newSpeed) else: for path in self.paths: path.setFlowToSpeed(newSpeed) wx.GetApp().Yield() def onSetFlowToSpread(self, event): newSpread = self._flowToSpreadSlider.GetValue() / 50.0 if len(self.paths) == 0: self.display.setDefaultFlowSpread(newSpread) else: for path in self.paths: path.setFlowToSpread(newSpread) def onSetAnimateFlowFrom(self, event): newValue = self._flowFromCheckBox.GetValue() for path in self.paths: path.setFlowFrom(newValue == wx.CHK_CHECKED) def onSetFlowFromColor(self, event): wxColor = self._flowFromColorPicker.GetColour() newColor = (wxColor.Red() / 255.0, wxColor.Green() / 255.0, wxColor.Blue() / 255.0, wxColor.Alpha() / 255.0) for path in self.paths: path.setFlowFromColor(newColor) def onSetFlowFromSpacing(self, event): expBase = e ** (log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpacing = expBase ** self._flowFromSpacingSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpacing(newSpacing) else: for path in self.paths: path.setFlowFromSpacing(newSpacing) def onSetFlowFromSpeed(self, event): expBase = e ** (log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpeed = expBase ** self._flowFromSpeedSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpeed(newSpeed) else: for path in self.paths: path.setFlowFromSpeed(newSpeed) def onSetFlowFromSpread(self, event): newSpread = self._flowFromSpreadSlider.GetValue() / 50.0 for path in self.paths: path.setFlowFromSpread(newSpread)
class PathInspector(Inspector): @classmethod def name(cls): return gettext('Path') @classmethod def canInspectDisplay(cls, display): if display: for visible in display.selectedVisibles: if visible.isPath(): return True return display and not any(display.selectedVisibles) def window(self, parentWindow=None): if not hasattr(self, '_window'): self._window = wx.Window(parentWindow, wx.ID_ANY) self._sizer = wx.BoxSizer(wx.VERTICAL) self._flowToBoxSizer = wx.StaticBoxSizer( wx.StaticBox(self._window, wx.ID_ANY, gettext('Flow to')), wx.HORIZONTAL) flowToGridSizer = wx.FlexGridSizer(6, 2, 8, 0) flowToGridSizer.SetFlexibleDirection(wx.HORIZONTAL) flowToGridSizer.AddGrowableCol(1, 1) self._flowToCheckBox = wx.CheckBox(self._window, wx.ID_ANY, '', style=wx.CHK_3STATE) self._window.Bind(wx.EVT_CHECKBOX, self.onSetAnimateFlowTo, self._flowToCheckBox) self._flowToLabel = wx.StaticText(self._window, wx.ID_ANY, gettext('Animate:')) flowToGridSizer.Add(self._flowToLabel, 0) flowToGridSizer.Add(self._flowToCheckBox, 1) self._flowToColorPicker = wx.lib.colourselect.ColourSelect( self._window, wx.ID_ANY) self._window.Bind(wx.lib.colourselect.EVT_COLOURSELECT, self.onSetFlowToColor, self._flowToColorPicker) flowToGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Color:')), 0) flowToGridSizer.Add(self._flowToColorPicker, 1) self._flowToSpacingSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowToSpacingSlider.Bind(wx.EVT_SCROLL, self.onSetFlowToSpacing, self._flowToSpacingSlider) flowToGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Spacing:')), 0) flowToGridSizer.Add(self._flowToSpacingSlider, 1, wx.EXPAND) self._flowToSpeedSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowToSpeedSlider.Bind(wx.EVT_SCROLL, self.onSetFlowToSpeed, self._flowToSpeedSlider) flowToGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Speed:')), 0) flowToGridSizer.Add(self._flowToSpeedSlider, 1, wx.EXPAND) self._flowToSpreadSlider = wx.Slider(self._window, wx.ID_ANY, 25, 0, 100) self._flowToSpreadSlider.Bind(wx.EVT_SCROLL, self.onSetFlowToSpread, self._flowToSpreadSlider) flowToGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Spread:')), 0) flowToGridSizer.Add(self._flowToSpreadSlider, 1, wx.EXPAND) self._flowToBoxSizer.Add(flowToGridSizer, 1, wx.EXPAND) self._sizer.Add(self._flowToBoxSizer, 1, wx.EXPAND | wx.ALL, 5) self._flowFromBoxSizer = wx.StaticBoxSizer( wx.StaticBox(self._window, wx.ID_ANY, gettext('Flow to')), wx.HORIZONTAL) flowFromGridSizer = wx.FlexGridSizer(6, 2, 8, 0) flowFromGridSizer.SetFlexibleDirection(wx.HORIZONTAL) flowFromGridSizer.AddGrowableCol(1, 1) self._flowFromCheckBox = wx.CheckBox(self._window, wx.ID_ANY, '', style=wx.CHK_3STATE) self._window.Bind(wx.EVT_CHECKBOX, self.onSetAnimateFlowFrom, self._flowFromCheckBox) flowFromGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Animate:')), 0) flowFromGridSizer.Add(self._flowFromCheckBox, 1) self._flowFromColorPicker = wx.lib.colourselect.ColourSelect( self._window, wx.ID_ANY) self._window.Bind(wx.lib.colourselect.EVT_COLOURSELECT, self.onSetFlowFromColor, self._flowFromColorPicker) flowFromGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Color:')), 0) flowFromGridSizer.Add(self._flowFromColorPicker, 1) self._flowFromSpacingSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowFromSpacingSlider.Bind(wx.EVT_SCROLL, self.onSetFlowFromSpacing, self._flowFromSpacingSlider) flowFromGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Spacing:')), 0) flowFromGridSizer.Add(self._flowFromSpacingSlider, 1, wx.EXPAND) self._flowFromSpeedSlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 1000) self._flowFromSpeedSlider.Bind(wx.EVT_SCROLL, self.onSetFlowFromSpeed, self._flowFromSpeedSlider) flowFromGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Speed:')), 0) flowFromGridSizer.Add(self._flowFromSpeedSlider, 1, wx.EXPAND) self._flowFromSpreadSlider = wx.Slider(self._window, wx.ID_ANY, 25, 0, 100) self._flowFromSpreadSlider.Bind(wx.EVT_SCROLL, self.onSetFlowFromSpread, self._flowFromSpreadSlider) flowFromGridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Spread:')), 0) flowFromGridSizer.Add(self._flowFromSpreadSlider, 1, wx.EXPAND) self._flowFromBoxSizer.Add(flowFromGridSizer, 1, wx.EXPAND) self._sizer.Add(self._flowFromBoxSizer, 1, wx.EXPAND | wx.ALL, 5) self._window.SetSizer(self._sizer) return self._window def inspectDisplay(self, display): self.display = display # Get the subset of visibles that are paths. self.paths = ObjectList() self.visibles = display.selection() for visible in self.visibles: if visible.isPath(): self.paths.append(visible) if len(self.paths) == 0: self._flowToLabel.Hide() self._flowToCheckBox.Hide() self._sizer.Hide(self._flowFromBoxSizer, True) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowColor'), self.display) dispatcher.connect(self.refreshGUI, ('set', 'defaultFlowSpread'), self.display) else: self._flowToLabel.Show() self._flowToCheckBox.Show() self._sizer.Show(self._flowFromBoxSizer, True) for path in self.paths: for attributeName in [ 'flowTo', 'flowToColor', 'flowToSpread', 'flowFrom', 'flowFromColor', 'flowFromSpread' ]: dispatcher.connect(self.refreshGUI, ('set', attributeName), visible) self.refreshGUI() def reasonableSpacingOrSpeed(self): # Compute some reasonable spacing/speed based on the bounding box of the visibles in the display. self.display.computeVisiblesBound() return sqrt(self.display.visiblesSize[0]**2 + self.display.visiblesSize[1]**2 + self.display.visiblesSize[2]**2) / 20.0 def refreshGUI(self, signal=('set', None), **arguments): updatedAttribute = signal[1] usingDefault = len(self.paths) == 0 # The spacing and speed sliders are mapped logarithmically so very small and very large values can be used. # The scale is set so that a reasonable spacing and speed are used when the slider is at the midpoint. # Unfortunately what is "reasonable" is dependent on the bounding box of the whole display which can change at any time. expBase = e**(log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) if usingDefault: self._flowToBoxSizer.GetStaticBox().SetLabel( gettext('Default flow appearance')) else: if len(self.paths) == 1: (startPoint, endPoint) = self.paths[0].pathEndPoints() flowToName = gettext( 'anonymous object' ) if endPoint.client == None else endPoint.client.name or gettext( '<unnamed %s>') % (endPoint.client.__class__.displayName()) flowFromName = gettext( 'anonymous object' ) if startPoint.client == None else startPoint.client.name or gettext( '<unnamed %s>') % ( startPoint.client.__class__.displayName()) else: flowToName = gettext('multiple objects') flowFromName = gettext('multiple objects') self._flowToBoxSizer.GetStaticBox().SetLabel( gettext('Flow to %s') % (flowToName)) self._flowFromBoxSizer.GetStaticBox().SetLabel( gettext('Flow to %s') % (flowFromName)) if updatedAttribute is None or updatedAttribute == 'flowTo': if usingDefault: self._flowToCheckBox.Set3StateValue(wx.CHK_UNCHECKED) self._flowToCheckBox.Disable() else: if self.paths.haveEqualAttr('flowTo'): if self.paths[0].flowTo(): self._flowToCheckBox.Set3StateValue(wx.CHK_CHECKED) else: self._flowToCheckBox.Set3StateValue(wx.CHK_UNCHECKED) else: self._flowToCheckBox.Set3StateValue(wx.CHK_UNDETERMINED) self._flowToCheckBox.Enable() if updatedAttribute is None or updatedAttribute == 'flowToColor' or updatedAttribute == 'defaultFlowColor': if usingDefault or self.paths.haveEqualAttr('flowToColor'): if usingDefault: red, green, blue, alpha = self.display.defaultFlowColor else: red, green, blue, alpha = self.paths[0].flowToColor( ) or self.display.defaultFlowColor self._flowToColorPicker.SetColour( wx.Colour(red * 255, green * 255, blue * 255, alpha * 255)) self._flowToColorPicker.SetLabel(gettext('')) else: self._flowToColorPicker.SetColour(wx.NamedColour( 'GRAY')) # TODO: be clever and pick the average color? self._flowToColorPicker.SetLabel(gettext('Multiple values')) if updatedAttribute is None or updatedAttribute == 'flowToSpacing' or updatedAttribute == 'defaultFlowSpacing': if usingDefault or self.paths.haveEqualAttr('flowToSpacing'): if usingDefault: spacing = self.display.defaultFlowSpacing else: spacing = self.paths[0].flowToSpacing( ) or self.display.defaultFlowSpacing self._flowToSpacingSlider.SetLabel('') self._flowToSpacingSlider.SetValue(log(spacing + 1.0, expBase)) else: self._flowToSpacingSlider.SetLabel(gettext('Multiple values')) self._flowToSpacingSlider.SetValue(0) if updatedAttribute is None or updatedAttribute == 'flowToSpeed' or updatedAttribute == 'defaultFlowSpeed': if usingDefault or self.paths.haveEqualAttr('flowToSpeed'): if usingDefault: speed = self.display.defaultFlowSpeed else: speed = self.paths[0].flowToSpeed( ) or self.display.defaultFlowSpeed self._flowToSpeedSlider.SetLabel('') self._flowToSpeedSlider.SetValue(log(speed + 1.0, expBase)) else: self._flowToSpeedSlider.SetLabel(gettext('Multiple values')) self._flowToSpeedSlider.SetValue(0) if updatedAttribute is None or updatedAttribute == 'flowToSpread' or updatedAttribute == 'defaultFlowSpread': if usingDefault or self.paths.haveEqualAttr('flowToSpread'): if usingDefault: spread = self.display.defaultFlowSpread else: spread = self.paths[0].flowToSpread( ) or self.display.defaultFlowSpread self._flowToSpreadSlider.SetLabel('') self._flowToSpreadSlider.SetValue(spread * 50.0) else: self._flowToSpreadSlider.SetLabel(gettext('Multiple values')) self._flowToSpreadSlider.SetValue(50) if (updatedAttribute is None or updatedAttribute == 'flowFrom') and not usingDefault: if self.paths.haveEqualAttr('flowFrom'): if self.paths[0].flowFrom(): self._flowFromCheckBox.Set3StateValue(wx.CHK_CHECKED) else: self._flowFromCheckBox.Set3StateValue(wx.CHK_UNCHECKED) else: self._flowFromCheckBox.Set3StateValue(wx.CHK_UNDETERMINED) self._flowFromCheckBox.Enable() if (updatedAttribute is None or updatedAttribute == 'flowFromColor') and not usingDefault: if usingDefault or self.paths.haveEqualAttr('flowFromColor'): if usingDefault: red, green, blue, alpha = self.display.defaultFlowColor else: red, green, blue, alpha = self.paths[0].flowFromColor( ) or self.display.defaultFlowColor self._flowFromColorPicker.SetColour( wx.Colour(red * 255, green * 255, blue * 255, alpha * 255)) self._flowFromColorPicker.SetLabel(gettext('')) else: self._flowFromColorPicker.SetColour(wx.NamedColour( 'GRAY')) # TODO: be clever and pick the average color? self._flowFromColorPicker.SetLabel(gettext('Multiple values')) if updatedAttribute is None or updatedAttribute == 'flowFromSpacing' or updatedAttribute == 'defaultFlowSpacing': if usingDefault or self.paths.haveEqualAttr('flowFromSpacing'): if usingDefault: spacing = self.display.defaultFlowSpacing else: spacing = self.paths[0].flowFromSpacing( ) or self.display.defaultFlowSpacing self._flowFromSpacingSlider.SetLabel('') self._flowFromSpacingSlider.SetValue( log(spacing + 1.0, expBase)) else: self._flowFromSpacingSlider.SetLabel( gettext('Multiple values')) self._flowFromSpacingSlider.SetValue(0) if updatedAttribute is None or updatedAttribute == 'flowFromSpeed' or updatedAttribute == 'defaultFlowSpeed': if usingDefault or self.paths.haveEqualAttr('flowFromSpeed'): if usingDefault: speed = self.display.defaultFlowSpeed else: speed = self.paths[0].flowFromSpeed( ) or self.display.defaultFlowSpeed self._flowFromSpeedSlider.SetLabel('') self._flowFromSpeedSlider.SetValue(log(speed + 1.0, expBase)) else: self._flowFromSpeedSlider.SetLabel(gettext('Multiple values')) self._flowFromSpeedSlider.SetValue(0) if (updatedAttribute is None or updatedAttribute == 'flowFromSpread') and not usingDefault: if usingDefault or self.paths.haveEqualAttr('flowFromSpread'): if usingDefault: spread = self.display.defaultFlowSpread else: spread = self.paths[0].flowFromSpread( ) or self.display.defaultFlowSpread self._flowFromSpreadSlider.SetLabel('') self._flowFromSpreadSlider.SetValue(spread * 50.0) else: self._flowFromSpreadSlider.SetLabel(gettext('Multiple values')) self._flowFromSpreadSlider.SetValue(50) self._window.Layout() def onSetAnimateFlowTo(self, event): newValue = self._flowToCheckBox.GetValue() for path in self.paths: path.setFlowTo(newValue == wx.CHK_CHECKED) def onSetFlowToColor(self, event): wxColor = self._flowToColorPicker.GetColour() newColor = (wxColor.Red() / 255.0, wxColor.Green() / 255.0, wxColor.Blue() / 255.0, wxColor.Alpha() / 255.0) if len(self.paths) == 0: self.display.setDefaultFlowColor(newColor) else: for path in self.paths: path.setFlowToColor(newColor) def onSetFlowToSpacing(self, event): expBase = e**(log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpacing = expBase**self._flowToSpacingSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpacing(newSpacing) else: for path in self.paths: path.setFlowToSpacing(newSpacing) wx.GetApp().Yield() def onSetFlowToSpeed(self, event): expBase = e**(log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpeed = expBase**self._flowToSpeedSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpeed(newSpeed) else: for path in self.paths: path.setFlowToSpeed(newSpeed) wx.GetApp().Yield() def onSetFlowToSpread(self, event): newSpread = self._flowToSpreadSlider.GetValue() / 50.0 if len(self.paths) == 0: self.display.setDefaultFlowSpread(newSpread) else: for path in self.paths: path.setFlowToSpread(newSpread) def onSetAnimateFlowFrom(self, event): newValue = self._flowFromCheckBox.GetValue() for path in self.paths: path.setFlowFrom(newValue == wx.CHK_CHECKED) def onSetFlowFromColor(self, event): wxColor = self._flowFromColorPicker.GetColour() newColor = (wxColor.Red() / 255.0, wxColor.Green() / 255.0, wxColor.Blue() / 255.0, wxColor.Alpha() / 255.0) for path in self.paths: path.setFlowFromColor(newColor) def onSetFlowFromSpacing(self, event): expBase = e**(log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpacing = expBase**self._flowFromSpacingSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpacing(newSpacing) else: for path in self.paths: path.setFlowFromSpacing(newSpacing) def onSetFlowFromSpeed(self, event): expBase = e**(log(self.reasonableSpacingOrSpeed() + 1.0) / 500.0) newSpeed = expBase**self._flowFromSpeedSlider.GetValue() - 1.0 if len(self.paths) == 0: self.display.setDefaultFlowSpeed(newSpeed) else: for path in self.paths: path.setFlowFromSpeed(newSpeed) def onSetFlowFromSpread(self, event): newSpread = self._flowFromSpreadSlider.GetValue() / 50.0 for path in self.paths: path.setFlowFromSpread(newSpread)
class AppearanceInspector(Inspector): @classmethod def name(cls): return gettext('Appearance') @classmethod def canInspectDisplay(cls, display): return display and any(display.selection()) def window(self, parentWindow=None): if not hasattr(self, '_window'): self._window = wx.Window(parentWindow, wx.ID_ANY) gridSizer = wx.FlexGridSizer(5, 2, 8, 8) # Add a color picker for our fill color. self._colorPicker = wx.lib.colourselect.ColourSelect( self._window, wx.ID_ANY) self._window.Bind(wx.lib.colourselect.EVT_COLOURSELECT, self.onSetColor, self._colorPicker) gridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Color:')), 0) gridSizer.Add(self._colorPicker, 1) # Add a slider for setting the opacity. self._opacitySlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 100) self._opacitySlider.Bind(wx.EVT_SCROLL, self.onSetOpacity, self._opacitySlider) gridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Opacity:')), 0) gridSizer.Add(self._opacitySlider, 1) # Add a pop-up for choosing the shape. self._shapeChoice = wx.Choice(self._window, wx.ID_ANY) shapeClasses = [(shapeClass.__name__, shapeClass) for shapeClass in display.shape.shapeClasses()] for shapeName, shapeClass in sorted(shapeClasses): self._shapeChoice.Append(shapeName, shapeClass) self._shapeChoice.Append(gettext('None'), None) self._multipleShapesId = wx.NOT_FOUND gridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Shape:')), 0) gridSizer.Add(self._shapeChoice, 0) parentWindow.Bind(wx.EVT_CHOICE, self.onSetShape, self._shapeChoice) # Add a pop-up for choosing the texture. self._textureChoice = wx.Choice(self._window, wx.ID_ANY) if hasattr(neuroptikon.library, 'texture'): for texture in neuroptikon.library.textures(): self._textureChoice.Append(gettext(texture.name), texture) self._textureChoice.Append(gettext('None'), None) self._multipleTexturesId = wx.NOT_FOUND gridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Texture:')), 0) gridSizer.Add(self._textureChoice, 0) parentWindow.Bind(wx.EVT_CHOICE, self.onSetTexture, self._textureChoice) # Add a slider for setting the weight. self._weightSlider = wx.Slider(self._window, wx.ID_ANY, 50, 0, 100) self._weightSlider.Bind(wx.EVT_SCROLL, self.onSetWeight, self._weightSlider) gridSizer.Add( wx.StaticText(self._window, wx.ID_ANY, gettext('Weight:')), 0) gridSizer.Add(self._weightSlider, 1) # TODO: label, ??? mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(gridSizer, 1, wx.ALL, 5) self._window.SetSizer(mainSizer) return self._window def inspectDisplay(self, display): self.visibles = display.selection() for visible in self.visibles: for attributeName in [ 'color', 'opacity', 'shape', 'texture', 'weight' ]: dispatcher.connect(self.refreshGUI, ('set', attributeName), visible) self.refreshGUI() def willBeClosed(self): for visible in self.visibles: for attributeName in [ 'color', 'opacity', 'shape', 'texture', 'weight' ]: dispatcher.disconnect(self.refreshGUI, ('set', attributeName), visible) self.visibles = ObjectList() def refreshGUI(self, signal=('set', None)): if any(self.visibles): updatedAttribute = signal[1] if updatedAttribute is None or updatedAttribute == 'color': if self.visibles.haveEqualAttr('color'): red, green, blue = self.visibles[0].color() self._colorPicker.SetColour( wx.Colour(red * 255, green * 255, blue * 255, 255)) self._colorPicker.SetLabel(gettext('')) else: self._colorPicker.SetColour(wx.NamedColour( 'GRAY')) # TODO: be clever and pick the average color? self._colorPicker.SetLabel(gettext('Multiple values')) if updatedAttribute is None or updatedAttribute == 'opacity': if self.visibles.haveEqualAttr('opacity'): self._opacitySlider.SetLabel('') self._opacitySlider.SetValue(self.visibles[0].opacity() * 100.0) else: self._opacitySlider.SetLabel(gettext('Multiple values')) self._opacitySlider.SetValue(100) if updatedAttribute is None or updatedAttribute == 'shape': shapeClass = type(self.visibles[0].shape()) equalClasses = True for visible in self.visibles[1:]: if type(visible) != shapeClass: equalClasses = False if equalClasses: for index in range(0, self._shapeChoice.GetCount()): if self._shapeChoice.GetClientData( index) == shapeClass: self._shapeChoice.SetSelection(index) break else: if self._multipleShapesId == wx.NOT_FOUND: self._multipleShapesId = self._shapeChoice.Append( gettext('Multiple values'), None) self._shapeChoice.SetSelection(self._multipleShapesId) if updatedAttribute is None or updatedAttribute == 'texture': if self.visibles.haveEqualAttr('texture'): for index in range(0, self._textureChoice.GetCount()): if self._textureChoice.GetClientData( index) == self.visibles[0].texture(): self._textureChoice.SetSelection(index) break else: if self._multipleTexturesId == wx.NOT_FOUND: self._multipleTexturesId = self._textureChoice.Append( gettext('Multiple values'), None) self._textureChoice.SetSelection(self._multipleTexturesId) if updatedAttribute is None or updatedAttribute == 'weight': if self.visibles.haveEqualAttr('weight'): self._weightSlider.SetLabel('') if self.visibles[0].weight() >= 1.0: self._weightSlider.SetValue( (self.visibles[0].weight() - 1.0) * 50.0 / 9.0 + 50.0) else: self._weightSlider.SetValue( 50.0 - (1.0 - self.visibles[0].weight()) * 10.0 * 50.0 / 9.0) else: self._weightSlider.SetLabel(gettext('Multiple values')) self._weightSlider.SetValue(50) self._window.Layout() def onSetColor(self, event_): wxColor = self._colorPicker.GetColour() colorTuple = (wxColor.Red() / 255.0, wxColor.Green() / 255.0, wxColor.Blue() / 255.0) for visible in self.visibles: visible.setColor(colorTuple) def onSetOpacity(self, event_): newOpacity = self._opacitySlider.GetValue() / 100.0 for visible in self.visibles: visible.setOpacity(newOpacity) def onSetShape(self, event_): shapeClass = self._shapeChoice.GetClientData( self._shapeChoice.GetSelection()) for visible in self.visibles: visible.setShape(None if shapeClass is None else shapeClass()) # Remove the "multiple values" choice now that all of the visibles have the same value. if self._multipleShapesId != wx.NOT_FOUND: self._shapeChoice.Delete(self._multipleShapesId) self._multipleShapesId = wx.NOT_FOUND def onSetTexture(self, event_): texture = self._textureChoice.GetClientData( self._textureChoice.GetSelection()) for visible in self.visibles: visible.setTexture(texture) visible.setTextureScale(10.0) # Remove the "multiple values" choice now that all of the visibles have the same value. if self._multipleTexturesId != wx.NOT_FOUND: self._textureChoice.Delete(self._multipleTexturesId) self._multipleTexturesId = wx.NOT_FOUND def onSetWeight(self, event_): newWeight = self._weightSlider.GetValue() if newWeight >= 50: newWeight = 1.0 + (newWeight - 50.0) / (50.0 / 9.0) else: newWeight = 1.0 - (50.0 - newWeight) / (50.0 / 9.0) / 10.0 for visible in self.visibles: visible.setWeight(newWeight)
class ObjectInspector(Inspector): @classmethod def name(cls): return cls.objectClass().displayName() @classmethod def shouldBeRegistered(cls): return cls != ObjectInspector.__class__ @classmethod def objectClass(cls): return Object @classmethod def inspectedAttributes(cls): return [] @classmethod def bitmap(cls, size=32): image = cls.objectClass().image() if image == None: return Inspector.bitmap(cls) else: scaledImage = image.Rescale(size, size, wx.IMAGE_QUALITY_HIGH) return wx.BitmapFromImage(scaledImage) @classmethod def canInspectDisplay(cls, display): # This inspector can be used if there is at least one object of this type selected. if display: for visible in display.selection(): if visible.client.__class__ == cls.objectClass(): return True return False def window(self, parentWindow=None): if not hasattr(self, '_window'): self._window = wx.Window(parentWindow, wx.ID_ANY) self.iconField = wx.StaticBitmap(self._window, wx.ID_ANY) self.iconField.SetMinSize(wx.Size(32, 32)) self.iconField.SetMaxSize(wx.Size(32, 32)) self.titleField = wx.StaticText(self._window, wx.ID_ANY, "") headerBox = wx.BoxSizer(wx.HORIZONTAL) headerBox.Add(self.iconField, 0, wx.EXPAND) headerBox.AddSpacer(8) headerBox.Add(self.titleField, 1, wx.EXPAND) objectSizer = self.objectSizer(self._window) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(headerBox, 0, wx.EXPAND | wx.ALL, 5) if objectSizer is not None: mainSizer.Add(objectSizer, 1, wx.ALL, 5) self._window.SetSizer(mainSizer) return self._window def inspectDisplay(self, display): # Object inspectors are supposed to work purely at the network/biological layer so they don't need to know what the display is. self.display = display self.objects = ObjectList() self.updatingObjects = False for visible in display.selection(): if visible.client.__class__ == self.__class__.objectClass(): self.objects.append(visible.client) dispatcher.connect(self.nameChanged, ('set', 'name'), visible.client) for attributePath in self.inspectedAttributes(): if '.' in attributePath: (subObject, attribute) = attributePath.split('.') object = getattr(visible.client, subObject) else: object = visible.client attribute = attributePath dispatcher.connect(self.inspectedAttributeChanged, ('set', attribute), object) # Set the icon image = self.objects[0].__class__.image() if image == None: pass else: scaledImage = image.Rescale(32, 32, wx.IMAGE_QUALITY_HIGH) self.iconField.SetBitmap(wx.BitmapFromImage(scaledImage)) self.populateNameField() self.populateObjectSizer() self._window.Layout() def objectSizer(self, parentWindow): return None def populateNameField(self): if self.objects.haveEqualAttr('name'): self.titleField.SetLabel( self.objects[0].name or ('<' + gettext('unnamed ') + self.__class__.objectClass().displayName().lower() + '>')) else: self.titleField.SetLabel(gettext('Multiple names')) def populateObjectSizer(self, attribute=None): pass def nameChanged(self, signal, sender, event=None, value=None, **arguments): self.populateNameField() def inspectedAttributeChanged(self, signal, sender, **arguments): if not self.updatingObjects: self.populateObjectSizer(signal[1]) def selectObject(self, object): self.display.selectObject(object)
def willBeClosed(self): for visible in self.visibles: for attributeName in ['color', 'opacity', 'shape', 'texture', 'weight']: dispatcher.disconnect(self.refreshGUI, ('set', attributeName), visible) self.visibles = ObjectList()
class AppearanceInspector(Inspector): @classmethod def name(cls): return gettext('Appearance') @classmethod def canInspectDisplay(cls, display): return display and any(display.selection()) def window(self, parentWindow=None): if not hasattr(self, '_window'): self._window = wx.Window(parentWindow, wx.ID_ANY) gridSizer = wx.FlexGridSizer(5, 2, 8, 8) # Add a color picker for our fill color. self._colorPicker = wx.lib.colourselect.ColourSelect(self._window, wx.ID_ANY) self._window.Bind(wx.lib.colourselect.EVT_COLOURSELECT, self.onSetColor, self._colorPicker) gridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Color:')), 0) gridSizer.Add(self._colorPicker, 1) # Add a slider for setting the opacity. self._opacitySlider = wx.Slider(self._window, wx.ID_ANY, 100, 0, 100) self._opacitySlider.Bind(wx.EVT_SCROLL, self.onSetOpacity, self._opacitySlider) gridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Opacity:')), 0) gridSizer.Add(self._opacitySlider, 1) # Add a pop-up for choosing the shape. self._shapeChoice = wx.Choice(self._window, wx.ID_ANY) shapeClasses = [(shapeClass.__name__, shapeClass) for shapeClass in display.shape.shapeClasses()] for shapeName, shapeClass in sorted(shapeClasses): self._shapeChoice.Append(shapeName, shapeClass) self._shapeChoice.Append(gettext('None'), None) self._multipleShapesId = wx.NOT_FOUND gridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Shape:')), 0) gridSizer.Add(self._shapeChoice, 0) parentWindow.Bind(wx.EVT_CHOICE, self.onSetShape, self._shapeChoice) # Add a pop-up for choosing the texture. self._textureChoice = wx.Choice(self._window, wx.ID_ANY) if hasattr(neuroptikon.library, 'texture'): for texture in neuroptikon.library.textures(): self._textureChoice.Append(gettext(texture.name), texture) self._textureChoice.Append(gettext('None'), None) self._multipleTexturesId = wx.NOT_FOUND gridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Texture:')), 0) gridSizer.Add(self._textureChoice, 0) parentWindow.Bind(wx.EVT_CHOICE, self.onSetTexture, self._textureChoice) # Add a slider for setting the weight. self._weightSlider = wx.Slider(self._window, wx.ID_ANY, 50, 0, 100) self._weightSlider.Bind(wx.EVT_SCROLL, self.onSetWeight, self._weightSlider) gridSizer.Add(wx.StaticText(self._window, wx.ID_ANY, gettext('Weight:')), 0) gridSizer.Add(self._weightSlider, 1) # TODO: label, ??? mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(gridSizer, 1, wx.ALL, 5) self._window.SetSizer(mainSizer) return self._window def inspectDisplay(self, display): self.visibles = display.selection() for visible in self.visibles: for attributeName in ['color', 'opacity', 'shape', 'texture', 'weight']: dispatcher.connect(self.refreshGUI, ('set', attributeName), visible) self.refreshGUI() def willBeClosed(self): for visible in self.visibles: for attributeName in ['color', 'opacity', 'shape', 'texture', 'weight']: dispatcher.disconnect(self.refreshGUI, ('set', attributeName), visible) self.visibles = ObjectList() def refreshGUI(self, signal = ('set', None)): if any(self.visibles): updatedAttribute = signal[1] if updatedAttribute is None or updatedAttribute == 'color': if self.visibles.haveEqualAttr('color'): red, green, blue = self.visibles[0].color() self._colorPicker.SetColour(wx.Colour(red * 255, green * 255, blue * 255, 255)) self._colorPicker.SetLabel(gettext('')) else: self._colorPicker.SetColour(wx.NamedColour('GRAY')) # TODO: be clever and pick the average color? self._colorPicker.SetLabel(gettext('Multiple values')) if updatedAttribute is None or updatedAttribute == 'opacity': if self.visibles.haveEqualAttr('opacity'): self._opacitySlider.SetLabel('') self._opacitySlider.SetValue(self.visibles[0].opacity() * 100.0) else: self._opacitySlider.SetLabel(gettext('Multiple values')) self._opacitySlider.SetValue(100) if updatedAttribute is None or updatedAttribute == 'shape': shapeClass = type(self.visibles[0].shape()) equalClasses = True for visible in self.visibles[1:]: if type(visible) != shapeClass: equalClasses = False if equalClasses: for index in range(0, self._shapeChoice.GetCount()): if self._shapeChoice.GetClientData(index) == shapeClass: self._shapeChoice.SetSelection(index) break else: if self._multipleShapesId == wx.NOT_FOUND: self._multipleShapesId = self._shapeChoice.Append(gettext('Multiple values'), None) self._shapeChoice.SetSelection(self._multipleShapesId) if updatedAttribute is None or updatedAttribute == 'texture': if self.visibles.haveEqualAttr('texture'): for index in range(0, self._textureChoice.GetCount()): if self._textureChoice.GetClientData(index) == self.visibles[0].texture(): self._textureChoice.SetSelection(index) break else: if self._multipleTexturesId == wx.NOT_FOUND: self._multipleTexturesId = self._textureChoice.Append(gettext('Multiple values'), None) self._textureChoice.SetSelection(self._multipleTexturesId) if updatedAttribute is None or updatedAttribute == 'weight': if self.visibles.haveEqualAttr('weight'): self._weightSlider.SetLabel('') if self.visibles[0].weight() >= 1.0: self._weightSlider.SetValue((self.visibles[0].weight() - 1.0) * 50.0 / 9.0 + 50.0) else: self._weightSlider.SetValue(50.0 - (1.0 - self.visibles[0].weight()) * 10.0 * 50.0 / 9.0) else: self._weightSlider.SetLabel(gettext('Multiple values')) self._weightSlider.SetValue(50) self._window.Layout() def onSetColor(self, event_): wxColor = self._colorPicker.GetColour() colorTuple = (wxColor.Red() / 255.0, wxColor.Green() / 255.0, wxColor.Blue() / 255.0) for visible in self.visibles: visible.setColor(colorTuple) def onSetOpacity(self, event_): newOpacity = self._opacitySlider.GetValue() / 100.0 for visible in self.visibles: visible.setOpacity(newOpacity) def onSetShape(self, event_): shapeClass = self._shapeChoice.GetClientData(self._shapeChoice.GetSelection()) for visible in self.visibles: visible.setShape(None if shapeClass is None else shapeClass()) # Remove the "multiple values" choice now that all of the visibles have the same value. if self._multipleShapesId != wx.NOT_FOUND: self._shapeChoice.Delete(self._multipleShapesId) self._multipleShapesId = wx.NOT_FOUND def onSetTexture(self, event_): texture = self._textureChoice.GetClientData(self._textureChoice.GetSelection()) for visible in self.visibles: visible.setTexture(texture) visible.setTextureScale(10.0) # Remove the "multiple values" choice now that all of the visibles have the same value. if self._multipleTexturesId != wx.NOT_FOUND: self._textureChoice.Delete(self._multipleTexturesId) self._multipleTexturesId = wx.NOT_FOUND def onSetWeight(self, event_): newWeight = self._weightSlider.GetValue() if newWeight >= 50: newWeight = 1.0 + (newWeight - 50.0) / (50.0 / 9.0) else: newWeight = 1.0 - (50.0 - newWeight) / (50.0 / 9.0) / 10.0 for visible in self.visibles: visible.setWeight(newWeight)