def __init__(self, structure=None, representations=None, parameters=None, **kwargs): super(NGLWidget, self).__init__(**kwargs) self._gui = None self._init_gui = kwargs.pop('gui', False) self._theme = kwargs.pop('theme', 'default') self._widget_image = widget_image.Image() self._widget_image.width = 900. self._image_array = [] # do not use _displayed_callbacks since there is another Widget._display_callbacks self._event = threading.Event() self._ngl_displayed_callbacks_before_loaded = [] self._ngl_displayed_callbacks_after_loaded = [] _add_repr_method_shortcut(self, self) self.shape = Shape(view=self) self._handle_msg_thread = threading.Thread(target=self.on_msg, args=(self._ngl_handle_msg,)) # # register to get data from JS side self._handle_msg_thread.daemon = True self._handle_msg_thread.start() self._remote_call_thread = RemoteCallThread(self) self._remote_call_thread.start() self._trajlist = [] self._ngl_component_ids = [] self._init_structures = [] if parameters: self.parameters = parameters if isinstance(structure, Trajectory): name = py_utils.get_name(structure, kwargs) self.add_trajectory(structure, name=name) elif isinstance(structure, (list, tuple)): trajectories = structure for trajectory in trajectories: name = py_utils.get_name(trajectory, kwargs) self.add_trajectory(trajectory, name=name) else: if structure is not None: self.add_structure(structure, **kwargs) # call before setting representations self._set_initial_structure(self._init_structures) if representations: self._init_representations = representations else: self._init_representations = [ {"type": "cartoon", "params": { "sele": "polymer" }}, {"type": "ball+stick", "params": { "sele": "hetero OR mol" }}, {"type": "ball+stick", "params": { "sele": "not protein and not nucleic" }} ] # keep track but making copy if structure is not None: self._representations = self._init_representations[:] self._set_unsync_camera() self.selector = str(uuid.uuid4()).replace('-', '') self._remote_call('setSelector', target='Widget', args=[self.selector,]) self.selector = '.' + self.selector # for PlaceProxy self._place_proxy = PlaceProxy(child=None, selector=self.selector) self.player = TrajectoryPlayer(self) self._already_constructed = True
class NGLWidget(DOMWidget): _view_name = Unicode("NGLView").tag(sync=True) _view_module = Unicode("nglview-js-widgets").tag(sync=True) _image_data = Unicode().tag(sync=True) selector = Unicode().tag(sync=True) frame = Int().tag(sync=True) count = Int(1).tag(sync=True) background = Unicode('white').tag(sync=True) loaded = Bool(False).tag(sync=False) picked = Dict().tag(sync=True) n_components = Int(0).tag(sync=True) orientation = List().tag(sync=True) _first_time_loaded = Bool(True).tag(sync=False) # hack to always display movie _n_dragged_files = Int().tag(sync=True) _init_representations = List().tag(sync=True) _init_structures_sync = List().tag(sync=True) _parameters = Dict().tag(sync=True) _full_stage_parameters = Dict().tag(sync=True) _original_stage_parameters = Dict().tag(sync=True) _coordinates_dict = Dict().tag(sync=False) _camera_str = CaselessStrEnum(['perspective', 'orthographic'], default_value='orthographic').tag(sync=True) _repr_dict = Dict().tag(sync=False) _ngl_component_ids = List().tag(sync=False) _ngl_component_names = List().tag(sync=False) _already_constructed = Bool(False).tag(sync=False) _ngl_msg = None _send_binary = Bool(True).tag(sync=False) _init_gui = Bool(False).tag(sync=False) _hold_image = Bool(False).tag(sync=False) def __init__(self, structure=None, representations=None, parameters=None, **kwargs): super(NGLWidget, self).__init__(**kwargs) self._gui = None self._init_gui = kwargs.pop('gui', False) self._theme = kwargs.pop('theme', 'default') self._widget_image = widget_image.Image() self._widget_image.width = 900. self._image_array = [] # do not use _displayed_callbacks since there is another Widget._display_callbacks self._event = threading.Event() self._ngl_displayed_callbacks_before_loaded = [] self._ngl_displayed_callbacks_after_loaded = [] _add_repr_method_shortcut(self, self) self.shape = Shape(view=self) self._handle_msg_thread = threading.Thread(target=self.on_msg, args=(self._ngl_handle_msg,)) # # register to get data from JS side self._handle_msg_thread.daemon = True self._handle_msg_thread.start() self._remote_call_thread = RemoteCallThread(self) self._remote_call_thread.start() self._trajlist = [] self._ngl_component_ids = [] self._init_structures = [] if parameters: self.parameters = parameters if isinstance(structure, Trajectory): name = py_utils.get_name(structure, kwargs) self.add_trajectory(structure, name=name) elif isinstance(structure, (list, tuple)): trajectories = structure for trajectory in trajectories: name = py_utils.get_name(trajectory, kwargs) self.add_trajectory(trajectory, name=name) else: if structure is not None: self.add_structure(structure, **kwargs) # call before setting representations self._set_initial_structure(self._init_structures) if representations: self._init_representations = representations else: self._init_representations = [ {"type": "cartoon", "params": { "sele": "polymer" }}, {"type": "ball+stick", "params": { "sele": "hetero OR mol" }}, {"type": "ball+stick", "params": { "sele": "not protein and not nucleic" }} ] # keep track but making copy if structure is not None: self._representations = self._init_representations[:] self._set_unsync_camera() self.selector = str(uuid.uuid4()).replace('-', '') self._remote_call('setSelector', target='Widget', args=[self.selector,]) self.selector = '.' + self.selector # for PlaceProxy self._place_proxy = PlaceProxy(child=None, selector=self.selector) self.player = TrajectoryPlayer(self) self._already_constructed = True @property def parameters(self): return self._parameters @parameters.setter def parameters(self, params): params = _camelize_dict(params) self._parameters = params @property def camera(self): return self._camera_str @camera.setter def camera(self, value): """ Parameters ---------- value : str, {'perspective', 'orthographic'} """ self._camera_str = value # use _remote_call so this function can be called right after # self is displayed self._remote_call("setParameters", target='Stage', kwargs=dict(cameraType=self._camera_str)) def _request_stage_parameters(self): self._remote_call('requestUpdateStageParameters', target='Widget') @observe('picked') def _on_picked(self, change): picked = change['new'] if self.player.widget_picked is not None: self.player.widget_picked.value = json.dumps(picked) @observe('background') def _update_background_color(self, change): color = change['new'] self.parameters = dict(background_color=color) @observe('_n_dragged_files') def on_update_dragged_file(self, change): if change['new'] - change['old'] == 1: self._ngl_component_ids.append(uuid.uuid4()) @observe('n_components') def _handle_n_components_changed(self, change): if self.player.widget_repr is not None: component_slider = widget_utils.get_widget_by_name(self.player.widget_repr, 'component_slider') if change['new'] - 1 >= component_slider.min: component_slider.max = change['new'] - 1 component_dropdown = widget_utils.get_widget_by_name(self.player.widget_repr, 'component_dropdown') component_dropdown.options = tuple(self._ngl_component_names) if change['new'] == 0: component_dropdown.options = tuple([' ']) component_dropdown.value = ' ' component_slider.max = 0 reprlist_choices = widget_utils.get_widget_by_name(self.player.widget_repr, 'reprlist_choices') reprlist_choices.options = tuple([' ']) repr_slider = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_slider') repr_slider.max = 0 repr_name_text = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_name_text') repr_selection = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_selection') repr_name_text.value = ' ' repr_selection.value = ' ' @observe('_repr_dict') def _handle_repr_dict_changed(self, change): if self.player.widget_repr is not None: repr_slider = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_slider') component_slider = widget_utils.get_widget_by_name(self.player.widget_repr, 'component_slider') repr_name_text = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_name_text') repr_selection = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_selection') reprlist_choices = widget_utils.get_widget_by_name(self.player.widget_repr, 'reprlist_choices') repr_names = get_repr_names_from_dict(self._repr_dict, component_slider.value) if change['new'] == {'c0': {}}: repr_selection.value = '' else: reprlist_choices.options = tuple([str(i) + '-' + name for (i, name) in enumerate(repr_names)]) try: reprlist_choices.value = reprlist_choices.options[repr_slider.value] except IndexError: if repr_slider.value == 0: reprlist_choices.options = tuple(['',]) reprlist_choices.value = '' else: reprlist_choices.value = reprlist_choices.options[repr_slider.value-1] # e.g: 0-cartoon repr_name_text.value = reprlist_choices.value.split('-')[-1] repr_slider.max = len(repr_names) - 1 if len(repr_names) >= 1 else len(repr_names) def _update_count(self): self.count = max(traj.n_frames for traj in self._trajlist if hasattr(traj, 'n_frames')) def _wait_until_finished(self, timeout=0.0001): # NGL need to send 'finished' signal to # backend self._event.clear() while True: # idle to make room for waiting for # "finished" event sent from JS time.sleep(timeout) if self._event.is_set(): # if event is set from another thread # break while True break def _run_on_another_thread(self, func, *args): # use `event` to singal # func(*args) thread = threading.Thread(target=func, args=args,) thread.daemon = True thread.start() return thread @observe('loaded') def on_loaded(self, change): # trick for firefox on Linux time.sleep(0.1) if change['new']: self._fire_callbacks(self._ngl_displayed_callbacks_before_loaded) def _fire_callbacks(self, callbacks): def _call(event): for callback in callbacks: callback(self) if callback._method_name == 'loadFile': self._wait_until_finished() self._run_on_another_thread(_call, self._event) def _refresh_render(self): """useful when you update coordinates for a single structure. Notes ----- If you are visualizing a trajectory with more than 1 frame, you can use the player slider to trigger the refreshing. """ current_frame = self.frame self.frame = int(1E6) self.frame = current_frame def sync_view(self): """call this if you want to sync multiple views of a single viewer Note: unstable feature """ self._fire_callbacks(self._ngl_displayed_callbacks_after_loaded) def _ipython_display_(self, **kwargs): super(NGLWidget, self)._ipython_display_(**kwargs) if self._first_time_loaded: self._first_time_loaded = False else: self.sync_view() if self._init_gui: if self._gui is None: self._gui = self.player._display() display(self._gui) if self._theme in ['dark', 'oceans16']: from nglview import theme display(theme.oceans16()) self._remote_call('cleanOutput', target='Widget') self._place_proxy._ipython_display_() def display(self, gui=False, use_box=False): if gui: if use_box: from nglview.widget_box import BoxNGL box = BoxNGL([self, self.player._display()]) box._gui_style = 'row' return box else: display(self) display(self.player._display()) display(self._place_proxy) return None else: return self def _set_place_proxy(self, widget): """ Parameters ---------- widget : instance of ipywidgets.DOMWidget (or derived class) Examples -------- >>> from ipywidgets import IntSlider >>> slider = IntSlider() >>> import nglview >>> view = nglview.demo() >>> view >>> view._set_place_proxy(slider) >>> view._place_proxy >>> # TODO: _place_proxy only has effect (adding widget to NGLWidget) >>> # if it was displayed (should fix) """ self._place_proxy.child = widget def _set_draggable(self, yes=True): if yes: self._remote_call('setDraggable', target='Widget', args=['',]) else: self._remote_call('setDraggable', target='Widget', args=['destroy',]) def _set_sync_frame(self): self._remote_call("setSyncFrame", target="Widget") def _set_unsync_frame(self): self._remote_call("setUnSyncFrame", target="Widget") def _set_sync_camera(self): self._remote_call("setSyncCamera", target="Widget") def _set_unsync_camera(self): self._remote_call("setUnSyncCamera", target="Widget") def _set_delay(self, delay): """unit of millisecond """ self._remote_call("setDelay", target="Widget", args=[delay,]) def _set_spin(self, axis, angle): self._remote_call('setSpin', target='Stage', args=[axis, angle]) def _set_selection(self, selection, component=0, repr_index=0): self._remote_call("setSelection", target='Representation', args=[selection], kwargs=dict(component_index=component, repr_index=repr_index)) def _set_color_by_residue(self, colors, component_index=0, repr_index=0): self._remote_call('setColorByResidue', target='Widget', args=[colors, component_index, repr_index]) def color_by(self, color_scheme, component=0): '''update color for all representations of given component Notes ----- Unstable feature Parameters ---------- color_scheme : str component : int, default 0 component index Examples -------- >>> # component 0 >>> view.color_by('atomindex') >>> # component 1 >>> view.color_by('atomindex', component=1) ''' repr_names = get_repr_names_from_dict(self._repr_dict, component) for index, _ in enumerate(repr_names): self.update_representation(component=component, repr_index=index, color_scheme=color_scheme) @property def representations(self): return self._representations @representations.setter def representations(self, reps): self._representations = reps[:] for index in range(len(self._ngl_component_ids)): self.set_representations(reps) def update_representation(self, component=0, repr_index=0, **parameters): """ Parameters ---------- component : int, default 0 component index repr_index : int, default 0 representation index for given component parameters : dict """ parameters = _camelize_dict(parameters) kwargs = dict(component_index=component, repr_index=repr_index) kwargs.update(parameters) self._remote_call('setParameters', target='Representation', kwargs=kwargs) self._remote_call('requestReprsInfo', target='Widget') def set_representations(self, representations, component=0): """ Parameters ---------- representations : list of dict """ self.clear_representations(component=component) for params in representations: assert isinstance(params, dict), 'params must be a dict' kwargs = params['params'] kwargs.update({'component_index': component}) self._remote_call('addRepresentation', target='compList', args=[params['type'],], kwargs=kwargs) def _remove_representation(self, component=0, repr_index=0): self._remote_call('removeRepresentation', target='Widget', args=[component, repr_index]) def _remove_representations_by_name(self, repr_name, component=0): self._remote_call('removeRepresentationsByName', target='Widget', args=[repr_name, component]) def _update_representations_by_name(self, repr_name, component=0, **kwargs): kwargs = _camelize_dict(kwargs) self._remote_call('updateRepresentationsByName', target='Widget', args=[repr_name, component], kwargs=kwargs) def _display_repr(self, component=0, repr_index=0, name=None): c = 'c' + str(component) r = str(repr_index) try: name = self._repr_dict[c][r]['name'] except KeyError: name = '' return RepresentationControl(self, component, repr_index, name=name) def _set_initial_structure(self, structures): """initialize structures for Widget Parameters ---------- structures : list list of Structure or Trajectory """ _init_structures_sync = structures if isinstance(structures, (list, tuple)) else [structures,] if _init_structures_sync: self._init_structures_sync = [ {"data": _structure.get_structure_string(), "ext": _structure.ext, "params": _structure.params, "id": _structure.id} for _structure in _init_structures_sync] def _set_coordinates(self, index): '''update coordinates for all trajectories at index-th frame ''' if self._trajlist: coordinates_dict = {} for trajectory in self._trajlist: traj_index = self._ngl_component_ids.index(trajectory.id) try: if trajectory.shown: if self.player.interpolate: t = self.player.iparams.get('t', 0.5) step = self.player.iparams.get('step', 1) coordinates_dict[traj_index] = interpolate.linear(index, t=t, traj=trajectory, step=step) else: coordinates_dict[traj_index] = trajectory.get_coordinates(index) else: coordinates_dict[traj_index] = np.empty((0), dtype='f4') except (IndexError, ValueError): coordinates_dict[traj_index] = np.empty((0), dtype='f4') self.coordinates_dict = coordinates_dict else: print("no trajectory available") @property def coordinates_dict(self): """ Returns ------- out : dict of numpy 3D-array, dtype='f4' coordinates of trajectories at current frame """ return self._coordinates_dict @coordinates_dict.setter def coordinates_dict(self, arr_dict): self._coordinates_dict = arr_dict if not self._send_binary: # send base64 encoded_coordinates_dict = dict((k, encode_base64(v)) for (k, v) in self._coordinates_dict.items()) mytime = time.time() * 1000 self.send({'type': 'base64_single', 'data': encoded_coordinates_dict, 'mytime': mytime}) else: # send binary buffers = [] coordinates_meta = dict() for index, arr in self._coordinates_dict.items(): buffers.append(arr.astype('f4').tobytes()) coordinates_meta[index] = index mytime = time.time() * 1000 self.send({'type': 'binary_single', 'data': coordinates_meta, 'mytime': mytime}, buffers=buffers) @observe('frame') def on_frame_changed(self, change): """set and send coordinates at current frame """ self._set_coordinates(self.frame) def clear(self, *args, **kwargs): '''shortcut of `clear_representations` ''' self.clear_representations(*args, **kwargs) def clear_representations(self, component=0): '''clear all representations for given component Parameters ---------- component : int, default 0 (first model) You need to keep track how many components you added. ''' self._remote_call("clearRepresentations", target='compList', kwargs={'component_index': component}) @_update_url def _add_shape(self, shapes, name='shape'): """add shape objects TODO: update doc, caseless shape keyword Parameters ---------- shapes : list of tuple name : str, default 'shape' name of given shape Notes ----- Supported shape: 'mesh', 'sphere', 'ellipsoid', 'cylinder', 'cone', 'arrow'. See also -------- {ngl_url} Examples -------- >>> sphere = ('sphere', [0, 0, 9], [1, 0, 0], 1.5) >>> arrow = ('arrow', [1, 2, 7 ], [30, 3, 3], [1, 0, 1], 1.0) >>> view._add_shape([sphere, arrow], name='my_shape') """ self._remote_call('addShape', target='Widget', args=[name, shapes]) @_update_url def add_representation(self, repr_type, selection='all', **kwargs): '''Add structure representation (cartoon, licorice, ...) for given atom selection. Parameters ---------- repr_type : str type of representation. Please see {ngl_url} for further info. selection : str or 1D array (atom indices) or any iterator that returns integer, default 'all' atom selection **kwargs: additional arguments for representation Example ------- >>> import nglview as nv >>> >>> t = (pt.datafiles.load_dpdp()[:].supej = pt.load(membrane_pdb) trajrpose('@CA')) >>> w = nv.show_pytraj(t) >>> w.add_representation('cartoon', selection='protein', color='blue') >>> w.add_representation('licorice', selection=[3, 8, 9, 11], color='red') >>> w Notes ----- User can also use shortcut >>> w.add_cartoon(selection) # w.add_representation('cartoon', selection) ''' if repr_type == 'surface': if 'useWorker' not in kwargs: kwargs['useWorker'] = False # avoid space sensitivity repr_type = repr_type.strip() # overwrite selection selection = seq_to_string(selection).strip() # make copy kwargs2 = _camelize_dict(kwargs) if 'component' in kwargs2: component = kwargs2.pop('component') else: component = 0 for k, v in kwargs2.items(): try: kwargs2[k] = v.strip() except AttributeError: # e.g.: opacity=0.4 kwargs2[k] = v d = {'params': {'sele': selection}} d['type'] = repr_type d['params'].update(kwargs2) params = d['params'] params.update({'component_index': component}) self._remote_call('addRepresentation', target='compList', args=[d['type'],], kwargs=params) def center(self, *args, **kwargs): """alias of `center_view` """ self.center_view(*args, **kwargs) def center_view(self, zoom=True, selection='*', component=0): """center view for given atom selection Examples -------- view.center_view(selection='1-4') """ self._remote_call('centerView', target='compList', args=[zoom, selection], kwargs={'component_index': component}) @observe('_image_data') def _on_render_image(self, change): '''update image data to widget_image Notes ----- method name might be changed ''' self._widget_image._b64value = change['new'] if self._hold_image: self._image_array.append(change['new']) def render_image(self, frame=None, factor=4, antialias=True, trim=False, transparent=False): """render and get image as ipywidgets.widget_image.Image Parameters ---------- frame : int or None, default None if None, use current frame if specified, use this number. factor : int, default 4 quality of the image, higher is better antialias : bool, default True trim : bool, default False transparent : bool, default False Examples -------- # tell NGL to render send image data to notebook. view.render_image() # make sure to call `get_image` method view.get_image() Notes ----- You need to call `render_image` and `get_image` in different notebook's Cells """ if frame is not None: self.frame = frame params = dict(factor=factor, antialias=antialias, trim=trim, transparent=transparent) self._remote_call('_exportImage', target='Widget', kwargs=params) def download_image(self, filename='screenshot.png', factor=4, antialias=True, trim=False, transparent=False): """render and download scence at current frame Parameters ---------- filename : str, default 'screenshot.png' factor : int, default 4 quality of the image, higher is better antialias : bool, default True trim : bool, default False transparent : bool, default False """ params = dict(factor=factor, antialias=antialias, trim=trim, transparent=transparent) self._remote_call('_downloadImage', target='Widget', args=[filename,], kwargs=params) def _ngl_handle_msg(self, widget, msg, buffers): """store message sent from Javascript. How? use view.on_msg(get_msg) """ self._ngl_msg = msg msg_type = self._ngl_msg.get('type') if msg_type == 'request_frame': self.frame += self.player.step if self.frame >= self.count: self.frame = 0 elif self.frame < 0: self.frame = self.count - 1 elif msg_type == 'repr_parameters': data_dict = self._ngl_msg.get('data') name = data_dict.pop('name') + '\n' selection = data_dict.get('sele', '') + '\n' # json change True to true data_dict_json = json.dumps(data_dict).replace('true', 'True').replace('false', 'False') data_dict_json = data_dict_json.replace('null', '"null"') if self.player.widget_repr is not None: # TODO: refactor repr_name_text = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_name_text') repr_selection = widget_utils.get_widget_by_name(self.player.widget_repr, 'repr_selection') repr_name_text.value = name repr_selection.value = selection elif msg_type == 'request_loaded': if not self.loaded: # trick to trigger observe loaded # so two viewers can have the same representations self.loaded = False self.loaded = msg.get('data') elif msg_type == 'all_reprs_info': self._repr_dict = self._ngl_msg.get('data') elif msg_type == 'stage_parameters': self._full_stage_parameters = msg.get('data') elif msg_type == 'async_message': if msg.get('data') == 'ok': self._event.set() def _request_repr_parameters(self, component=0, repr_index=0): self._remote_call('requestReprParameters', target='Widget', args=[component, repr_index]) def add_structure(self, structure, **kwargs): '''add structure to view Parameters ---------- structure : nglview.Structure object Examples -------- >>> view.add_trajectory(traj0) >>> view.add_trajectory(traj1) >>> # then add Structure >>> view.add_structure(...) See Also -------- nglview.NGLWidget.add_component ''' if not isinstance(structure, Structure): raise ValueError('{} is not an instance of Structure'.format(structure)) if self.loaded or self._already_constructed: self._load_data(structure, **kwargs) else: # update via structure_list self._init_structures.append(structure) name = py_utils.get_name(structure, kwargs) self._ngl_component_names.append(name) self._ngl_component_ids.append(structure.id) self.center_view(component=len(self._ngl_component_ids)-1) self._update_component_auto_completion() def add_trajectory(self, trajectory, **kwargs): '''add new trajectory to `view` Parameters ---------- trajectory: nglview.Trajectory or its derived class or pytraj.Trajectory-like, mdtraj.Trajectory or MDAnalysis objects See Also -------- nglview.NGLWidget.add_component Examples -------- >>> import nglview as nv, pytraj as pt >>> traj = pt.load(nv.datafiles.TRR, nv.datafiles.PDB) >>> view = nv.show_pytraj(view) >>> # show view first >>> view >>> # add new Trajectory >>> traj2 = pt.datafiles.load_tz2() >>> view.add_trajectory(traj2) ''' backends = BACKENDS package_name = trajectory.__module__.split('.')[0] if package_name in backends: trajectory = backends[package_name](trajectory) else: trajectory = trajectory if self.loaded or self._already_constructed: self._load_data(trajectory, **kwargs) else: # update via _init_structures_sync self._init_structures.append(trajectory) name = py_utils.get_name(trajectory, kwargs) self._ngl_component_names.append(name) setattr(trajectory, 'shown', True) self._trajlist.append(trajectory) self._update_count() self._ngl_component_ids.append(trajectory.id) self._update_component_auto_completion() def add_pdbid(self, pdbid): '''add new Structure view by fetching pdb id from rcsb Examples -------- >>> view = nglview.NGLWidget() >>> view.add_pdbid('1tsu') >>> # which is equal to >>> # view.add_component('rcsb://1tsu.pdb') ''' self.add_component('rcsb://{}.pdb'.format(pdbid)) def add_component(self, filename, **kwargs): '''add component from file/trajectory/struture Parameters ---------- filename : str or Trajectory or Structure or their derived class or url **kwargs : additional arguments, optional Examples -------- >>> view = nglview.Widget() >>> view >>> view.add_component(filename) Notes ----- If you want to load binary file such as density data, mmtf format, it is faster to load file from current or subfolder. ''' self._load_data(filename, **kwargs) # assign an ID self._ngl_component_ids.append(str(uuid.uuid4())) self._update_component_auto_completion() def _load_data(self, obj, **kwargs): ''' Parameters ---------- obj : nglview.Structure or any object having 'get_structure_string' method or string buffer (open(fn).read()) ''' kwargs2 = _camelize_dict(kwargs) try: is_url = FileManager(obj).is_url except NameError: is_url = False if 'defaultRepresentation' not in kwargs2: kwargs2['defaultRepresentation'] = True if not is_url: if hasattr(obj, 'get_structure_string'): blob = obj.get_structure_string() kwargs2['ext'] = obj.ext passing_buffer = True binary = False else: fh = FileManager(obj, ext=kwargs.get('ext'), compressed=kwargs.get('compressed')) # assume passing string blob = fh.read() passing_buffer = not fh.use_filename if fh.ext is None and passing_buffer: raise ValueError('must provide extension') kwargs2['ext'] = fh.ext binary = fh.is_binary use_filename = fh.use_filename if binary and not use_filename: # send base64 blob = base64.b64encode(blob).decode('utf8') blob_type = 'blob' if passing_buffer else 'path' args=[{'type': blob_type, 'data': blob, 'binary': binary}] else: # is_url blob_type = 'url' url = obj args=[{'type': blob_type, 'data': url, 'binary': False}] name = py_utils.get_name(obj, kwargs2) self._ngl_component_names.append(name) self._remote_call("loadFile", target='Stage', args=args, kwargs=kwargs2) def remove_component(self, component_id): """remove component by its uuid Examples -------- >>> view.add_trajectory(traj0) >>> view.add_trajectory(traj1) >>> view.add_struture(structure) >>> # remove last component >>> view.remove_component(view._ngl_component_ids[-1]) """ self._clear_component_auto_completion() if self._trajlist: for traj in self._trajlist: if traj.id == component_id: self._trajlist.remove(traj) component_index = self._ngl_component_ids.index(component_id) self._ngl_component_ids.remove(component_id) self._ngl_component_names.pop(component_index) self._remote_call('removeComponent', target='Stage', args=[component_index,]) self._update_component_auto_completion() def _remote_call(self, method_name, target='Widget', args=None, kwargs=None): """call NGL's methods from Python. Parameters ---------- method_name : str target : str, {'Stage', 'Viewer', 'compList', 'StructureComponent'} args : list kwargs : dict if target is 'compList', "component_index" could be passed to specify which component will call the method. Examples -------- view._remote_call('loadFile', args=['1L2Y.pdb'], target='Stage', kwargs={'defaultRepresentation': True}) # perform centerView for 1-th component # component = Stage.compList[1]; # component.centerView(true, "1-12"); view._remote_call('centerView', target='component', args=[True, "1-12"], kwargs={'component_index': 1}) """ args = [] if args is None else args kwargs = {} if kwargs is None else kwargs msg = {} if 'component_index' in kwargs: msg['component_index'] = kwargs.pop('component_index') if 'repr_index' in kwargs: msg['repr_index'] = kwargs.pop('repr_index') msg['target'] = target msg['type'] = 'call_method' msg['methodName'] = method_name msg['args'] = args msg['kwargs'] = kwargs def callback(widget, msg=msg): widget.send(msg) callback._method_name = method_name if self.loaded: self._remote_call_thread.q.append(callback) else: # send later # all callbacks will be called right after widget is loaded self._ngl_displayed_callbacks_before_loaded.append(callback) self._ngl_displayed_callbacks_after_loaded.append(callback) def _get_traj_by_id(self, itsid): """return nglview.Trajectory or its derived class object """ for traj in self._trajlist: if traj.id == itsid: return traj return None def hide(self, indices): """set invisibility for given component/struture/trajectory (by their indices) """ traj_ids = set(traj.id for traj in self._trajlist) for index in indices: comp_id = self._ngl_component_ids[index] if comp_id in traj_ids: traj = self._get_traj_by_id(comp_id) traj.shown = False self._remote_call("setVisibility", target='compList', args=[False,], kwargs={'component_index': index}) def show(self, **kwargs): """shortcut of `show_only` """ self.show_only(**kwargs) def show_only(self, indices='all'): """set visibility for given components (by their indices) Parameters ---------- indices : {'all', array-like}, component index, default 'all' """ traj_ids = set(traj.id for traj in self._trajlist) if indices == 'all': indices_ = set(range(self.n_components)) else: indices_ = set(indices) for index, comp_id in enumerate(self._ngl_component_ids): if comp_id in traj_ids: traj = self._get_traj_by_id(comp_id) else: traj = None if index in indices_: args = [True,] if traj is not None: traj.shown = True else: args = [False,] if traj is not None: traj.shown = False self._remote_call("setVisibility", target='compList', args=args, kwargs={'component_index': index}) def _js_console(self): self.send(dict(type='get', data='any')) def _get_full_params(self): self.send(dict(type='get', data='parameters')) def _display_image(self): '''for testing ''' from IPython import display return display.Image(self._image_data) def _clear_component_auto_completion(self): for index, _ in enumerate(self._ngl_component_ids): name = 'component_' + str(index) delattr(self, name) def _update_component_auto_completion(self): trajids = [traj.id for traj in self._trajlist] for index, cid in enumerate(self._ngl_component_ids): comp = ComponentViewer(self, index) name = 'component_' + str(index) setattr(self, name, comp) if cid in trajids: traj_name = 'trajectory_' + str(trajids.index(cid)) setattr(self, traj_name, comp) def __getitem__(self, index): """return ComponentViewer """ postive_index = py_utils.get_positive_index(index, len(self._ngl_component_ids)) return ComponentViewer(self, postive_index) def __iter__(self): """return ComponentViewer """ for i, _ in enumerate(self._ngl_component_ids): yield self[i] def detach(self, split=False): """detach player from its original container. Parameters ---------- split : bool, default False if True, resize notebook then move it to the right of its container """ if not self.loaded: raise RuntimeError("must display view first") # resize notebook first # width of the dialog will be calculated based on notebook container offset if split: # rename js_utils._move_notebook_to_the_right() self._remote_call('setDialog', target='Widget')