class _BoundedIntRange(_IntRange): step = CInt( 1, help="Minimum step that the value can take (ignored by some views)", sync=True) max = CInt(100, help="Max value", sync=True) min = CInt(0, help="Min value", sync=True) def __init__(self, *pargs, **kwargs): any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs _IntRange.__init__(self, *pargs, **kwargs) # ensure a minimal amount of sanity if self.min > self.max: raise ValueError("min must be <= max") if any_value_given: # if a value was given, clamp it within (min, max) self._validate("value", None, self.value) else: # otherwise, set it to 25-75% to avoid the handles overlapping self.value = (0.75 * self.min + 0.25 * self.max, 0.25 * self.min + 0.75 * self.max) # callback already set for 'value', 'lower', 'upper' self.on_trait_change(self._validate, ['min', 'max']) def _validate(self, name, old, new): if name == "min": if new > self.max: raise ValueError("setting min > max") elif name == "max": if new < self.min: raise ValueError("setting max < min") low, high = self.value if name == "value": low, high = min(new), max(new) elif name == "upper": if new < self.lower: raise ValueError("setting upper < lower") high = new elif name == "lower": if new > self.upper: raise ValueError("setting lower > upper") low = new low = max(self.min, min(low, self.max)) high = min(self.max, max(high, self.min)) # determine the order in which we should update the # lower, upper traits to avoid a temporary inverted overlap lower_first = high < self.lower self.value = (low, high) if lower_first: self.lower = low self.upper = high else: self.upper = high self.lower = low
class _BoundedInt(_Int): """Base class used to create widgets that represent a int that is bounded by a minium and maximum.""" step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True) max = CInt(100, help="Max value", sync=True) min = CInt(0, help="Min value", sync=True) def __init__(self, *pargs, **kwargs): """Constructor""" super(_BoundedInt, self).__init__(*pargs, **kwargs) self.on_trait_change(self._validate_value, ['value']) self.on_trait_change(self._handle_max_changed, ['max']) self.on_trait_change(self._handle_min_changed, ['min']) def _validate_value(self, name, old, new): """Validate value.""" if self.min > new or new > self.max: self.value = min(max(new, self.min), self.max) def _handle_max_changed(self, name, old, new): """Make sure the min is always <= the max.""" if new < self.min: raise ValueError("setting max < min") def _handle_min_changed(self, name, old, new): """Make sure the max is always >= the min.""" if new > self.max: raise ValueError("setting min > max")
class _IntRange(_Int): value = Tuple(CInt, CInt, default_value=(0, 1), help="Tuple of (lower, upper) bounds", sync=True) lower = CInt(0, help="Lower bound", sync=False) upper = CInt(1, help="Upper bound", sync=False) def __init__(self, *pargs, **kwargs): value_given = 'value' in kwargs lower_given = 'lower' in kwargs upper_given = 'upper' in kwargs if value_given and (lower_given or upper_given): raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget") if lower_given != upper_given: raise ValueError("Must specify both 'lower' and 'upper' for range widget") super(_IntRange, self).__init__(*pargs, **kwargs) # ensure the traits match, preferring whichever (if any) was given in kwargs if value_given: self.lower, self.upper = self.value else: self.value = (self.lower, self.upper) self.on_trait_change(self._validate, ['value', 'upper', 'lower']) def _validate(self, name, old, new): if name == 'value': self.lower, self.upper = min(new), max(new) elif name == 'lower': self.value = (new, self.value[1]) elif name == 'upper': self.value = (self.value[0], new)
class _BoundedInt(_Int): """Base class used to create widgets that represent a int that is bounded by a minium and maximum.""" step = CInt(1, help="Minimum step to increment the value (ignored by some views)", sync=True) max = CInt(100, help="Max value", sync=True) min = CInt(0, help="Min value", sync=True) def __init__(self, *pargs, **kwargs): """Constructor""" super(_BoundedInt, self).__init__(*pargs, **kwargs) def _value_validate(self, value, trait): """Cap and floor value""" if self.min > value or self.max < value: value = min(max(value, self.min), self.max) return value def _min_validate(self, min, trait): """Enforce min <= value <= max""" if min > self.max: raise TraitError("Setting min > max") if min > self.value: self.value = min return min def _max_validate(self, max, trait): """Enforce min <= value <= max""" if max < self.min: raise TraitError("setting max < min") if max < self.value: self.value = max return max
class Renderer(DOMWidget): _view_name = Unicode('RendererView', sync=True) width = CInt(600, sync=True) height = CInt(400, sync=True) renderer_type = Enum(['webgl', 'canvas', 'auto'], 'auto', sync=True) scene = Instance(Scene, sync=True) camera = Instance(Camera, sync=True) controls = Instance(Controls, sync=True)
class RingGeometry(Geometry): _view_name = Unicode('RingGeometryView', sync=True) innerRadius = CFloat(1.0, sync=True) outerRadius = CFloat(3.0, sync=True) thetaSegments = CInt(8, sync=True) phiSegments = CInt(8, sync=True) thetaStart = CFloat(0, sync=True) thetaLength = CFloat(math.pi*2, sync=True)
class SurfaceGeometry(Geometry): """ A regular grid with heights """ _view_name = Unicode('SurfaceGeometryView', sync=True) z = List(CFloat, [0]*100, sync=True) width = CInt(10, sync=True) height = CInt(10, sync=True) width_segments = CInt(10, sync=True) height_segments = CInt(10, sync=True)
class _BoundedIntWidget(_IntWidget): step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True) max = CInt(100, help="Max value", sync=True) min = CInt(0, help="Min value", sync=True) def __init__(self, *pargs, **kwargs): """Constructor""" DOMWidget.__init__(self, *pargs, **kwargs) self.on_trait_change(self._validate, ['value', 'min', 'max']) def _validate(self, name, old, new): """Validate value, max, min.""" if self.min > new or new > self.max: self.value = min(max(new, self.min), self.max)
class TrajectoryViewer(MolecularViewer): frame = CInt() def __init__(self, coordinate_frames, topology, width=500, height=500): '''Display a trajectory in the IPython notebook. :param list coordinate_frames: A list containing the positions of the atoms (as np.ndarray) for each frame. :param dict topology: A dictionary specifying the topology .. seealso:: :class:`MolecularViewer` ''' self.coordinate_frames = coordinate_frames super(TrajectoryViewer, self).__init__(coordinate_frames[0], topology, width=width, height=height) self.controls = TrajectoryControls(len(coordinate_frames)) link((self, 'frame'), (self.controls, 'frame')) def _frame_changed(self, name, old, new): self.coordinates = self.coordinate_frames[new] def _ipython_display_(self): display(self.controls) super(TrajectoryViewer, self)._ipython_display_()
class TubeGeometry(Geometry): _view_name = Unicode('TubeGeometryView', sync=True) path = List(vector3(), sync=True) segments = CInt(64, sync=True) radius = CFloat(1, sync=True) radialSegments = CFloat(8, sync=True) closed = Bool(False, sync=True)
class TextTexture(Texture): _view_name = Unicode('TextTextureView', sync=True) fontFace = Unicode('Arial', sync=True) size = CInt(12, sync=True) color = Color('black', sync=True) string = Unicode('', sync=True) squareTexture = Bool(True, sync=True)
class _SelectionContainer(Box): """Base class used to display multiple child widgets.""" _titles = Dict(help="Titles of the pages", sync=True) selected_index = CInt(0, sync=True) # Public methods def set_title(self, index, title): """Sets the title of a container page. Parameters ---------- index : int Index of the container page title : unicode New title""" self._titles[index] = title self.send_state('_titles') def get_title(self, index): """Gets the title of a container pages. Parameters ---------- index : int Index of the container page""" if index in self._titles: return self._titles[index] else: return None
class TrajectoryControls(DOMWidget): _view_name = Unicode('TrajectoryControls', sync=True) frame = CInt(sync=True) n_frames = CInt(sync=True) fps = CInt(sync=True) def __init__(self, n_frames, fps=30): '''Play/Pause controls useful for playing trajectories. Example: You can connect a callback to be executed every time the frame changes. .. code:: from IPython.display import display controls = TrajectoryControls(10) # 10 frames def callback(frame): print("Current frame %d" % frame) controls.on_frame_change(callback) display(controls) .. py:attribute:: frame Current frame .. py:attribute:: n_frames Total number of frames .. py:attribute:: fps Frames per second (defaults to 30) ''' super(TrajectoryControls, self).__init__() self.n_frames = n_frames - 1 self.fps = fps def on_frame_change(self, callback): '''Connect a callback to be executed every time the frame attribute changes.''' self.on_trait_change(lambda name, old, new: callback(new), "frame")
class _Int(DOMWidget): """Base class used to create widgets that represent an int.""" value = CInt(0, help="Int value", sync=True) disabled = Bool(False, help="Enable or disable user changes", sync=True) description = Unicode(help="Description of the value this widget represents", sync=True) def __init__(self, value=None, **kwargs): if value is not None: kwargs['value'] = value super(_Int, self).__init__(**kwargs)
class DataTexture(Texture): _view_name = Unicode('DataTextureView', sync=True) data = List(CInt, sync=True) format = Enum(['RGBAFormat', 'AlphaFormat', 'RGBFormat', 'LuminanceFormat', 'LuminanceAlphaFormat'], 'RGBAFormat', sync=True) width = CInt(256, sync=True) height = CInt(256, sync=True) type = Enum(['UnsignedByteType', 'ByteType', 'ShortType', 'UnsignedShortType', 'IntType', 'UnsignedIntType', 'FloatType', 'UnsignedShort4444Type', 'UnsignedShort5551Type', 'UnsignedShort565Type'], 'UnsignedByteType', sync=True) mapping = Enum(['UVMapping', 'CubeReflectionMapping', 'CubeRefractionMapping', 'SphericalReflectionMapping', 'SphericalRefractionMapping'], 'UVMapping', sync=True) wrapS = Enum(['ClampToEdgeWrapping', 'RepeatWrapping', 'MirroredRepeatWrapping'], 'ClampToEdgeWrapping', sync=True) wrapT = Enum(['ClampToEdgeWrapping', 'RepeatWrapping', 'MirroredRepeatWrapping'], 'ClampToEdgeWrapping', sync=True) magFilter = Enum(['LinearFilter', 'NearestFilter'], 'LinearFilter', sync=True) minFilter = Enum(['NearestFilter', 'NearestMipMapNearestFilter', 'NearestMipMapLinearFilter', 'LinearFilter', 'LinearMipMapNearestFilter'], 'NearestFilter', sync=True) anisotropy = CInt(1, sync=True)
class Picker(Controls): _view_name = Unicode('PickerView', sync=True) event = Unicode('click', sync=True) root = Instance(Object3d, sync=True) picked = List(Dict, sync=True) distance = CFloat(sync=True) point = vector3(CFloat, sync=True) object = Instance(Object3d, sync=True) face = vector3(CInt, sync=True) faceNormal = vector3(CFloat, sync=True) faceVertices = List(vector3(), sync=True) faceIndex = CInt(sync=True) all = Bool(False, sync=True)
class MolecularViewer(DOMWidget): # Name of the javascript class which this widget syncs against on the # browser side. To work correctly, this javascript class has to be # registered and loaded in the browser before this widget is constructed # (that's what enable_notebook() does) _view_name = Unicode('MolecularView', sync=True) frame = CInt(0, help='Which frame from the trajectory to display') trajectory = Any() # The essence of the IPython interactive widget API on the python side is # that by declaring traitlets with sync=True, these variables are # automatically synced by the IPython runtime between this class in Python # and the browser-side model. Changes to these attributes are propagated # automatically to the browser (and changes on the browser side can trigger # events on this class too, although we're not using that feature). coordinates = Dict(sync=True) topology = Dict(sync=True) point_size = CFloat(sync=True) def __init__(self, trajectory, frame=0, **kwargs): super(MolecularViewer, self).__init__(**kwargs) self.trajectory = trajectory self.frame = frame self.coordinates = encode_numpy(self.trajectory.xyz[self.frame]) def _frame_changed(self, name, old, new): """Automatically called by the traitlet system when self.frame is modified""" self.coordinates = encode_numpy(self.trajectory.xyz[self.frame]) def _trajectory_changed(self, name, old, new): """Automatically called by the traitlet system when self.trajectory is modified""" self.trajectory = new self.frame = 0 # The topology gets changed immediately top = {} bondIndices = [] for ai, aj in self.trajectory.topology.bonds: bondIndices.append((ai.index, aj.index)) top['bonds'] = bondIndices top['atom_colors'] = [ get_atom_color(a.element.symbol) for a in self.trajectory.topology.atoms ] self.topology = top
class LoadBalancedView(View): """An load-balancing View that only executes via the Task scheduler. Load-balanced views can be created with the client's `view` method: >>> v = client.load_balanced_view() or targets can be specified, to restrict the potential destinations: >>> v = client.client.load_balanced_view([1,3]) which would restrict loadbalancing to between engines 1 and 3. """ follow = Any() after = Any() timeout = CFloat() retries = CInt(0) _task_scheme = Any() _flag_names = List( ['targets', 'block', 'track', 'follow', 'after', 'timeout', 'retries']) def __init__(self, client=None, socket=None, **flags): super(LoadBalancedView, self).__init__(client=client, socket=socket, **flags) self._task_scheme = client._task_scheme def _validate_dependency(self, dep): """validate a dependency. For use in `set_flags`. """ if dep is None or isinstance(dep, (basestring, AsyncResult, Dependency)): return True elif isinstance(dep, (list, set, tuple)): for d in dep: if not isinstance(d, (basestring, AsyncResult)): return False elif isinstance(dep, dict): if set(dep.keys()) != set(Dependency().as_dict().keys()): return False if not isinstance(dep['msg_ids'], list): return False for d in dep['msg_ids']: if not isinstance(d, basestring): return False else: return False return True def _render_dependency(self, dep): """helper for building jsonable dependencies from various input forms.""" if isinstance(dep, Dependency): return dep.as_dict() elif isinstance(dep, AsyncResult): return dep.msg_ids elif dep is None: return [] else: # pass to Dependency constructor return list(Dependency(dep)) def set_flags(self, **kwargs): """set my attribute flags by keyword. A View is a wrapper for the Client's apply method, but with attributes that specify keyword arguments, those attributes can be set by keyword argument with this method. Parameters ---------- block : bool whether to wait for results track : bool whether to create a MessageTracker to allow the user to safely edit after arrays and buffers during non-copying sends. after : Dependency or collection of msg_ids Only for load-balanced execution (targets=None) Specify a list of msg_ids as a time-based dependency. This job will only be run *after* the dependencies have been met. follow : Dependency or collection of msg_ids Only for load-balanced execution (targets=None) Specify a list of msg_ids as a location-based dependency. This job will only be run on an engine where this dependency is met. timeout : float/int or None Only for load-balanced execution (targets=None) Specify an amount of time (in seconds) for the scheduler to wait for dependencies to be met before failing with a DependencyTimeout. retries : int Number of times a task will be retried on failure. """ super(LoadBalancedView, self).set_flags(**kwargs) for name in ('follow', 'after'): if name in kwargs: value = kwargs[name] if self._validate_dependency(value): setattr(self, name, value) else: raise ValueError("Invalid dependency: %r" % value) if 'timeout' in kwargs: t = kwargs['timeout'] if not isinstance(t, (int, long, float, type(None))): raise TypeError("Invalid type for timeout: %r" % type(t)) if t is not None: if t < 0: raise ValueError("Invalid timeout: %s" % t) self.timeout = t @sync_results @save_ids def _really_apply(self, f, args=None, kwargs=None, block=None, track=None, after=None, follow=None, timeout=None, targets=None, retries=None): """calls f(*args, **kwargs) on a remote engine, returning the result. This method temporarily sets all of `apply`'s flags for a single call. Parameters ---------- f : callable args : list [default: empty] kwargs : dict [default: empty] block : bool [default: self.block] whether to block track : bool [default: self.track] whether to ask zmq to track the message, for safe non-copying sends !!!!!! TODO: THE REST HERE !!!! Returns ------- if self.block is False: returns AsyncResult else: returns actual result of f(*args, **kwargs) on the engine(s) This will be a list of self.targets is also a list (even length 1), or the single result if self.targets is an integer engine id """ # validate whether we can run if self._socket.closed: msg = "Task farming is disabled" if self._task_scheme == 'pure': msg += " because the pure ZMQ scheduler cannot handle" msg += " disappearing engines." raise RuntimeError(msg) if self._task_scheme == 'pure': # pure zmq scheme doesn't support extra features msg = "Pure ZMQ scheduler doesn't support the following flags:" "follow, after, retries, targets, timeout" if (follow or after or retries or targets or timeout): # hard fail on Scheduler flags raise RuntimeError(msg) if isinstance(f, dependent): # soft warn on functional dependencies warnings.warn(msg, RuntimeWarning) # build args args = [] if args is None else args kwargs = {} if kwargs is None else kwargs block = self.block if block is None else block track = self.track if track is None else track after = self.after if after is None else after retries = self.retries if retries is None else retries follow = self.follow if follow is None else follow timeout = self.timeout if timeout is None else timeout targets = self.targets if targets is None else targets if not isinstance(retries, int): raise TypeError('retries must be int, not %r' % type(retries)) if targets is None: idents = [] else: idents = self.client._build_targets(targets)[0] # ensure *not* bytes idents = [ident.decode() for ident in idents] after = self._render_dependency(after) follow = self._render_dependency(follow) subheader = dict(after=after, follow=follow, timeout=timeout, targets=idents, retries=retries) msg = self.client.send_apply_message(self._socket, f, args, kwargs, track=track, subheader=subheader) tracker = None if track is False else msg['tracker'] ar = AsyncResult(self.client, msg['header']['msg_id'], fname=f.__name__, targets=None, tracker=tracker) if block: try: return ar.get() except KeyboardInterrupt: pass return ar @spin_after @save_ids def map(self, f, *sequences, **kwargs): """view.map(f, *sequences, block=self.block, chunksize=1, ordered=True) => list|AsyncMapResult Parallel version of builtin `map`, load-balanced by this View. `block`, and `chunksize` can be specified by keyword only. Each `chunksize` elements will be a separate task, and will be load-balanced. This lets individual elements be available for iteration as soon as they arrive. Parameters ---------- f : callable function to be mapped *sequences: one or more sequences of matching length the sequences to be distributed and passed to `f` block : bool [default self.block] whether to wait for the result or not track : bool whether to create a MessageTracker to allow the user to safely edit after arrays and buffers during non-copying sends. chunksize : int [default 1] how many elements should be in each task. ordered : bool [default True] Whether the results should be gathered as they arrive, or enforce the order of submission. Only applies when iterating through AsyncMapResult as results arrive. Has no effect when block=True. Returns ------- if block=False: AsyncMapResult An object like AsyncResult, but which reassembles the sequence of results into a single list. AsyncMapResults can be iterated through before all results are complete. else: the result of map(f,*sequences) """ # default block = kwargs.get('block', self.block) chunksize = kwargs.get('chunksize', 1) ordered = kwargs.get('ordered', True) keyset = set(kwargs.keys()) extra_keys = keyset.difference_update(set(['block', 'chunksize'])) if extra_keys: raise TypeError("Invalid kwargs: %s" % list(extra_keys)) assert len(sequences) > 0, "must have some sequences to map onto!" pf = ParallelFunction(self, f, block=block, chunksize=chunksize, ordered=ordered) return pf.map(*sequences)
class ForceDirectedGraph(widgets.DOMWidget): _view_module = Unicode('nbextensions/d3networkx/widget', sync=True) _view_name = Unicode('D3ForceDirectedGraphView', sync=True) width = CInt(1000, sync=True) height = CInt(1000, sync=True) charge = CFloat(270., sync=True) distance = CFloat(30., sync=True) strength = CFloat(0.3, sync=True) def __init__(self, eventful_graph, *pargs, **kwargs): widgets.DOMWidget.__init__(self, *pargs, **kwargs) self._eventful_graph = eventful_graph self._send_dict_changes(eventful_graph.graph, 'graph') self._send_dict_changes(eventful_graph.node, 'node') self._send_dict_changes(eventful_graph.adj, 'adj') def _ipython_display_(self, *pargs, **kwargs): # Show the widget, then send the current state widgets.DOMWidget._ipython_display_(self, *pargs, **kwargs) for (key, value) in self._eventful_graph.graph.items(): self.send({ 'dict': 'graph', 'action': 'add', 'key': key, 'value': value }) for (key, value) in self._eventful_graph.node.items(): self.send({ 'dict': 'node', 'action': 'add', 'key': key, 'value': value }) for (key, value) in self._eventful_graph.adj.items(): self.send({ 'dict': 'adj', 'action': 'add', 'key': key, 'value': value }) def _send_dict_changes(self, eventful_dict, dict_name): def key_add(key, value): self.send({ 'dict': dict_name, 'action': 'add', 'key': key, 'value': value }) def key_set(key, value): self.send({ 'dict': dict_name, 'action': 'set', 'key': key, 'value': value }) def key_del(key): self.send({'dict': dict_name, 'action': 'del', 'key': key}) eventful_dict.on_add(key_add) eventful_dict.on_set(key_set) eventful_dict.on_del(key_del)
class RepresentationViewer(DOMWidget): # Name of the javascript class which this widget syncs against on the # browser side. To work correctly, this javascript class has to be # registered and loaded in the browser before this widget is constructed # (that's what enable_notebook() does) _view_module = Unicode('nbextensions/chemview_widget', sync=True) _view_name = Unicode('MolecularView', sync=True) width = CInt(sync=True) height = CInt(sync=True) background = CInt(sync=True) # Update Camera Hack camera_str = CUnicode(sync=True) static_moving = CBool(sync=True) # Helper loaded = CBool(False, sync=True) def __init__(self, width=500, height=500): '''RepresentationViewer is an IPython notebook widget useful to display 3d scenes through webgl. Example: .. code:: from IPython.display import display rv = RepresentationViewer() rv.add_representation('point', {'coordinates': coordinates, 'colors': colors, 'sizes': sizes}) display(rv) .. py:attribute: width Width in pixels of the IPython widget .. py:attribute: height Height in pixels of the IPython widget .. py:attribute: camera_str A string-representation of camera position and orientation .. py:attribute: static_moving Set to True to make the camera lose the "bouncy" rotation. ''' super(RepresentationViewer, self).__init__() self.displayed = False self.width = width self.height = height # Store the events sent from the javascript side self._event_handlers = defaultdict(list) # What to do when we export def callback(content): display(Image(url=content.get('dataUrl'))) self._connect_event('displayImg', callback) # A record of the new representations self.representations = {} # Things to be called when the js part is done loading self._displayed_callbacks = [] def on_loaded(name, old, new): for cb in self._displayed_callbacks: cb(self) self.on_trait_change(on_loaded, "loaded") def add_representation(self, rep_type, options): '''Add a 3D representation to the viewer. See User Guide for a complete description of the representations available. :return: An unique hexadecimal identifier for the representation. :rtype: str ''' # Add our unique id to be able to refer to the representation rep_id = uuid4().hex self.representations[rep_id] = { 'type': rep_type, 'options': options.copy() } self._remote_call('addRepresentation', type=rep_type, repId=rep_id, options=options) return rep_id def remove_representation(self, rep_id): '''Remove a representation from the viewer :param str rep_id: the unique identifier generated by RepresentationViewer.add_representation ''' self._remote_call('removeRepresentation', repId=rep_id) del self.representations[rep_id] def update_representation(self, rep_id, options): '''Update a representation with new data. :param str rep_id: the unique identifier returned by RepresentationViewer.add_representation :param dict options: dictionary containing the updated data. ''' self.representations[rep_id]['options'].update(options) self._remote_call('updateRepresentation', repId=rep_id, options=options) def _connect_event(self, event_name, callback): '''Respond to an event sent by the Javascript side. Events available: - displayImg - serialize ''' self._event_handlers[event_name].append(callback) def _remote_call(self, method_name, **kwargs): '''Call a method remotely on the javascript side''' msg = {} msg['type'] = 'callMethod' msg['methodName'] = method_name msg['args'] = self._recursive_serialize(kwargs) if self.displayed is True: self.send(msg) # This will be received with View.on_msg else: # We should prepare a callback to be # called when widget is displayed def callback(widget, msg=msg): widget.send(msg) self._displayed_callbacks.append(callback) def _recursive_serialize(self, dictionary): '''Serialize a dictionary inplace''' for k, v in dictionary.items(): if isinstance(v, dict): self._recursive_serialize(v) else: # This is when custom serialization happens if isinstance(v, np.ndarray): if v.dtype == 'float64': # We don't support float64 on js side v = v.astype('float32') dictionary[k] = encode_numpy(v) return dictionary def _handle_custom_msg(self, content): # Handle custom messages sent by the javascript counterpart event = content.get('event', '') for cb in self._event_handlers[event]: cb(content) def _ipython_display_(self, **kwargs): super(RepresentationViewer, self)._ipython_display_(**kwargs) self.displayed = True def get_scene(self): '''Return a dictionary that uniquely identifies the scene displayed''' scene = {} # Camera camspec = json.loads(self.camera_str) location = np.array([ camspec['position']['x'], camspec['position']['y'], camspec['position']['z'] ], 'float') quaternion = np.array([ camspec['quaternion']['_x'], camspec['quaternion']['_y'], camspec['quaternion']['_z'], camspec['quaternion']['_w'] ], 'float') target = np.array([ camspec['target']['x'], camspec['target']['y'], camspec['target']['z'] ], 'float') scene['camera'] = dict(location=location, quaternion=quaternion, target=target, vfov=camspec['fov'], aspect=camspec['aspect']) # Lights: TODO scene['lights'] = [{ 'position': np.array([2, 4, -3]) * 1000, 'color': 0xffffff }, { 'position': np.array([-1, 2, 3]) * 1000, 'color': 0xffffff }] # Objects scene['representations'] = self.representations.values() scene['background'] = self.background return scene
class _IntWidget(DOMWidget): value = CInt(0, help="Int value", sync=True) disabled = Bool(False, help="Enable or disable user changes", sync=True) description = Unicode(help="Description of the value this widget represents", sync=True)
class ParametricGeometry(Geometry): _view_name = Unicode('ParametricGeometryView', sync=True) func = Unicode('', sync=True) slices = CInt(105, sync=True) stacks = CInt(105,sync=True)
class OpenMMSimulator(Device): name = 'OpenMM' path = 'msmaccelerator.simulate.simulation.OpenMMSimulator' short_description = 'Run a single round of dynamics with OpenMM' long_description = '''This device will connect to the msmaccelerator server, request the initial conditions with which to start a simulation, and propagate dynamics''' # configurables. system_xml = Unicode('system.xml', config=True, help=''' Path to the XML file containing the OpenMM system to propagate''') system = Instance('simtk.openmm.openmm.System') integrator_xml = Unicode('integrator.xml', config=True, help=''' Path to the XML file containing the OpenMM Integrator to use''') integrator = Instance('simtk.openmm.openmm.Integrator') number_of_steps = CInt(10000, config=True, help=''' Number of steps of dynamics to do''') report_interval = CInt(1000, config=True, help=''' Interval at which to save positions to a disk, in units of steps''') minimize = Bool(True, config=True, help='''Do local energy minimization on the configuration that's passed to me, before running dynamics''') random_initial_velocities = Bool(True, config=True, help='''Choose random initial velocities from the Maxwell-Boltzmann distribution''') platform = Enum( ['Reference', 'CUDA', 'OpenCL'], default_value='CUDA', config=True, help='''The OpenMM platform on which to run the simulation''') device_index = CInt(0, config=True, help='''OpenMM device index for CUDA or OpenCL platforms. This is used to select which GPU will be used on a multi-gpu system. This option is ignored on reference platform''') # expose these as command line flags on --help # other settings can still be specified on the command line, its just # less convenient aliases = dict(system_xml='OpenMMSimulator.system_xml', integrator_xml='OpenMMSimulator.integrator_xml', number_of_steps='OpenMMSimulator.number_of_steps', report_interval='OpenMMSimulator.report_interval', zmq_port='Device.zmq_port', zmq_url='Device.zmq_url', platform='OpenMMSimulator.platform', device_index='OpenMMSimulator.device_index') def start(self): # load up the system and integrator files with open(self.system_xml) as f: self.system = XmlSerializer.deserialize(f.read()) # reset the random number seed for any random # forces (andersen thermostat, montecarlo barostat) for i in range(self.system.getNumForces()): force = self.system.getForce(i) if hasattr(force, 'setRandomNumberSeed'): force.setRandomNumberSeed(random_seed()) with open(self.integrator_xml) as f: self.integrator = XmlSerializer.deserialize(f.read()) # reset the random number seed for a stochastic integrator if hasattr(self.integrator, 'setRandomNumberSeed'): self.integrator.setRandomNumberSeed(random_seed()) super(OpenMMSimulator, self).start() def on_startup_message(self, msg): """This method is called when the device receives its startup message from the server. """ assert msg.header.msg_type in ['simulate'] # only allowed RPC return getattr(self, msg.header.msg_type)(msg.header, msg.content) def simulate(self, header, content): """Main method that is "executed" by the receipt of the msg_type == 'simulate' message from the server. We run some OpenMM dynamics, and then send back the results. """ self.log.info('Setting up simulation...') state, topology = self.deserialize_input(content) starting_state_path = content.starting_state.path # set the GPU platform platform = Platform.getPlatformByName(str(self.platform)) if self.platform == 'CUDA': properties = { 'CudaPrecision': 'mixed', 'CudaDeviceIndex': str(self.device_index) } elif self.platform == 'OpenCL': properties = { 'OpenCLPrecision': 'mixed', 'OpenCLDeviceIndex': str(self.device_index) } else: properties = None simulation = Simulation(topology, self.system, self.integrator, platform, properties) # do the setup self.set_state(state, simulation) self.sanity_check(simulation) if self.minimize: self.log.info('minimizing...') simulation.minimizeEnergy() if self.random_initial_velocities: try: temp = simulation.integrator.getTemperature() simulation.context.setVelocitiesToTemperature(temp) except AttributeError: print "I don't know what temperature to use!!" # TODO: look through the system's forces to find an andersen # thermostate? raise pass assert content.output.protocol == 'localfs', "I'm currently only equiped for localfs output" self.log.info('adding reporters...') self.add_reporters(simulation, content.output.path) # run dynamics! self.log.info('Starting dynamics') simulation.step(self.number_of_steps) for reporter in simulation.reporters: # explicitly delete the reporters so that any open file handles # are closed. del reporter # tell the master that I'm done self.send_recv(msg_type='simulation_done', content={ 'status': 'success', 'starting_state': { 'protocol': 'localfs', 'path': starting_state_path }, 'output': { 'protocol': 'localfs', 'path': content.output.path } }) ########################################################################## # Begin helpers for setting up the simulation ########################################################################## def sanity_check(self, simulation): positions = simulation.context.getState( getPositions=True).getPositions(asNumpy=True) for atom1, atom2 in simulation.topology.bonds(): d = np.linalg.norm(positions[atom1.index, :] - positions[atom2.index, :]) if not d < 0.3: self.log.error(positions[atom1.index, :]) self.log.error(positions[atom2.index, :]) raise ValueError( 'atoms are bonded according to topology but not close by ' 'in space: %s. %s' % (d, positions)) def deserialize_input(self, content): """Retreive the state and topology from the message content The message protocol tries not to pass 'data' around within the messages, but instead pass paths to data. So far we're only sending paths on the local filesystem, but we might could generalize this to HTTP or S3 or something later. The assumption that data can be passed around on the local filesystem shouldn't be built deep into the code at all """ # todo: better name for this function? if content.starting_state.protocol == 'localfs': with open(content.starting_state.path) as f: self.log.info('Opening state file: %s', content.starting_state.path) state = XmlSerializer.deserialize(f.read()) else: raise ValueError('Unknown protocol') if content.topology_pdb.protocol == 'localfs': topology = PDBFile(content.topology_pdb.path).topology else: raise ValueError('Unknown protocol') return state, topology def set_state(self, state, simulation): "Set the state of a simulation to whatever is in the state object" # why do I have to do this so... manually? # this is why: # simulation.context.setState(state) # TypeError: in method 'Context_setState', argument 2 of type 'State const &' simulation.context.setPositions(state.getPositions()) simulation.context.setVelocities(state.getVelocities()) simulation.context.setPeriodicBoxVectors( *state.getPeriodicBoxVectors()) for key, value in state.getParameters(): simulation.context.setParameter(key, value) def add_reporters(self, simulation, outfn): "Add reporters to a simulation" def reporter_callback(report): """Callback for processing reporter output""" self.log.info(report) callback_reporter = CallbackReporter(reporter_callback, self.report_interval, step=True, potentialEnergy=True, temperature=True, time=True, total_steps=self.number_of_steps) h5_reporter = HDF5Reporter(outfn, self.report_interval, coordinates=True, time=True, cell=True, potentialEnergy=True, kineticEnergy=True, temperature=True) simulation.reporters.append(callback_reporter) simulation.reporters.append(h5_reporter)
class PolyhedronGeometry(Geometry): _view_name = Unicode('PolyhedronGeometryView', sync=True) radius = CFloat(1, sync=True) detail = CInt(0, sync=True) vertices = List(List(CFloat), sync=True) faces = List(List(CInt), sync=True)
class TrajectoryView(DOMWidget): """IPython notebook widget for displaying trajectories in the browser with WebGL Example ------- # if the final line occurs at the end of an IPython notebook cell, the # resulting interactive widget will be displayed >>> t = md.load('trajectory.pdb') >>> from mdtraj.html import enable_notebook, TrajectoryView >>> enable_notebook() >>> widget = TrajectoryView(t) >>> widget Attributes ---------- camera : {'perspective', 'orthographic'} Camera mode (default='perspective') background : {'black', 'grey', 'white'} Background color (default='black') colorBy : {'spectrum', 'chain', 'secondary structure', 'residue', 'polarity', 'atom'} Color scheme (default='white') primaryStructure : {'lines', 'stick', 'ball & stick','sphere', 'nothing'} Drawing method for the primary structure (default='nothing') secondaryStructure = {'ribbon', 'strand', 'cylinder & plate', 'C alpha trace', 'nothing'} Drawing method for secondary structure. (default='cylinder & plate') surfaceRepresentation = {'Van der Waals surface', 'solvent excluded surface', 'solvent accessible surface', 'molecular surface', 'nothing'} Drawing method for surface representation. (default='nothing') Notes ----- All of the attributes listed above are synced with the browser's widget. Modifying these attributes, after the widget is constructed, will cause the widget to update *live*. They can also be set at widget construction time as keyword arguments to ``__init__``. The viewer WebGL viewer used, iview, is documented in [1]. References ---------- ..[1] Li, Hongjian, et al. "iview: an interactive WebGL visualizer for protein-ligand complex." BMC Bioinformatics 15.1 (2014): 56. See Also -------- enable_notebook() : Executing this function before using the widget is required to load the required browser-side libraries """ disabled = Bool(False, help="Enable or disable user changes.", sync=True) # Name of the javascript class which this widget syncs against on the # browser side. To work correctly, this javascript class has to be # registered and loaded in the browser before this widget is constructed # (that's what enable_notebook() does) _view_name = Unicode('TrajectoryView', sync=True) frame = CInt(0, help='Which frame from the trajectory to display') trajectory = Any() # The essence of the IPython interactive widget API on the python side is # that by declaring traitlets with sync=True, these variables are # automatically synced by the IPython runtime between this class in Python # and the browser-side model. Changes to these attributes are propagated # automatically to the browser (and changes on the browser side can trigger # events on this class too, although we're not using that feature). _topology = Dict(sync=True) _frameData = Dict(sync=True) # Display options camera = Enum(['perspective', 'orthographic'], 'perspective', sync=True) background = Enum(['black', 'grey', 'white'], 'white', sync=True) colorBy = Enum([ 'spectrum', 'chain', 'secondary structure', 'residue', 'polarity', 'atom' ], 'spectrum', sync=True) primaryStructure = Enum( ['lines', 'stick', 'ball & stick', 'sphere', 'nothing'], 'nothing', sync=True) secondaryStructure = Enum( ['ribbon', 'strand', 'cylinder & plate', 'C alpha trace', 'nothing'], 'cylinder & plate', sync=True) surfaceRepresentation = Enum([ 'Van der Waals surface', 'solvent excluded surface', 'solvent accessible surface', 'molecular surface', 'nothing' ], 'nothing', sync=True) def __init__(self, trajectory, frame=0, **kwargs): super(TrajectoryView, self).__init__(**kwargs) self.trajectory = trajectory self.frame = frame def _frame_changed(self, name, old, new): """Automatically called by the traitlet system when self.frame is modified""" self._update_frame_data() def _trajectory_changed(self, name, old, new): """Automatically called by the traitlet system when self.trajectory is modified""" self._topology = self._computeTopology() self._update_frame_data() def _update_frame_data(self): self._frameData = { 'coordinates': encode_numpy(self.trajectory.xyz[self.frame]), 'secondaryStructure': self._computeSecondaryStructure() } def _computeSecondaryStructure(self): """Compute the secondary structure of the selected frame and format it for the browser """ SS_MAP = {'C': 'coil', 'H': 'helix', 'E': 'sheet'} top = self.trajectory.topology dssp = md.compute_dssp(self.trajectory[self.frame])[0] result = {} # iterate over the (rindx, ss) pairs in enumerate(dssp), # and use itertools to group them into streaks by contiguous # chain and ss. keyfunc = lambda ir: (top.residue(ir[0]).chain, ir[1]) for (chain, ss), grouper in groupby(enumerate(dssp), keyfunc): # rindxs is a list of residue indices in this contiguous run rindxs = [g[0] for g in grouper] for r in rindxs: # add entry for each atom in the residue for a in top.residue(r).atoms: result[a.index] = { 'ss': SS_MAP[ss], 'ssbegin': (r == rindxs[0] and ss in set(['H', 'E'])), 'ssend': (r == rindxs[-1] and ss in set(['H', 'E'])) } return result def _computeTopology(self): """Extract the topology and format it for the browser. iview has a particular format for storing topology-based information, and for simplicity and hack-ability the best place to do the conversion is here in python. """ # TODO(rmcgibbo). Document this schema. It needs to match with what's # going on inside iview.loadTopology on the browser side. atoms = {} # these should be mutually exclusive. you're only in one of # these categories peptideIndices = [] waterIndices = [] ionIndices = [] ligandIndices = [] bondIndices = [] calphaIndices = [] hetIndices = [] for atom in self.trajectory.topology.atoms: atoms[atom.index] = { 'alt': ' ', 'b': 0, 'chain': atom.residue.chain.index, 'elem': atom.element.symbol if atom.element is not None else 'X', 'insc': ' ', 'name': atom.name, 'resi': atom.residue.index, 'resn': atom.residue.name, 'serial': atom.index, 'ss': None, 'coord': None, 'bonds': [], } if atom.name == 'CA': calphaIndices.append(atom.index) if atom.residue.is_water: waterIndices.append(atom.index) elif not atom.residue.is_protein: ligandIndices.append(atom.index) else: peptideIndices.append(atom.index) for ai, aj in self.trajectory.topology.bonds: bondIndices.append((ai.index, aj.index)) return { 'atoms': atoms, 'bondIndices': bondIndices, 'ionIndices': ionIndices, 'calphaIndices': calphaIndices, 'hetIndices': hetIndices, 'peptideIndices': peptideIndices, 'ligandIndices': ligandIndices, 'waterIndices': waterIndices }
class _Int(DOMWidget): """Base class used to create widgets that represent an int.""" value = CInt(0, help="Int value", sync=True) disabled = Bool(False, help="Enable or disable user changes", sync=True) description = Unicode( help="Description of the value this widget represents", sync=True)
class HistoryManager(HistoryAccessor): """A class to organize all history-related functionality in one place. """ # Public interface # An instance of the IPython shell we are attached to shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') # Lists to hold processed and raw history. These start with a blank entry # so that we can index them starting from 1 input_hist_parsed = List([""]) input_hist_raw = List([""]) # A list of directories visited during session dir_hist = List() def _dir_hist_default(self): try: return [os.getcwdu()] except OSError: return [] # A dict of output history, keyed with ints from the shell's # execution count. output_hist = Dict() # The text/plain repr of outputs. output_hist_reprs = Dict() # The number of the current session in the history database session_number = CInt() # Should we log output to the database? (default no) db_log_output = Bool(False, config=True) # Write to database every x commands (higher values save disk access & power) # Values of 1 or less effectively disable caching. db_cache_size = Int(0, config=True) # The input and output caches db_input_cache = List() db_output_cache = List() # History saving in separate thread save_thread = Instance('IPython.core.history.HistorySavingThread') try: # Event is a function returning an instance of _Event... save_flag = Instance(threading._Event) except AttributeError: # ...until Python 3.3, when it's a class. save_flag = Instance(threading.Event) # Private interface # Variables used to store the three last inputs from the user. On each new # history update, we populate the user's namespace with these, shifted as # necessary. _i00 = Unicode(u'') _i = Unicode(u'') _ii = Unicode(u'') _iii = Unicode(u'') # A regex matching all forms of the exit command, so that we don't store # them in the history (it's annoying to rewind the first entry and land on # an exit call). _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$") def __init__(self, shell=None, config=None, **traits): """Create a new history manager associated with a shell instance. """ # We need a pointer back to the shell for various tasks. super(HistoryManager, self).__init__(shell=shell, config=config, **traits) self.save_flag = threading.Event() self.db_input_cache_lock = threading.Lock() self.db_output_cache_lock = threading.Lock() self.save_thread = HistorySavingThread(self) self.save_thread.start() self.new_session() def _get_hist_file_name(self, profile=None): """Get default history file name based on the Shell's profile. The profile parameter is ignored, but must exist for compatibility with the parent class.""" profile_dir = self.shell.profile_dir.location return os.path.join(profile_dir, 'history.sqlite') @needs_sqlite def new_session(self, conn=None): """Get a new session number.""" if conn is None: conn = self.db with conn: cur = conn.execute( """INSERT INTO sessions VALUES (NULL, ?, NULL, NULL, "") """, (datetime.datetime.now(), )) self.session_number = cur.lastrowid def end_session(self): """Close the database session, filling in the end time and line count.""" self.writeout_cache() with self.db: self.db.execute( """UPDATE sessions SET end=?, num_cmds=? WHERE session==?""", (datetime.datetime.now(), len(self.input_hist_parsed) - 1, self.session_number)) self.session_number = 0 def name_session(self, name): """Give the current session a name in the history database.""" with self.db: self.db.execute("UPDATE sessions SET remark=? WHERE session==?", (name, self.session_number)) def reset(self, new_session=True): """Clear the session history, releasing all object references, and optionally open a new session.""" self.output_hist.clear() # The directory history can't be completely empty self.dir_hist[:] = [os.getcwdu()] if new_session: if self.session_number: self.end_session() self.input_hist_parsed[:] = [""] self.input_hist_raw[:] = [""] self.new_session() # ------------------------------ # Methods for retrieving history # ------------------------------ def _get_range_session(self, start=1, stop=None, raw=True, output=False): """Get input and output history from the current session. Called by get_range, and takes similar parameters.""" input_hist = self.input_hist_raw if raw else self.input_hist_parsed n = len(input_hist) if start < 0: start += n if not stop or (stop > n): stop = n elif stop < 0: stop += n for i in range(start, stop): if output: line = (input_hist[i], self.output_hist_reprs.get(i)) else: line = input_hist[i] yield (0, i, line) def get_range(self, session=0, start=1, stop=None, raw=True, output=False): """Retrieve input by session. Parameters ---------- session : int Session number to retrieve. The current session is 0, and negative numbers count back from current session, so -1 is previous session. start : int First line to retrieve. stop : int End of line range (excluded from output itself). If None, retrieve to the end of the session. raw : bool If True, return untranslated input output : bool If True, attempt to include output. This will be 'real' Python objects for the current session, or text reprs from previous sessions if db_log_output was enabled at the time. Where no output is found, None is used. Returns ------- An iterator over the desired lines. Each line is a 3-tuple, either (session, line, input) if output is False, or (session, line, (input, output)) if output is True. """ if session <= 0: session += self.session_number if session == self.session_number: # Current session return self._get_range_session(start, stop, raw, output) return super(HistoryManager, self).get_range(session, start, stop, raw, output) ## ---------------------------- ## Methods for storing history: ## ---------------------------- def store_inputs(self, line_num, source, source_raw=None): """Store source and raw input in history and create input cache variables _i*. Parameters ---------- line_num : int The prompt number of this input. source : str Python input. source_raw : str, optional If given, this is the raw input without any IPython transformations applied to it. If not given, ``source`` is used. """ if source_raw is None: source_raw = source source = source.rstrip('\n') source_raw = source_raw.rstrip('\n') # do not store exit/quit commands if self._exit_re.match(source_raw.strip()): return self.input_hist_parsed.append(source) self.input_hist_raw.append(source_raw) with self.db_input_cache_lock: self.db_input_cache.append((line_num, source, source_raw)) # Trigger to flush cache and write to DB. if len(self.db_input_cache) >= self.db_cache_size: self.save_flag.set() # update the auto _i variables self._iii = self._ii self._ii = self._i self._i = self._i00 self._i00 = source_raw # hackish access to user namespace to create _i1,_i2... dynamically new_i = '_i%s' % line_num to_main = { '_i': self._i, '_ii': self._ii, '_iii': self._iii, new_i: self._i00 } self.shell.user_ns.update(to_main) def store_output(self, line_num): """If database output logging is enabled, this saves all the outputs from the indicated prompt number to the database. It's called by run_cell after code has been executed. Parameters ---------- line_num : int The line number from which to save outputs """ if (not self.db_log_output) or (line_num not in self.output_hist_reprs): return output = self.output_hist_reprs[line_num] with self.db_output_cache_lock: self.db_output_cache.append((line_num, output)) if self.db_cache_size <= 1: self.save_flag.set() def _writeout_input_cache(self, conn): with conn: for line in self.db_input_cache: conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)", (self.session_number, ) + line) def _writeout_output_cache(self, conn): with conn: for line in self.db_output_cache: conn.execute("INSERT INTO output_history VALUES (?, ?, ?)", (self.session_number, ) + line) @needs_sqlite def writeout_cache(self, conn=None): """Write any entries in the cache to the database.""" if conn is None: conn = self.db with self.db_input_cache_lock: try: self._writeout_input_cache(conn) except sqlite3.IntegrityError: self.new_session(conn) print("ERROR! Session/line number was not unique in", "database. History logging moved to new session", self.session_number) try: # Try writing to the new session. If this fails, don't recurse self._writeout_input_cache(conn) except sqlite3.IntegrityError: pass finally: self.db_input_cache = [] with self.db_output_cache_lock: try: self._writeout_output_cache(conn) except sqlite3.IntegrityError: print("!! Session/line number for output was not unique", "in database. Output will not be stored.") finally: self.db_output_cache = []
class LatheGeometry(Geometry): _view_name = Unicode('LatheGeometryView', sync=True) points = List(vector3(), sync=True) segments = CInt(12, sync=True) phiStart = CFloat(0, sync=True) phiLength = CFloat(2*math.pi, sync=True)