diff --git a/README.md b/README.md
index d9b006e..fe348db 100644
--- a/README.md
+++ b/README.md
@@ -2,46 +2,92 @@ notebook-molecular-visualization
===============================
[](https://badge.fury.io/py/nbmolviz)
-A Python widgets library for 2D and 3D molecular visualization in Jupyter notebooks
+Jupyter notebook add-ons for the [Molecular Design Toolkit](https://github.com/Autodesk/molecular-design-toolkit). NBMolViz provides visualization and interactivity for 3D Molecular Structures in Jupyter notebooks.
+
+After installing it, you'll never need to use the NBMolViz package directly. It's instead called through MDT to provide enhanced functionality in notebooks.
## Installation
+When you install `nbmolviz`, you'll need to both install the python library _and_ enable the notebook extensions.
+1. **Install the python library:**
+```bash
$ pip install nbmolviz
- $ jupyter nbextension enable --python --system nbmolviz
- $ jupyter nbextension enable --python --system widgetsnbextension
+```
+
+2. **Activate notebook extensions:**
+To enable for your user account:
+```bash
+ $ python -m nbmolviz activate --user
+```
+
+To enable within your current virtual environment:
+```bash
+ $ python -m nbmolviz activate --sys-prefix
+```
+To globally enable for all users (use with caution! This may require `sudo`):
+```bash
+ $ python -m nbmolviz activate --global
+```
+
+## Upgrading from older versions
+
+1. **Upgrade the library to the newest version**
+ $ pip install --upgrade nbmolviz
+
+2. **Remove old notebook extensions (you will be notified if it's necessary to run with `sudo`)**:
+```bash
+ $ python -m nbmolviz clean-all
+```
## Examples
-To draw an OpenBabel molecule:
+Draw a small molecule:
+```python
+import moldesign as mdt
+mol = mdt.from_name('ethylene')
+mol.draw()
+```
+
+
+Draw a protein:
```python
-import nbmolviz
-import pybel
-benzene = pybel.read_string('smi','c1cccc1').next()
-nbmolviz.visualize(benzene)
+import moldesign as mdt
+mol = mdt.from_pdb('3aid')
+mol.draw()
```
+
+Interactively select atoms (the currently selected atoms will be available as `selector.selected_atoms`)
+```python
+import moldesign as mdt
+mol = mdt.from_pdb('3aid')
+selector = mdt.widgets.ResidueSelector(mol)
+selector
+```
+
+
## Dev install
Requires npm.
- $ git clone https://github.com/autodesk/notebook-molecular-visualization.git
+ $ git clone https://github.com/autodesk/notebook-molecular-visualization
$ cd notebook-molecular-visualization
+ # ./set_filters.sh # tells git to clean up notebooks before committing
$ python setup.py jsdeps
$ pip install -e .
$ jupyter nbextension install --py --symlink --user nbmolviz
$ jupyter nbextension enable --py --user nbmolviz
+ $ cd tests/galileo && npm install
This will build your widgets into a folder at `notebook-molecular-visualization/nbmolviz/static`
During development, to see the effects of changes to any javascript files (in notebook-molecular/visualization/js/src), run `python setup.py jsdeps` and reload any notebook browser windows.
-## Tests
-Run tests with:
-
- pytest nbmolviz/_tests
+## To run visual tests
+`cd tests/nb && ../galileo/bin/galileo --launchnb`
## Releasing a new version
Travis automatically releases commits that are tagged, so to trigger a new release, just do:
diff --git a/img/protein.png b/img/protein.png
new file mode 100644
index 0000000..6a78f9f
Binary files /dev/null and b/img/protein.png differ
diff --git a/img/selector.png b/img/selector.png
new file mode 100644
index 0000000..305b57a
Binary files /dev/null and b/img/selector.png differ
diff --git a/img/smallmol.png b/img/smallmol.png
new file mode 100644
index 0000000..abdc336
Binary files /dev/null and b/img/smallmol.png differ
diff --git a/nbmolviz/__main__.py b/nbmolviz/__main__.py
new file mode 100644
index 0000000..4ab9971
--- /dev/null
+++ b/nbmolviz/__main__.py
@@ -0,0 +1,107 @@
+from __future__ import print_function, absolute_import, division
+from future.builtins import *
+from future import standard_library
+standard_library.install_aliases()
+
+# Copyright 2017 Autodesk Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+import argparse
+
+class SmartFormatter(argparse.HelpFormatter):
+ """
+ From https://stackoverflow.com/a/22157136/1958900
+ """
+ def _split_lines(self, text, width):
+ if text.startswith('R|'):
+ return text[2:].splitlines()
+ # this is the RawTextHelpFormatter._split_lines
+ return argparse.HelpFormatter._split_lines(self, text, width)
+
+def main():
+ from . import install, _version
+ parser = argparse.ArgumentParser('python -m nbmolviz', formatter_class=SmartFormatter)
+
+ parser.add_argument('command', choices=['activate', 'uninstall', 'check'],
+ help='R|activate - install and enable nbmolviz\n'
+ 'uninstall - remove old nbmolviz installations\n'
+ 'check - check installed versions\n')
+
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument('--sys-prefix', '--env', '--venv', action='store_true',
+ help="Apply command to your virtual environment")
+ group.add_argument('--user', action='store_true',
+ help="Apply command to all of this user's environments")
+ group.add_argument('--sys', '--global', '--system', action='store_true',
+ help="Apply command to the global system configuration")
+
+ args = parser.parse_args()
+
+ if args.command == 'check':
+ print('Expected version:', _version.get_versions()['version'])
+ versions = install.get_installed_versions('nbmolviz-js', True)
+ foundone = False
+ for key, vers in versions.items():
+ if vers.installed:
+ if foundone:
+ print('--')
+ else:
+ foundone = True
+ print('Installed notebook extension locations:\n')
+
+ if key == 'user':
+ print('Environment:', 'current user')
+ elif key == 'environment':
+ print('Environment:', 'current virtual environment')
+ else:
+ assert key == 'system'
+ print('Environment:', 'global / system-wide')
+
+ print('Version: ', vers.version)
+ print('Location: ', vers.path)
+
+ if not foundone:
+ print("NBMolViz Jupyter extensions are not installed in any current environments")
+
+ elif args.command == 'activate':
+ if not (args.sys_prefix or args.user or args.sys):
+ print('Please indicate which environment to activate nbmolviz in.')
+ parser.print_help()
+ sys.exit(10)
+
+ if args.sys_prefix:
+ install.activate('--sys-prefix')
+ if args.user:
+ install.activate('--user')
+ if args.sys:
+ install.activate('--system')
+
+ elif args.command == 'uninstall':
+ if not (args.sys_prefix or args.user or args.sys):
+ args.sys_prefix = args.user = args.sys = True
+
+ if args.sys_prefix:
+ install.uninstall('--sys-prefix')
+ if args.user:
+ install.uninstall('--user')
+ if args.sys:
+ install.uninstall('--system')
+
+ else:
+ assert False, "parser failure"
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/nbmolviz/install.py b/nbmolviz/install.py
index 1d25c1c..ac7b75c 100644
--- a/nbmolviz/install.py
+++ b/nbmolviz/install.py
@@ -11,9 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
import os
+import sys
import collections
+import subprocess
import nbmolviz
@@ -28,24 +29,13 @@
NbExtVersion = collections.namedtuple('NbExtVersion', 'name installed path version'.split())
-def nbextension_check(extname, getversion):
+def get_installed_versions(extname, getversion):
""" Check if the required NBExtensions are installed. If not, prompt user for action.
"""
import jupyter_core.paths as jupypaths
from notebook import nbextensions
- # TODO: implement the following:
- # 0. Resolve jupyter nbextension search path, find installed
- # jupyter-js-widgets and nbmolviz-js extensions and their versions
- # 1. If extension with correct version is installed and enabled, do nothing, we're done
- # 2. If correct extensions are installed but not enabled, prompt user to enable
- # 3. If there are multiple copies, and the wrong version(s) are enabled, prompt user to
- # enable the right ones
- # 4. If not installed, prompt user to install/enable in the first writeable instance of
- # the following: sys-prefix, user-dir, systemwide
- # see https://github.com/ipython-contrib/jupyter_contrib_nbextensions/blob/master/src/jupyter_contrib_nbextensions/install.py
-
- installed = {k: nbextensions.check_nbextension('nbmolviz-js', **kwargs) for k,kwargs in EXTENSION_KWARGS.items()}
+ installed = {k: nbextensions.check_nbextension(extname, **kwargs) for k,kwargs in EXTENSION_KWARGS.items()}
jupyter_dir = {'user': jupypaths.jupyter_data_dir(),
'environment': jupypaths.ENV_JUPYTER_PATH[0],
'system': jupypaths.SYSTEM_JUPYTER_PATH[0]}
@@ -63,12 +53,43 @@ def nbextension_check(extname, getversion):
with open(versionfile, 'r') as pfile:
versions[k] = pfile.read().strip()
else:
- versions[k] = 'pre-0.8'
+ versions[k] = 'pre-0.7'
return {k: NbExtVersion(extname, installed[k], paths.get(k, None), versions.get(k, None))
for k in installed}
+def activate(flags):
+ try:
+ _jnbextrun('install', 'widgetsnbextension', flags)
+ _jnbextrun('enable', 'widgetsnbextension', flags)
+ except subprocess.CalledProcessError as exc:
+ if exc.returncode == 2:
+ print(('ERROR - failed to enable the widget extensions with %s.' % flags) +
+ ' Try rerunning the command with \"sudo\"!')
+ sys.exit(2)
+
+ _jnbextrun('install', 'nbmolviz', flags)
+ _jnbextrun('enable', 'nbmolviz', flags)
+
+
+def uninstall(flags):
+ try:
+ _jnbextrun('disable', 'nbmolviz', flags)
+ _jnbextrun('uninstall', 'nbmolviz', flags)
+ except subprocess.CalledProcessError as exc:
+ if exc.returncode == 2:
+ print(('ERROR - failed to uninstall the widget extensions with %s.' % flags) +
+ ' Try rerunning the command with \"sudo\"!')
+ sys.exit(2)
+
+
+def _jnbextrun(cmd, lib, flags):
+ shellcmd = ['jupyter', 'nbextension', cmd, '--py', flags, lib]
+ print('> %s' % ' '.join(shellcmd))
+ subprocess.check_call(shellcmd)
+
+
def find_nbmolviz_extension(extname):
import jupyter_core.paths as jupypaths
for extpath in jupypaths.jupyter_path('nbextensions'):
@@ -77,4 +98,3 @@ def find_nbmolviz_extension(extname):
return extpath
else:
return None
-
diff --git a/tests/nb/test_compute_widgets.ipynb b/tests/nb/test_compute_widgets.ipynb
new file mode 100644
index 0000000..3986c90
--- /dev/null
+++ b/tests/nb/test_compute_widgets.ipynb
@@ -0,0 +1,98 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#! test_about\n",
+ "import moldesign as mdt\n",
+ "ui = mdt.about()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#! test_pyccc_status_widget\n",
+ "import pyccc\n",
+ "engine = pyccc.engines.Docker()\n",
+ "infiles = {'a.txt':\"the only thing in this file is 'a'\"}\n",
+ "job = engine.launchp(image='alpine', command=\"cp a.txt b.txt\", inputs=infiles)\n",
+ "job.get_display_object()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#! test_logging_status_widgets\n",
+ "import moldesign as mdt\n",
+ "mdt.from_name('benzene')\n",
+ "mdt.from_name('hexane')\n",
+ "mdt.from_name('error_has_error')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as ipy"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ipy.Textarea(fontfamily='monospace', font_size=24, value='abc', font_family='monospace',\n",
+ " layout=ipy.Layout(font_size=24))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "_14.style"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
\ No newline at end of file
diff --git a/tests/py/test_nbmolviz.py b/tests/py/test_nbmolviz.py
index 720688d..01914b0 100644
--- a/tests/py/test_nbmolviz.py
+++ b/tests/py/test_nbmolviz.py
@@ -2,6 +2,7 @@
the supporting functionality
"""
from past.builtins import unicode
+import subprocess
import pytest
@@ -31,3 +32,7 @@ def test_generating_cubefile_works(wfn_viewer):
grid, values = wfn_viewer._calc_orb_grid(wfn_viewer.mol.wfn.orbitals.canonical[1])
cb = wfn_viewer._grid_to_cube(grid, values)
assert isinstance(cb, unicode)
+
+
+def test_install_checks_doesnt_crash():
+ subprocess.check_call('python -m nbmolviz check'.split())