# Copyright 2016 Autodesk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# TODO: catch and log event exceptions
import ipywidgets as ipy
import moldesign as mdt
from moldesign import utils, viewer
[docs]class Selector(object):
"""
This is the abstract base class for something that can make a selection.
"""
def __init__(self, *args, **kwargs):
self.selection_group = None
self.selection_id = None
super(Selector, self).__init__(*args, **kwargs)
[docs] def handle_selection_event(self, selection):
raise NotImplementedError()
[docs] def fire_selection_event(self, new_selection):
self.selection_group.update_selections(self, new_selection)
[docs]class SelectionGroup(ipy.Box):
"""
Broadcasts selections among a group of widgets.
It doesn't do much beside rebroadcast events:
A SelectionGroup object will call the "handle_selection_event" methods of
all of its children whenever its "update_selections" method is called.
"""
def __reduce__(self):
"""These don't gat passed around,
so it reduces to NOTHING"""
return utils.make_none, tuple()
def __init__(self, *args, **kwargs):
super(SelectionGroup, self).__init__(*args, **kwargs)
self.selection_listeners = []
self.num_listeners = 0
self.selection = {}
self.viewer = None
self.graphviewer = None
self.register_selection_listeners()
[docs] def update_selections(self, event_source, selection):
self.selection.update(selection)
for listener in self.selection_listeners:
listener.handle_selection_event(selection)
[docs] def register_selection_listeners(self):
self.num_listeners = 0
self.selection_listeners = self.get_child_listeners(self)
[docs] def get_child_listeners(self, element):
if hasattr(element, 'handle_selection_event'):
self.num_listeners += 1
listeners = [element]
element.selection_group = self
element.selection_id = self.num_listeners
if issubclass(element.__class__, mdt.viewer.GeometryViewer):
self.viewer = element
if issubclass(element.__class__, mdt.viewer.ChemicalGraphViewer):
self.graphviewer = element
else:
listeners = []
if hasattr(element, 'children'):
for child in element.children:
listeners.extend(self.get_child_listeners(child))
return listeners
@utils.args_from(viewer.GeometryViewer.set_color)
def set_color(self, *args, **kwargs):
if self.graphviewer: self.graphviewer.set_color(*args, **kwargs)
if self.viewer: self.viewer.set_color(*args, **kwargs)
@utils.args_from(viewer.GeometryViewer.set_color)
def color_by(self, *args, **kwargs):
if self.graphviewer: self.graphviewer.color_by(*args, **kwargs)
if self.viewer: self.viewer.color_by(*args, **kwargs)
@utils.args_from(viewer.GeometryViewer.set_color)
def set_colors(self, *args, **kwargs):
if self.graphviewer: self.graphviewer.set_colors(*args, **kwargs)
if self.viewer: self.viewer.set_colors(*args, **kwargs)
@utils.args_from(viewer.GeometryViewer.unset_color)
def unset_color(self, *args, **kwargs):
if self.graphviewer: self.graphviewer.unset_color(*args, **kwargs)
if self.viewer: self.viewer.unset_color(*args, **kwargs)
def __getattr__(self, item):
if self.viewer is not None: return getattr(self.viewer, item)
else: raise AttributeError(item)
[docs]class ValueSelector(Selector):
""" This is an abstract mixin for a widget class
"""
def __init__(self, value_selects=None, **kwargs):
self.value_selects = value_selects
super(ValueSelector, self).__init__(**kwargs)
self.observe(self.value_update, 'value')
self.__hold_fire = False
[docs] def value_update(self, *args):
if self.__hold_fire: return # prevent recursive selections
self.fire_selection_event({self.value_selects: self.value})
[docs] def handle_selection_event(self, selection):
self.__hold_fire = True
try:
if self.value_selects in selection:
self.value = selection[self.value_selects]
except Exception as exc:
print 'ERROR: (ignored) %s' % exc
self.__hold_fire = False
[docs]def create_value_selector(widget, value_selects, **kwargs):
"""
Creates a UI element (slider, checkbox, etc.) to add to your
selection group.
:param widget: widget class (e.g. ipywidgets.FloatSlider)
:param value_selects: What the value of the widget selects (e.g. time)
:param kwargs: keyword arguments for the widget (e.g. description = "time")
:return: the constructed widget
"""
class SelectionWidget(ValueSelector, widget): pass
return SelectionWidget(value_selects=value_selects, **kwargs)