diff --git a/.coveragerc b/.coveragerc index 101904c..828b6ef 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,2 +1,3 @@ [run] branch = true + diff --git a/devtools/conda-recipe/meta.yaml b/devtools/conda-recipe/meta.yaml index 2ca1123..a49d1b2 100644 --- a/devtools/conda-recipe/meta.yaml +++ b/devtools/conda-recipe/meta.yaml @@ -26,16 +26,23 @@ requirements: test: source_files: - .coveragerc + - molpx/notebooks/* requires: - pip - - pytest - - pytest-cov + - pytest ==3.2.5 + - pytest-cov ==2.5.1 + - coverage ==4.4.2 +# - nbval ==0.7 imports: - molpx commands: - - pip install git+https://github.com/computationalmodelling/nbval + - pip install git+https://github.com/computationalmodelling/nbval@0.9 - pytest -vv --pyargs molpx --cov=molpx --cov-report=xml --current-env --nbval-lax - - python -c "import shutil, os; shutil.copy('coverage.xml', os.path.expanduser('~'))" + #- pytest -vv --pyargs molpx --cov=molpx --current-env --cov-report=xml + #- ls -R + #- pwd + #- pytest -vv --current-env --nbval-lax + - python -c "import shutil, os; shutil.copy('coverage.xml', os.path.expanduser('~'))" || true about: home: https://github.com/gph82/projection_explorer license: GNU Lesser Public License v3+ diff --git a/doc/source/INSTALL.rst b/doc/source/INSTALL.rst index 6ee4527..6a6902c 100644 --- a/doc/source/INSTALL.rst +++ b/doc/source/INSTALL.rst @@ -7,7 +7,7 @@ available in their required versions, the installation will fail. We recommend o that is relatively safe, but you are welcome to try another approaches if you know what you are doing. -Anaconda install (Recommended) +Anaconda Install (recommended) ============================== We strongly recommend to use the Anaconda scientific python distribution in order to install @@ -18,8 +18,7 @@ but play at your own risk. If you already have a conda installation, directly go to step 3: -1. Download and install miniconda for Python 2.7 or 3+, 32 or 64 bit depending on your system. Note that - you can still use Python 2.7, however we recommend to use Python3: +1. Download and install miniconda for Python 3+, 32 or 64 bit depending on your system. http://conda.pydata.org/miniconda.html @@ -35,27 +34,33 @@ If you already have a conda installation, directly go to step 3: 2. If you have installed from a Linux shell, either open a new shell to have an updated PATH, or update your PATH variable by ``source ~/.bashrc`` (or .tcsh, .csh - whichever shell you are using). -3. Add the omnia-md software channel, and install (or update) molPX: +3. Install molPX using the conda-forge channel: - .. code:: + :: - conda config --add channels omnia - conda install pyemma + conda install molpx -c conda-forge - if the command conda is unknown, the PATH variable is probably not set correctly (see 1. and 2.) + if the command ``conda`` is unknown, the PATH variable is probably not set correctly (see 1. and 2.) 4. Check installation: - .. code:: + :: conda list - shows you the installed python packages. You should find a molpx 0.1.2 (or later) - and ipython, ipython-notebook 3.1 (or later). If ipython is not up to date, you canot use molPX. Please update it by + shows you the installed python packages. You should find a molpx 0.1.2 (or later). + molPX requires all the following packages, s. t. they will be installed (and their dependencies) if they + are not installed already. - .. code:: + :: - conda install ipython-notebook + nglview>=1 + ipywidgets>=7 + pyemma + scikit-learn + notebook + mdtraj + ipympl Python Package Index (PyPI) =========================== @@ -116,7 +121,23 @@ but be aware that success is not guaranteed. See the "Known Issues" below. Known Issues ============= - * A ``SandboxViolation`` error might appear when installing from source. Until we figure this out, + * After a successfull installation and execution of ``molpx.example_notebooks()``...no widgets are shown! Most probably, this has to do with `nglview `_ and its needed Jupyter notebook extensions. + **This is a known, frustrating behaviour:** + + * `deja vu: nglview widget does not appear `_ + * `nglview widget does not appear `_ + * `Troubleshooting: nglviewer fresh install tips `_ + + We'are aware of this and molPX automatically checks for the needed extensions every time it gets imported, s.t. it will refuse + to start-up if it finds any problems. If somehow it manages to start-up but no widget is shown, try issuing + :: + + molpx._auto_enable_extensions() + + restarting everything and trying again. Otherwise, check the above links. + + * A ``SandboxViolation`` error might appear when installing from source. This is because the ``nglview`` dependency + is trying to enable the needed extensions. Until we figure this out, try to install ``nglview`` externally issuing: @@ -127,5 +148,3 @@ Known Issues >>> pip install nglview * Note that molPX only works with ``nglview`` versions >=0.6.2.1. - - * The interplay between some modules (nglview, nbextensions, ipywidgets) might limit you to use python3.X on some platforms. Sorry about that. \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index d95ea9d..bae7e5f 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -380,8 +380,9 @@ def __getattribute__(self, item): # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'https://docs.python.org/': None, intersphinx_mapping = {'pyemma': ('http://www.emma-project.org/latest/', None), + 'matplotlib': ('http://matplotlib.org/', None), 'mdtraj': ('http://mdtraj.org/latest/', None), - 'pylab' : ('http://matplotlib.org/api/', None), + #'pylab' : ('http://matplotlib.org/api/', None), 'nglview' : ('http://arose.github.io/nglview/latest/', None) } diff --git a/doc/source/index_visualize.rst b/doc/source/index_visualize.rst index 1be13c3..3ee71a4 100644 --- a/doc/source/index_visualize.rst +++ b/doc/source/index_visualize.rst @@ -10,10 +10,10 @@ The core functionality is to link two interative figures, *fig1* and *fig2*, ins so that an action in *fig1* (e.g.a click of the mouse or a slide of a slidebar) will trigger an event in *fig2* (e.g. a frame update or point moved) and vice versa. Usually, these two figures contain representations from: -* **molecules**: an `nglviewer `_ widget showing one (or more) molecular structure(s) that a particular value of the coordinate(s) is associated with and +* **molecules**: an `NGLview `_ widget showing one (or more) molecular structure(s) that a particular value of the coordinate(s) is associated with and * **projected coordinates**: a matplotlib figure showing the projected coordinates (e.g. TICs or PCs or any other), :math:`{Y_0, ..., Y_N}`, either as a 2D histogram, :math:`PDF(Y_i, Y_j)` or as trajectory views :math:`{Y_0(t), ...Y_N(t)}` -You are **strongly encouraged** to check nglview's `documentation `_, since its functionalities extend beyond the scope of this package and the molecular visualization universe is rich and complex (unlike this module). +You are **strongly encouraged** to check NGLview's `documentation `_, since its functionalities extend beyond the scope of this package and the molecular visualization universe is rich and complex (unlike this module). The methods offered by this module are: diff --git a/molpx/__init__.py b/molpx/__init__.py index 3bc05cf..401951c 100644 --- a/molpx/__init__.py +++ b/molpx/__init__.py @@ -132,6 +132,11 @@ def _get_extension_status(ext_list=_ext_mapping.keys()): return enabled_exts +def _auto_enable_extensions(): + r"""Try to automatically enable the needed extensions. Won't throw exception on failuer""" + for _ext, _path in _ext_mapping.items(): + _enable_extensions(_ext_mapping[_ext]) + def _enable_extensions(this_ext_path): r""" Try to install/enable an extension. Prompt the user to do so if an exception is thrown @@ -155,8 +160,10 @@ def _enable_extensions(this_ext_path): # Try to help the user getting molpx working out of the box and raise an Exception if molpx wont work if not all(_get_extension_status().values()): - for ext, enabled in _get_extension_status().items(): - if not enabled: - if not _enable_extensions(_ext_mapping[ext]): + for _ext, _enabled in _get_extension_status().items(): + if not _enabled: + if not _enable_extensions(_ext_mapping[_ext]): raise ModuleNotFoundError("Could not initialize molpx") + + diff --git a/molpx/_bmutils.py b/molpx/_bmutils.py index ab770b1..fe7a810 100644 --- a/molpx/_bmutils.py +++ b/molpx/_bmutils.py @@ -1,4 +1,3 @@ -from __future__ import print_function import numpy as _np import mdtraj as _md diff --git a/molpx/_linkutils.py b/molpx/_linkutils.py index e9b5b1b..f37a763 100644 --- a/molpx/_linkutils.py +++ b/molpx/_linkutils.py @@ -1,4 +1,3 @@ -from __future__ import print_function import numpy as _np from matplotlib.widgets import AxesWidget as _AxesWidget diff --git a/molpx/_nbtools.py b/molpx/_nbtools.py index 9562499..b056c4e 100644 --- a/molpx/_nbtools.py +++ b/molpx/_nbtools.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import warnings as _warnings import os as _os import sys as _sys diff --git a/molpx/generate.py b/molpx/generate.py index 2fbe812..0314714 100644 --- a/molpx/generate.py +++ b/molpx/generate.py @@ -1,4 +1,3 @@ -from __future__ import print_function __author__ = 'gph82' diff --git a/molpx/visualize.py b/molpx/visualize.py index ab9d5c0..222460a 100644 --- a/molpx/visualize.py +++ b/molpx/visualize.py @@ -1,4 +1,3 @@ -from __future__ import print_function __author__ = 'gph82' @@ -11,7 +10,7 @@ from matplotlib.cm import get_cmap as _get_cmap from matplotlib.colors import rgb2hex as _rgb2hex, to_hex as _to_hex -from . import generate +from . import generate as _generate from . import _bmutils from . import _linkutils @@ -86,8 +85,7 @@ def FES(MD_trajectories, MD_top, projected_trajectories, ---------- MD_trajectories : str, or list of strings with the filename(s) the the molecular dynamics (MD) trajectories. - Any file extension that :py:obj:`mdtraj` (.xtc, .dcd etc) can read is accepted. - + Any file extension that :obj:`mdtraj` (.xtc, .dcd, etc) can read is accepted. Alternatively, a single :obj:`mdtraj.Trajectory` object or a list of them can be given as input. MD_top : str to topology filename or directly an :obj:`mdtraj.Topology` object @@ -105,7 +103,7 @@ def FES(MD_trajectories, MD_top, projected_trajectories, n_sample : int, default is 100 The number of geometries that will be used to represent the FES. The higher the number, the higher the spatial - resolution of the "click"-action. + resolution of the "click"-action. (And the longer it will take to generate the plot) proj_stride : int, default is 1 Stride value that was used in the :obj:`projected_trajectories` relative to the :obj:`MD_trajectories` @@ -114,13 +112,14 @@ def FES(MD_trajectories, MD_top, projected_trajectories, the user that the :obj:`MD_trajectories` and the :obj:`projected_trajectories` have different number of frames. weights : iterable of floats (or list thereof) each of shape (n_frames, 1) or (n_frames) - The sample weights, typically coming from a metadynamics run. Has to have the same length - as the :py:obj:`projected_trajectories` argument. - - proj_labels : either string or list of strings or (experimental PyEMMA featurizer) - The projection plots will get this paramter for labeling their yaxis. If a str is - provided, that will be the base name proj_labels='%s_%u'%(proj_labels,ii) for each - projection. If a list, the list will be used. If not enough labels are there + The sample weights can come from a metadynamics run or an MSM-object, e.g. + via the method :obj:`pyemma.msm.BayesianMSM.trajectory_weights`. + Has to have the same length as the :py:obj:`projected_trajectories` + + proj_labels : either string or list of strings or (experimental) PyEMMA featurizer + The projection plots will get this parameter for labeling their yaxis. If a str is + provided, that will be the base name ``proj_labels='%s_%u'%(proj_labels,ii)`` for each + projection. If :obj:`proj_labels` is a list, the list will be used as is. If there are not enough labels, the module will complain n_overlays : int, default is 1 @@ -129,31 +128,42 @@ def FES(MD_trajectories, MD_top, projected_trajectories, atom_selection : string or iterable of integers, default is None The geometries of the original trajectory files will be filtered down to these atoms. It can be any DSL string - that :obj:`mdtraj.Topology.select` could understand or directly the iterable of integers. - If :py:obj`MD_trajectories` is already a (list of) md.Trajectory objects, the atom-slicing can take place - before calling this method. + that :obj:`mdtraj.Topology.select` could understand or directly the iterable of integers. + If :obj:`MD_trajectories` is already a (list of) :obj:`mdtraj.Trajectory` objects, the atom-slicing can be + done by the user place before calling this method. sample_kwargs : dictionary of named arguments, optional - named arguments for the function :obj:`visualize.sample`. Non-expert users can safely ignore this option. Examples - are superpose or proj_ + named arguments for the function :obj:`molpx.visualize.sample`. Non-expert users can safely ignore this option. Examples + are :obj:`superpose` or :obj:`proj_idxs` Returns - -------- + ------- - widgetbox: - :obj:`ipywidgets.HBox` containing both the NGLWidget (ngl_wdg) and the interactive figure. It also - contains the extra attributes - # TODO reshape this docstring - ax : - :obj:`pylab.Axis` object - fig : - :obj:`pylab.Figure` object - ngl_wdg : - :obj:`nglview.NGLWidget` - data_sample: - numpy ndarray of shape (n, n_sample) with the position of the dots in the plot - geoms: - :obj:`mdtraj.Trajectory` object with the geometries n_sample geometries shown by the ngl_wdg + widgetbox : + A :obj:`molpx._linkutils.MolPXHBox`-object. + + It contains the :obj:`~nglview.NGLWidget` and the :obj:`~matplotlib.widgets.AxesWidget` (which is + responsible for the interactive figure). It is child-class of the :obj:`ipywidgets.HBox`-class and + has been monkey-patched to have the following extra attributes so that the user has access to all the + information being displayed. + + linked_axes : + list with all the :obj:`pylab.Axis`-objects contained in the :obj:`widgetbox` + + linked_ax_wdgs : + list with all the :obj:`matplotlib.widgets.AxesWidget`objects contained in the :obj:`widgetbox` + + linked_figs : + list with all the :obj:`pylab.Figure`-objects contained in the :obj:`widgetbox` + + linked_ngl_wdgs : + list with all the :obj:`nglview.NGLWidget`-objects contained in the :obj:`widgetbox` + + linked_data_arrays : + list with all the numpy ndarrays contained in the :obj:`widgetbox` + + linked_mdgeoms: + list with all the :obj:`mdtraj.Trajectory`-objects contained in the :obj:`widgetbox` """ @@ -167,7 +177,7 @@ def FES(MD_trajectories, MD_top, projected_trajectories, # Prepare for 1D case proj_idxs = _bmutils.listify_if_int(proj_idxs) - data_sample, geoms, data = generate.sample(MD_trajectories, MD_top, projected_trajectories, + data_sample, geoms, data = _generate.sample(MD_trajectories, MD_top, projected_trajectories, atom_selection=atom_selection, proj_idxs=proj_idxs, n_points=n_sample, @@ -625,17 +635,21 @@ def correlations(correlation_input, Parameters --------- - correlation_input : anything - Something that could, in principle, be a :obj:`pyemma.coordinates.transformer`, - like a TICA, PCA object or directly a correlation matrix, with a row for each feature and a column - for each projection, very much like the :obj:`feature_TIC_correlation` of the TICA object of pyemma. + correlation_input : numpy ndarray or some PyEMMA objects + + if array : + (m,m) correlation matrix, with a row for each feature and a column for each projection + + if PyEMMA-object : + :obj:`~pyemma.coordinates.transform.TICA`, :obj:`~pyemma.coordinates.transform.PCA` or + :obj:`~pyemma.coordinates.data.featurization.featurizer.MDFeaturizer`. geoms : None or :obj:`mdtraj.Trajectory`, default is None The values of the most correlated features will be returned for the geometries in this object. If widget is left to its default, None, :obj:`correlations` will create a new widget and try to show the most correlated features on top of the widget. - widget : None or :obj:`nglview NGLWidget` + widget : None or :obj:`nglview.NGLWidget`, default is None Provide an already existing widget to visualize the correlations on top of. This is only for expert use, because no checks are done to see if :obj:`correlation_input` and the geometry contained in the widget **actually match**. Use with caution. @@ -653,23 +667,25 @@ def correlations(correlation_input, projection specific list of colors to provide the representations with. The default None yields blue. In principle, the list can contain one color for each projection (= as many colors as len(proj_idxs) but if your list is short it will just default to the last color. This way, proj_color_list=['black'] will paint - all black regardless len(proj_idxs) + all black regardless len(proj_idxs). Input anything that yields :obj:`matplotlib.colors.is_color_like` == True proj_idxs : None, or int, or iterable of integers, default is None - The indices of the projections for which the most correlated feture will be returned + The indices of the projections for which the most correlated feture will be returned. If none it will default to the dimension of the correlation_input object feat_name : None or str, default is None The prefix with which to prepend the labels of the most correlated features. If left to None, the feature - description found in :obj:`correlation_input` will be used (if available) + description found in :obj:`correlation_input` will be used, if available n_feats : int, default is 1 - Number of argmax correlation to return for each feature. + Number of most correlated features to return for each projection - featurizer : optional featurizer, default is None - If :obj:`correlation_input` is not an :obj:`_MDFeautrizer` itself or doesn't have a - data_producer.featurizer attribute, the user can input one here. If both an _MDfeaturizer *and* an :obj:`featurizer` - are provided, the latter will be ignored. + featurizer : None or :obj:`~pyemma.coordinates.data.featurization.featurizer.MDFeaturizer`, default is None + If :obj:`correlation_input` is not an :obj:`~pyemma.coordinates.data.featurization.featurizer.MDFeaturizer` + itself, or doesn't have a :obj:`~pyemma.coordinates.transform.TICA.data_producer` attribute, the user can input one here. + If :obj:`correlation_input` and :obj:`featurizer` + **are both** :obj:`~pyemma.coordinates.data.featurization.featurizer.MDFeaturizer`-objects, + :obj:`featurizer` will be ignored. verbose : Bool, default is True print to standard output @@ -678,8 +694,8 @@ def correlations(correlation_input, ------- corr_dict and ngl_wdg - corr_dict: - A dictionary with items: + corr_dict : + Dictionary with items: idxs : List of length len(proj_idxs) with lists of length n_feat with the idxs of the most correlated features @@ -694,17 +710,19 @@ def correlations(correlation_input, feats : If an :obj:`mdtraj.Trajectory` is passed as an :obj:`geom` argument, the most correlated features will - be evaluated for that geom and returned as list of length len(proj_idxs) with arrays with shape + be evaluated and returned as list of length len(:obj:`proj_idxs`). Each element in th elist + is an arrays with shape (:obj:`geom.n_frames`, :obj:`n_feats`) atom_idxs : List of length len(proj_idxs) each with an nd.array of shape (nfeat, m), where m is the number of atoms needed to describe each feature (1 of cartesian, 2 for distances, 3 for angles, 4 for dihedrals) info : - List of length len(proj_idxs) with lists of length n_feat with strings describing the correlations + List of length len(:obj:`proj_idxs`) with lists of length :obj:`n_feat` with strings describing the correlations - widget : - obj:`nglview.NGLwidget` with the correlations visualized on top of it + ngl_wdg : + :obj:`nglview.NGLWidget` with the most correlated features (distances, angles, dihedrals, positions) + visualized on top of it. """ # todo consider kwargs for most_corr @@ -827,70 +845,82 @@ def sample(positions, geom, ax, Parameters ---------- positions : numpy nd.array of shape (n_frames, 2) - Contains the position associated with each frame in :obj:`geom` in that order + Contains the position associated the geometries in :obj:`geom`. See below for more details - geom : :obj:`mdtraj.Trajectory` objects or a list thereof. - The geometries associated with the the :obj:`positions`. Hence, all have to have the same number of n_frames + geom : :obj:`mdtraj.Trajectory` object or a list thereof. + The geometries associated with the the :obj:`positions`. + # TODO: WRITE HOW THE LISTS-LENGTHS CORRESPONDS FOR THE STICKY OPTION - ax : matplotlib.pyplot.Axes object - The axes to be linked with the nglviewer ngl_wdg + ax : :obj:`matplotlib.pyplot.Axes` object + The axes to be linked with the :obj:`~nglview.NGLWidget` plot_path : bool, default is False whether to draw a line connecting the positions in :obj:`positions` clear_lines : bool, default is True - whether to clear all the lines that were previously drawn in :obj:`ax` + whether to clear all the 2D objects that were previously drawn in :obj:`ax` n_smooth : int, default is 0, - if n_smooth > 0, the shown geometries and paths will be smoothed out by 2*n frames. + if :obj:`n_smooth` > 0, the shown geometries and paths will be smoothed out by 2* :obj:`n_smooth` +1. See :obj:`molpx._bmutils.smooth_geom` for more information - ngl_wdg : None or existing nglview ngl_wdg - you can provide an already instantiated nglviewer ngl_wdg here (avanced use) + ngl_wdg : None or an existing :obj:`~nglview.NGLWidget`, default is None + you can provide an already instantiated :obj:`~nglview.NGLWidget` here (avanced use) superpose : boolean, default is True - The geometries in :obj:`geom` may or may not be oriented, depending on where they were generated. + The geometries in :obj:`geom` may or may not be oriented, depending on how they were generated. Since this method is mostly for visualization purposes, the default behaviour is to orient them all to maximally overlap with the most compact frame available - projection : object that generated the projection, default is None - The projected coordinates may come from a variety of sources. When working with :obj:`pyemma` a number of objects + projection : object that generated the :obj:`positions`, default is None + The projected coordinates may come from a variety of sources. When working with PyEMMA, a number of objects might have generated this projection, like a - * :obj:`pyemma.coordinates.transform.TICA` or a - * :obj:`pyemma.coordinates.transform.PCA` or a - Expert use. Pass this object along ONLY if the :obj:`positions` have been generetaed using :any:`projection_paths`, - so that looking at linear correlations makes sense. Observe the features that are most correlated with the projections - will be plotted for the sample, allowing the user to establish a visual connection between the - projected coordinate and the original features (distances, angles, contacts etc) + * :obj:`~pyemma.coordinates.transform.TICA`- or a + * :obj:`~pyemma.coordinates.transform.PCA`- or an + * :obj:`~pyemma.coordinates.data.featurization.featurizer.MDFeaturizer`-object. + + Makes most sense when :obj:`positions` where generated with :obj:`molpx.generate.projection_paths`, + otherwise might produce rubbish. See :obj:`n_feats` for more info. + # TODO: delete from here below? + The features most correlated with the :obj:`positions` will be shown + in the widget + # TODO CHECK THIS + geometries in :obj:`geom`, allowing the user to establish a visual connection between the + projected coordinate and the original features (distances, angles, contacts etc). n_feats : int, default is 1 - If a :obj:`projection` is passed along, the first n_feats features that most correlate the - the projected trajectories will be represented, both in form of trajectories feat vs t as well as in - the ngl_wdg. If :obj:`projection` is None, :obj:`nfeats` will be ignored. + If a :obj:`projection` is passed along, the first :obj:`n_feats` features that most correlate the + the projected trajectories will be represented, both in form of figures of feat(t) as well as in + the on top of the :obj:`ngl_wdg`. If :obj:`projection == None`, :obj:`nfeats` will be ignored. - sticky : boolean, default is False, - If set to True, the ngl_wdg the generated visualizations will be sticky in that they do not disappear with - the next click event. Particularly useful for represeting more minima simultaneously. + sticky : boolean, default is False + If set to True, the :obj:`ngl_wdg` will be *sticky* in that every click adds a new molecular + structure without deleting the previous one. Left-clicks adds a structure, right-clicks deletes + a structure. Particularly useful for representing more minima simultaneously. - color_list : None or list of len(pos) + color_list : None, list of len(:obj:`positions`), or "random" The colors with which the sticky frames will be plotted. - Can by anything that yields matplotlib.colors.is_color_like == True + A color is anything that yields :obj:`matplotlib.colors.is_color_like == True` + "None" defaults to coloring by element. + "random" randomizes the color choice - list_of_repr_dicts : None or list of dictionaries having at least keys 'repr_type' and 'selection' keys. - Other **kwargs are currently ignored but will be implemented in the future (see nglview.add_representation - for more info). Only active for sticky widgets + list_of_repr_dicts : None or list of dictionaries, default is None + Has an effect only for :obj:`sticky == True`, s.t. these reps instead of the default + ones are used. The dictionaries must have at least the keys 'repr_type' and 'selection'. + Other key-value pairs are currently ignored but, will be implemented in the future. + See :obj:`nglview.NGLWidget.add_representation` for more info). link_ax2wdg_kwargs: dictionary of named arguments, optional - named arguments for the function :obj:`_link_ax_w_pos_2_nglwidget`, which is the one that internally + named arguments for the function :obj:`molpx._linkutils.link_ax_w_pos_2_nglwidget`, which is the one that internally provides the interactivity. Non-expert users can safely ignore this option. Returns -------- - ngl_wdg : :obj:`nglview.NGLWidget` + ngl_wdg : :obj:`~nglview.NGLWidget` - axes_wdg: obj:`matplotlib.Axes.AxesWidget` + axes_wdg: :obj:`~matplotlib.Axes.AxesWidget` """