├── .eslintrc.json
├── .github
└── workflows
│ ├── build.yml
│ └── ci.yml
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc.json
├── .readthedocs.yaml
├── CHANGELOG.md
├── CITATION.cff
├── DEVELOPMENT.md
├── LICENSE
├── README.md
├── babel.config.js
├── codecov.yml
├── css
└── widget.css
├── docs
├── Makefile
├── _static
│ ├── custom.css
│ ├── demo.png
│ └── mols2grid_logo.png
├── api
│ ├── callbacks.rst
│ ├── molgrid.rst
│ ├── simple.rst
│ └── utils.rst
├── conf.py
├── contents.md
├── environment.yml
├── index.rst
├── make.bat
└── notebooks
│ ├── callbacks.ipynb
│ ├── customization.ipynb
│ ├── filtering.ipynb
│ └── quickstart.ipynb
├── install.json
├── mols2grid.json
├── mols2grid
├── __init__.py
├── _version.py
├── callbacks.py
├── dispatch.py
├── molgrid.py
├── nbextension
│ └── extension.js
├── select.py
├── templates
│ ├── __init__.py
│ ├── css
│ │ ├── common.css
│ │ ├── interactive.css
│ │ └── static.css
│ ├── html
│ │ ├── common_header.html
│ │ ├── iframe.html
│ │ └── interactive_header.html
│ ├── interactive.html
│ ├── js
│ │ ├── callbacks
│ │ │ ├── external_link.js
│ │ │ └── show_3d.js
│ │ ├── draw_mol.js
│ │ ├── filter.js
│ │ ├── grid_interaction.js
│ │ ├── interactive.js
│ │ ├── kernel.js
│ │ ├── molstorage.js
│ │ ├── popup.js
│ │ ├── search.js
│ │ └── sort.js
│ └── static.html
├── utils.py
└── widget
│ ├── __init__.py
│ ├── _frontend.py
│ └── widget.py
├── package-lock.json
├── package.json
├── pyproject.toml
├── setup.py
├── src
├── extension.ts
├── index.ts
├── plugin.ts
├── version.ts
└── widget.ts
├── tests
├── __init__.py
├── conftest.py
├── environment.yml
├── pytest.ini
├── test_callbacks.py
├── test_interface.py
├── test_molgrid.py
├── test_select.py
├── test_utils.py
└── webdriver_utils.py
├── tsconfig.json
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "space-before-function-paren": ["error", "never"]
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | release:
4 | types: [released]
5 | workflow_dispatch:
6 |
7 | defaults:
8 | run:
9 | shell: bash -l {0}
10 |
11 | env:
12 | IS_PRERELEASE: ${{ github.event_name == 'workflow_dispatch' }}
13 |
14 | jobs:
15 | build-n-publish:
16 | name: Build and publish mols2grid
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v3
22 |
23 | - name: Get prerelease version tags
24 | if: env.IS_PRERELEASE == 'true'
25 | run: |
26 | py_dirty_tag=$(awk '/__version__ = "[[:digit:]+]\.[[:digit:]+]\.[[:digit:]+]\-.+"/ {print $3}' ./mols2grid/_version.py)
27 | py_is_pre=$(test -z "$py_dirty_tag" && echo "false" || echo "true")
28 | js_version_string=$(grep '"version":' ./package.json)
29 | js_dirty_tag=$(echo "$js_version_string" | cut -d- -f2)
30 | js_is_pre=$(test "$js_version_string" == "$js_dirty_tag" && echo "false" || echo "true")
31 | echo "py_is_pre=$py_is_pre" >> $GITHUB_ENV
32 | echo "js_is_pre=$js_is_pre" >> $GITHUB_ENV
33 |
34 | - name: Fail if prerelease is not correctly versioned
35 | if: (env.IS_PRERELEASE == 'true') && !( env.py_is_pre && env.js_is_pre )
36 | uses: actions/github-script@v3
37 | with:
38 | script: |
39 | core.setFailed("Versions are not tagged as a prerelease")
40 |
41 | - name: Install node
42 | uses: actions/setup-node@v3
43 | with:
44 | node-version: '12.x'
45 | registry-url: 'https://registry.npmjs.org/'
46 | cache: "npm"
47 |
48 | - name: Install python with pip
49 | uses: actions/setup-python@v4
50 | with:
51 | python-version: 3.8
52 | cache: "pip"
53 |
54 | - name: Install dependencies for packaging
55 | run: |
56 | pip install setuptools wheel build virtualenv twine
57 |
58 | - name: Check python installation
59 | run: |
60 | which python
61 | python --version
62 | pip --version
63 | pip list
64 |
65 | - name: Build package
66 | run: |
67 | python -m build .
68 |
69 | - name: Publish the Python package
70 | env:
71 | TWINE_USERNAME: __token__
72 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
73 | run: twine upload dist/mols2grid*.{tar.gz,whl}
74 |
75 | - name: Publish the NPM package
76 | run: npm publish
77 | env:
78 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
79 | PRE_RELEASE: ${{ env.IS_PRERELEASE }}
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | branches:
8 | - "*"
9 | workflow_dispatch:
10 |
11 | defaults:
12 | run:
13 | shell: bash -l {0}
14 |
15 | concurrency:
16 | group: ${{ github.ref }}-${{ github.head_ref }}"
17 | cancel-in-progress: true
18 |
19 | jobs:
20 | tests:
21 | name: ${{ matrix.label }}
22 | runs-on: ${{ matrix.os }}
23 | strategy:
24 | matrix:
25 | include:
26 | - label: CI-old
27 | os: ubuntu-latest
28 | python-version: 3.8
29 | extra_dependencies: "rdkit==2020.03.1 boost-cpp=1.72.0=h359cf19_6"
30 | - label: CI-edge
31 | os: ubuntu-latest
32 | python-version: "3.10"
33 | extra_dependencies: "rdkit"
34 | - label: CI-py3.8-rdkit2022
35 | os: ubuntu-latest
36 | python-version: 3.8
37 | extra_dependencies: "rdkit==2022.03.1"
38 |
39 | steps:
40 | - uses: actions/checkout@v3
41 |
42 | - name: Install node
43 | uses: actions/setup-node@v3
44 | with:
45 | node-version: "12.x"
46 | cache: "npm"
47 |
48 | - name: Install Firefox
49 | uses: browser-actions/setup-firefox@latest
50 |
51 | - run: firefox --version
52 |
53 | - name: Prepare Selenium
54 | uses: browser-actions/setup-geckodriver@latest
55 | with:
56 | geckodriver-version: "0.32.0"
57 |
58 | - run: geckodriver --version
59 |
60 | - name: Cache conda
61 | uses: actions/cache@v3
62 | env:
63 | CACHE_NUMBER: 0
64 | with:
65 | path: ~/conda_pkgs_dir
66 | key:
67 | conda-${{ hashFiles('environment.yml') }}-${{ matrix.label }}-${{ env.CACHE_NUMBER }}
68 |
69 | - name: Cache pip
70 | uses: actions/cache@v3
71 | with:
72 | path: ~/.cache/pip
73 | key: pip-${{ hashFiles('setup.cfg') }}
74 | restore-keys: pip-
75 |
76 | - name: Setup Conda
77 | uses: conda-incubator/setup-miniconda@v2
78 | with:
79 | python-version: ${{ matrix.python-version }}
80 | environment-file: tests/environment.yml
81 | use-only-tar-bz2: true
82 | miniforge-variant: Mambaforge
83 | miniforge-version: latest
84 | use-mamba: true
85 |
86 | - name: Check conda and pip
87 | run: |
88 | which python
89 | python --version
90 | pip --version
91 | conda --version
92 | mamba --version
93 |
94 | - name: Install remaining conda dependencies
95 | run: |
96 | mamba install ${{ matrix.extra_dependencies }}
97 | mamba list
98 |
99 | - name: Install package through pip
100 | run: |
101 | pip install .[tests,build]
102 | pip list
103 |
104 | - name: Run tests
105 | run: |
106 | pytest --color=yes --disable-pytest-warnings \
107 | --cov=mols2grid --cov-report=xml \
108 | tests/ -m "not webdriver"
109 |
110 | - name: Run webdriver tests
111 | uses: nick-fields/retry@v2
112 | with:
113 | timeout_seconds: 340
114 | max_attempts: 5
115 | retry_on: timeout
116 | shell: bash
117 | command: |
118 | source /usr/share/miniconda/etc/profile.d/conda.sh
119 | conda activate /usr/share/miniconda3/envs/test
120 | pytest --color=yes --disable-pytest-warnings \
121 | --cov=mols2grid --cov-report=xml --cov-append \
122 | tests/ -m "webdriver"
123 |
124 | - name: Measure tests coverage
125 | uses: codecov/codecov-action@v3
126 | with:
127 | files: ./coverage.xml
128 | fail_ci_if_error: true
129 | verbose: true
130 |
131 | - name: Prepare for build
132 | run: |
133 | pip uninstall -y mols2grid
134 | python -m build .
135 | echo "$SCRIPT" > test_install.py
136 | cat test_install.py
137 | env:
138 | SCRIPT: |
139 | import mols2grid as mg
140 | from rdkit import RDConfig
141 | sdf = f"{RDConfig.RDDocsDir}/Book/data/solubility.test.sdf"
142 | mg.save(sdf, output="/dev/null")
143 |
144 | - name: Test tar.gz build
145 | run: |
146 | pip install dist/mols2grid-*.tar.gz
147 | python test_install.py
148 | pip uninstall -y mols2grid
149 |
150 | - name: Test wheel build
151 | run: |
152 | pip install dist/mols2grid-*.whl
153 | python test_install.py
154 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/node
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node
4 |
5 | ### Node ###
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 | .pnpm-debug.log*
14 |
15 | # Diagnostic reports (https://nodejs.org/api/report.html)
16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
17 |
18 | # Runtime data
19 | pids
20 | *.pid
21 | *.seed
22 | *.pid.lock
23 |
24 | # Directory for instrumented libs generated by jscoverage/JSCover
25 | lib-cov
26 |
27 | # Coverage directory used by tools like istanbul
28 | coverage
29 | *.lcov
30 |
31 | # nyc test coverage
32 | .nyc_output
33 |
34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
35 | .grunt
36 |
37 | # Bower dependency directory (https://bower.io/)
38 | bower_components
39 |
40 | # node-waf configuration
41 | .lock-wscript
42 |
43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
44 | build/Release
45 |
46 | # Dependency directories
47 | node_modules/
48 | jspm_packages/
49 |
50 | # Snowpack dependency directory (https://snowpack.dev/)
51 | web_modules/
52 |
53 | # TypeScript cache
54 | *.tsbuildinfo
55 |
56 | # Optional npm cache directory
57 | .npm
58 |
59 | # Optional eslint cache
60 | .eslintcache
61 |
62 | # Optional stylelint cache
63 | .stylelintcache
64 |
65 | # Microbundle cache
66 | .rpt2_cache/
67 | .rts2_cache_cjs/
68 | .rts2_cache_es/
69 | .rts2_cache_umd/
70 |
71 | # Optional REPL history
72 | .node_repl_history
73 |
74 | # Output of 'npm pack'
75 | *.tgz
76 |
77 | # Yarn Integrity file
78 | .yarn-integrity
79 |
80 | # dotenv environment variable files
81 | .env
82 | .env.development.local
83 | .env.test.local
84 | .env.production.local
85 | .env.local
86 |
87 | # parcel-bundler cache (https://parceljs.org/)
88 | .cache
89 | .parcel-cache
90 |
91 | # Next.js build output
92 | .next
93 | out
94 |
95 | # Nuxt.js build / generate output
96 | .nuxt
97 | dist
98 |
99 | # Gatsby files
100 | .cache/
101 | # Comment in the public line in if your project uses Gatsby and not Next.js
102 | # https://nextjs.org/blog/next-9-1#public-directory-support
103 | # public
104 |
105 | # vuepress build output
106 | .vuepress/dist
107 |
108 | # vuepress v2.x temp and cache directory
109 | .temp
110 |
111 | # Docusaurus cache and generated files
112 | .docusaurus
113 |
114 | # Serverless directories
115 | .serverless/
116 |
117 | # FuseBox cache
118 | .fusebox/
119 |
120 | # DynamoDB Local files
121 | .dynamodb/
122 |
123 | # TernJS port file
124 | .tern-port
125 |
126 | # Stores VSCode versions used for testing VSCode extensions
127 | .vscode-test
128 |
129 | # yarn v2
130 | .yarn/cache
131 | .yarn/unplugged
132 | .yarn/build-state.yml
133 | .yarn/install-state.gz
134 | .pnp.*
135 |
136 | ### Node Patch ###
137 | # Serverless Webpack directories
138 | .webpack/
139 |
140 | # Optional stylelint cache
141 |
142 | # SvelteKit build / generate output
143 | .svelte-kit
144 |
145 | # End of https://www.toptal.com/developers/gitignore/api/node
146 |
147 | # Created by https://www.toptal.com/developers/gitignore/api/jupyternotebooks,python
148 | # Edit at https://www.toptal.com/developers/gitignore?templates=jupyternotebooks,python
149 |
150 | ### JupyterNotebooks ###
151 | # gitignore template for Jupyter Notebooks
152 | # website: http://jupyter.org/
153 |
154 | .ipynb_checkpoints
155 | */.ipynb_checkpoints/*
156 |
157 | # IPython
158 | profile_default/
159 | ipython_config.py
160 |
161 | # Remove previous ipynb_checkpoints
162 | # git rm -r .ipynb_checkpoints/
163 |
164 | ### Python ###
165 | # Byte-compiled / optimized / DLL files
166 | __pycache__/
167 | *.py[cod]
168 | *$py.class
169 |
170 | # C extensions
171 | *.so
172 |
173 | # Distribution / packaging
174 | .Python
175 | build/
176 | develop-eggs/
177 | dist/
178 | downloads/
179 | eggs/
180 | .eggs/
181 | lib/
182 | parts/
183 | sdist/
184 | var/
185 | wheels/
186 | pip-wheel-metadata/
187 | share/python-wheels/
188 | *.egg-info/
189 | .installed.cfg
190 | *.egg
191 | MANIFEST
192 |
193 | # PyInstaller
194 | # Usually these files are written by a python script from a template
195 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
196 | *.manifest
197 | *.spec
198 |
199 | # Installer logs
200 | pip-log.txt
201 | pip-delete-this-directory.txt
202 |
203 | # Unit test / coverage reports
204 | htmlcov/
205 | .tox/
206 | .nox/
207 | .coverage
208 | .coverage.*
209 | .cache
210 | nosetests.xml
211 | coverage.xml
212 | *.cover
213 | *.py,cover
214 | .hypothesis/
215 | .pytest_cache/
216 | pytestdebug.log
217 |
218 | # Translations
219 | *.mo
220 | *.pot
221 |
222 | # Django stuff:
223 | *.log
224 | local_settings.py
225 | db.sqlite3
226 | db.sqlite3-journal
227 |
228 | # Flask stuff:
229 | instance/
230 | .webassets-cache
231 |
232 | # Scrapy stuff:
233 | .scrapy
234 |
235 | # Sphinx documentation
236 | docs/_build/
237 | doc/_build/
238 |
239 | # PyBuilder
240 | target/
241 |
242 | # Jupyter Notebook
243 |
244 | # IPython
245 |
246 | # pyenv
247 | .python-version
248 |
249 | # pipenv
250 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
251 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
252 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
253 | # install all needed dependencies.
254 | #Pipfile.lock
255 |
256 | # poetry
257 | #poetry.lock
258 |
259 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
260 | __pypackages__/
261 |
262 | # Celery stuff
263 | celerybeat-schedule
264 | celerybeat.pid
265 |
266 | # SageMath parsed files
267 | *.sage.py
268 |
269 | # Environments
270 | # .env
271 | .env/
272 | .venv/
273 | env/
274 | venv/
275 | ENV/
276 | env.bak/
277 | venv.bak/
278 | pythonenv*
279 |
280 | # Spyder project settings
281 | .spyderproject
282 | .spyproject
283 |
284 | # Rope project settings
285 | .ropeproject
286 |
287 | # mkdocs documentation
288 | /site
289 |
290 | # mypy
291 | .mypy_cache/
292 | .dmypy.json
293 | dmypy.json
294 |
295 | # Pyre type checker
296 | .pyre/
297 |
298 | # pytype static type analyzer
299 | .pytype/
300 |
301 | # operating system-related files
302 | *.DS_Store #file properties cache/storage on macOS
303 | Thumbs.db #thumbnail cache on Windows
304 |
305 | # profiling data
306 | .prof
307 |
308 |
309 | # End of https://www.toptal.com/developers/gitignore/api/jupyternotebooks,python
310 |
311 | # =========================
312 | # Operating System Files
313 | # =========================
314 |
315 | # OSX
316 | # =========================
317 |
318 | .DS_Store
319 | .AppleDouble
320 | .LSOverride
321 |
322 | # Thumbnails
323 | ._*
324 |
325 | # Files that might appear in the root of a volume
326 | .DocumentRevisions-V100
327 | .fseventsd
328 | .Spotlight-V100
329 | .TemporaryItems
330 | .Trashes
331 | .VolumeIcon.icns
332 |
333 | # Directories potentially created on remote AFP share
334 | .AppleDB
335 | .AppleDesktop
336 | Network Trash Folder
337 | Temporary Items
338 | .apdisk
339 |
340 | # Windows
341 | # =========================
342 |
343 | # Windows image file caches
344 | Thumbs.db
345 | ehthumbs.db
346 |
347 | # Folder config file
348 | Desktop.ini
349 |
350 | # Recycle Bin used on file shares
351 | $RECYCLE.BIN/
352 |
353 | # Windows Installer files
354 | *.cab
355 | *.msi
356 | *.msm
357 | *.msp
358 |
359 | # Windows shortcuts
360 | *.lnk
361 |
362 |
363 | # NPM
364 | # ----
365 |
366 | **/node_modules/
367 |
368 | # Coverage data
369 | # -------------
370 | **/coverage/
371 |
372 | # ======
373 | # Custom
374 | # ======
375 |
376 | mols2grid/nbextension/index.*
377 | mols2grid/labextension
378 | .vscode/
379 | quickstart-grid.html
380 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | tests/
4 | .jshintrc
5 | # Ignore any build output from python:
6 | dist/*.tar.gz
7 | dist/*.wheel
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | mols2grid/templates/interactive.html
2 | mols2grid/templates/static.html
3 | mols2grid/templates/html/interactive_header.html
4 | mols2grid/templates/html/iframe.html
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "proseWrap": "preserve",
3 | "printWidth": 110,
4 | "tabWidth": 4,
5 | "useTabs": false,
6 | "semi": false,
7 | "singleQuote": true,
8 | "quoteProps": "as-needed",
9 | "trailingComma": "es5",
10 | "bracketSpacing": false,
11 | "bracketSameLine": true,
12 | "arrowParens": "avoid",
13 | "htmlWhitespaceSensitivity": "css"
14 | }
15 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yaml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | build:
9 | os: ubuntu-20.04
10 | tools:
11 | python: "mambaforge-4.10"
12 |
13 | sphinx:
14 | configuration: docs/conf.py
15 | fail_on_warning: false
16 |
17 | python:
18 | install:
19 | - method: pip
20 | path: .
21 | extra_requirements:
22 | - docs
23 |
24 | conda:
25 | environment: docs/environment.yml
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ---
10 |
11 | ## [2.0.0] - 2023/07/23
12 |
13 | This release is a major change on the UI contributed by @themoenen, refer to
14 | [PR#55](https://github.com/cbouy/mols2grid/pull/55) for the full list of changes:
15 |
16 | ### Added
17 | - `background_color="white"` parameter added to `display` and `save` to control the background
18 | color of each cell.
19 | - Property values displayed via subset or tooltip can now be copied by clicking them.
20 |
21 | ### Changed
22 | - Responsiveness: the grid as well as all UI components are now fully responsive, up until around
23 | ~415px wide. Smaller usage seems unlikely.
24 | - `n_items_per_page` replaces the `n_rows` and `n_cols` parameters. The responsive CSS assumes
25 | results to be a multiple of 12, otherwise a gap is displayed at the end of the grid.
26 | - Hover tooltips: now displayed when hovering the *`i`* icon, and anchored by clicking this icon.
27 | - Save CVS: When exporting as CSV we now use a semicolon `;` as delineator instead of a tab, this
28 | way CSVs are properly previewed on Mac.
29 | - Templates: the `pages`/`table` templates and corresponding functions have been renamed to
30 | `interactive`/`static` for clarity.
31 | - Parameters `width` and `height` have been renamed to `iframe_width` and `iframe_height` to be more
32 | descriptive.
33 | - Improved the sorting UI to be more intuitive.
34 | - You can now toggle between text/SMARTS search instead of having to click a dropdown.
35 | - The checkbox menu icon was replaced with a more standard triple dot menu.
36 | - The mols2grid-id is now permanently displayed next to the checkbox in the corner.
37 | - SVGs are now rendered with a transparent background.
38 | - The entire cell is now clickable, instead of just the tiny checkbox.
39 | - Implemented some basic keyboard navigation: ENTER / ESC for selecting / unselecting, arrows and
40 | TAB for navigation.
41 | - Copy to clipboard: this will now record a tab delineated text which is ready to be pasted into a
42 | spreadsheet, instead of the less useful dictionary format.
43 | - The `tooltip_trigger` parameter has been removed since callback and hover functionalities don't
44 | overlap anymore.
45 | - We're now automatically adding `"img"` to the subset instead of throwing an error.
46 | - A smaller default font size (12px) makes the experience out of the box a bit more practical.
47 |
48 | ### Fixed
49 | - Docstring now mention default values and some inconsistencies have been fixed.
50 | - All UI elements are now neatly aligned and displayed on top so they're accessible without
51 | scrolling.
52 | - Longer property values are now only truncated in the interactive grid, and broken into multiple
53 | lines by default in the static grid, as it is mostly meant for printing. A new parameter
54 | `truncate` lets you override the default truncating behavior.
55 | - The tooltip display zone (around which the tooltip is displayed) is now the entire cell instead
56 | of only the image, so it never overlaps with any of the cell's data or functionality.
57 | - When you download a CSV or SMILES file without any cells selected, you will now download data for
58 | all cells instead of an empty document.
59 | - Parameter `gap` in static template didn't work.
60 | - We no longer resize the iframe if a custom `iframe_height` is set by the display function.
61 |
62 | ---
63 |
64 | ## [1.1.1] - 2023/03/18
65 |
66 | ### Added
67 | - Support for `pathlib.Path` objects as input for `display`, `save`, `MolGrid.from_sdf`
68 | and `sdf_to_dataframe`.
69 |
70 | ### Changed
71 | - The hover tooltip placement has been changed from `"bottom"` to `"auto"`.
72 | - Code and notebook formatting with `black` and `isort`.
73 | - Switched to `hatchling` for the build process, `tbump` for versioning, and migrated to
74 | using only the `pyproject.toml` file.
75 | - Refactored tests to use Pytest's `contest.py` file.
76 |
77 | ### Fixed
78 | - CSV export when sorting the grid was not using the selected molecules.
79 |
80 | ---
81 |
82 | ## [1.1.0] - 2022/12/24
83 |
84 | ### Added
85 | - Predefined JavaScript callbacks in the `mols2grid.callbacks` module. Those can be
86 | extensively configured:
87 | - `info`: displays a bigger image alongside some common descriptors for the molecule
88 | - `show_3d`: displays the molecule in 3D
89 | - `external_link`: opens a new tab. By default, opens [Leruli.com](https://leruli.com/)
90 | using the SMILES of the molecule.
91 | - Support for `tuple` of molecules in `display` and `save`.
92 |
93 | ### Changed
94 | - The `"click"` event is now automatically removed from `tooltip_trigger` when
95 | specifying a callback.
96 |
97 | ### Fixed
98 | - Text searches containing any of the following regex characters `-[]{}()*+?.,\^$|#`
99 | would automatically return an empty grid, preventing searching for CAS numbers and any
100 | other identifier or text containing the above characters. This has been temporarily
101 | patched until a proper fix is released in the underlying `list.js` library.
102 | - The link to the KNIME component on the corresponding badges has been fixed.
103 |
104 |
105 | ## [1.0.0] - 2022/09/04
106 | ### Added
107 | - Notebooks running in VSCode and Jupyter Lab now support accessing selections from
108 | Python, executing Python callback functions, and filtering based on other widgets.
109 | ### Changed
110 | - Python callbacks can now also be `lambda` functions.
111 | - If ``prerender=True``, substructure highlighting will be automatically disabled by
112 | default instead of raising an error.
113 | - When exporting a selection to a SMILES file through the GUI, the output no longer
114 | contains a header.
115 | - Relies on a custom ipywidget to handle communication between the front-end/Javascript
116 | and the back-end/Python.
117 | - When calling `grid.filter` and other filtering methods, mols2grid will now use the
118 | filtering code based on ipywidgets, except for Streamlit where it will use the older
119 | JavaScript version of the code to maintain compatibility.
120 | ### Fixed
121 | - Automatically fitting to the content's height in Streamlit.
122 | ### Removed
123 | - `mapping` argument for renaming fields, replaced by `rename` in `v0.1.0`.
124 | - `mols2grid.selection`, replaced by `mols2grid.get_selection()` in `v0.1.0`.
125 |
126 |
127 | ## [0.2.4] - 2022/05/29
128 | ### Fixed
129 | - Calling `MolGrid.get_selection()` when 2 grids with different names are present should
130 | now display the selection of the grid itself, and not the selection corresponding to
131 | indices of the grid that was last interacted with.
132 |
133 |
134 | ## [0.2.3] - 2022/05/10
135 | ### Fixed
136 | - Doing a substructure search on molecules with explicit hydrogens should now highlight
137 | the correct atoms.
138 |
139 |
140 | ## [0.2.2] - 2022/04/04
141 | ### Added
142 | - A proper documentation page with tutorials can now be accessed online.
143 | - Added a `single_highlight=False` parameter to only highlight a single match per
144 | molecule in substructure queries.
145 | - Added a *"Check matching"* button that only selects items that match the current search
146 | and/or filters.
147 | - Added `custom_css`, `custom_header` and `sort_by` to the "table" template
148 | ### Changed
149 | - Compounds matching a substructure search are now aligned to the query molecule before
150 | rendering the image.
151 | - When doing a substructure search, all matches are now highlighted by default. To only
152 | show a single one, use `single_highlight=True`.
153 | - The *Check all*, *Uncheck all* and *Invert* selection buttons have been fixed. They now
154 | actually check/uncheck ALL items, and not just the ones matching the current search. A
155 | *Check matching* button has been added to reproduce the old behaviour.
156 | - If both `subset` and `tooltip` are `None`, the index and image will be directly
157 | displayed on the grid while the remaining fields will be in the tooltip. This makes the
158 | default representation much more readable.
159 | - The default number of columns is now 5 for `template="table"` (same as the other default
160 | template)
161 | ### Fixed
162 | - `template="table"` now correctly displays images when `prerender=True` (Issue #27)
163 | - Displaying the grid with `template="table"` in a notebook now automatically fits to the
164 | content of the table.
165 |
166 |
167 | ## [0.2.1] - 2022/02/23
168 | ### Fixed
169 | - Field names containing spaces are now correctly delt with
170 | - The text search now looks for matches inside the values of the tooltip fields, rather
171 | than inside the HTML code of the tooltip which included tags and other irrelevant text
172 | - Fixed an encoding bug when saving the grid as an HTML file on French Windows, which uses
173 | CP-1252 encoding instead of UTF-8
174 |
175 |
176 | ## [0.2.0] - 2022/02/10
177 | ### Added
178 | - `cache_selection=True` allows to retrieve the checkbox state when re-displaying a grid,
179 | as long as they have the same name. Fixes #22
180 | - `prerender=False` moves the rendering of molecule images from Python to the browser and
181 | only when the molecule is on the current page, giving a performance boost and allowing
182 | to process much larger files. Fixes #17
183 | - `substruct_highlight=True` highlight the atoms that matched the substructure query when
184 | using the SMARTS search (only available when `prerender=False`). Fixes #18
185 | - Added CSV save option. Exports all the data present in `subset` and `tooltip` for the
186 | current selection
187 | - Support for `.sdf.gz` files
188 | - Added automated tests of the interface, which should prevent future updates from
189 | breaking things
190 | ### Changed
191 | - Python 3.6 is no longer supported
192 | - Molecule images are now generated by the web browser (see `prerender=False` argument)
193 | - The coordinates of the input file are now ignored by default (`use_coords=False`). This
194 | change was made to comply with generating images from SMILES string with the browser by
195 | default.
196 | - Python callbacks are now automatically registered in Google Colab
197 | - Javascript callbacks can access RDKit as either `RDKit` or `RDKitModule`
198 | - The "img" field is now available from the callback data
199 | - The `subset` parameter now throws an error if "img" is not present
200 | - Clicking "Check all"/"Uncheck all" should now be faster
201 | - Bumped RDKit JS version to `2021.9.4` to better support moldrawoptions
202 | - Installation now requires `jinja2>=2.11.0` to prevent an error when given a pathlib.Path
203 | object instead of a string
204 | ### Fixed
205 | - Callbacks now work when `selection=False`. Fixes: Issue #22
206 | - Using both `transform` and `style` should now display the labels as expected in the
207 | tooltip
208 | - Fixed a race condition when clicking checkboxes on different grids
209 | - Fixed the `gap` argument not being properly taken into account
210 | - Automatic resizing of the iframe (used in `mols2Grid.display`) should now work even
211 | better
212 |
213 |
214 | ## [0.1.0] - 2021/10/11
215 | ### Added
216 | - The grid can be filtered using pandas DataFrame's `query` and `loc` logic (mostly
217 | useful to combine with ipywidgets) with `MolGrid.filter_by_index` and `MolGrid.filter`.
218 | - Selections can now be modified (select and unselect all, or invert) and exported (to
219 | clipboard or a SMILES file) even without a notebook kernel. Fixes: Issue #16.
220 | - The grid can be sorted according to the selection status and to values in the tooltips.
221 | - Added tracking the selection in multiple grids at the same time (i.e. it's not a
222 | global object that get's overwritten anymore).
223 | - Added support for executing custom JavaScript code or Python function when clicking on
224 | a molecule's image through the `callback` argument.
225 | - Added the `mols2grid.make_popup_callback` helper function to create a popup-like window
226 | as a JavaScript callback.
227 | - Added styling for the whole cell through `style={"__all__": userfunction}`.
228 | - Added `mols2grid.get_selection()` allowing users to specify which grid selection should
229 | be returned. Without argument, the most recently updated grid is returned.
230 | - Added `mols2grid.list_grids()` to return a list of grid names available.
231 | - Added the `mols2grid.sdf_to_dataframe` function to easily convert an SDF file to a
232 | pandas DataFrame.
233 | - Added the `custom_css` argument to pass custom CSS for the HTML document.
234 | - Added the `sort_by` argument to change how the grid elements are ordered
235 | ### Changed
236 | - The functions in `style` and `transform` are now also applied to tooltips.
237 | - The sizing of the iframe displaying the grid is now fully automated and more precise.
238 | - Reorganized the code to separate the JS, CSS and HTML templates.
239 | ### Fixed
240 | - Fixed `mols2grid.save` that returned an error about missing the `output` argument.
241 | - The tooltip is now compatible with the "focus" mode: `tooltip_trigger="focus"`.
242 | - Fixed rendering SVG images in tooltips.
243 | ### Deprecated
244 | - Deprecated `mols2grid.selection` in favor of `mols2grid.get_selection()`.
245 | - Deprecated `mapping` in favor of `rename` in the MolGrid class and `mols2grid.display`.
246 |
247 |
248 | ## [0.0.6] - 2021/05/14
249 | ### Changed
250 | - Javascript module for RDKit is now sourced from `unpkg.com` and pinned to `v2021.3.2`
251 |
252 |
253 | ## [0.0.5] - 2021/04/08
254 | ### Added
255 | - New `transform` parameter that accepts a dictionary of field-function items where each
256 | function transforms the input value that will be displayed. Fixes: Issue #10
257 | ### Fixed
258 | - Running mols2grid could throw an ImportError (instead of ModuleNotFoundError) if the
259 | `google` module was installed, but not `google.colab`. Solved by PR #11
260 | - Private molecule properties (i.e properties starting with `_`) were not registered when
261 | reading properties from RDKit molecules (SDF or list of mols).
262 |
263 |
264 | ## [0.0.4] - 2021/04/01
265 | ### Changed
266 | - The demo notebook can now be run on Google Colab
267 | ### Fixed
268 | - DataFrames with `NaN` values would previously lead to an **empty grid** as `NaN` were
269 | converted to `nan` (not recognized by JS) instead of `NaN`.
270 | - Selection of molecules in Google Colab now works as expected.
271 | - Saved documents are now displayed properly
272 |
273 |
274 | ## [0.0.3] - 2021/03/31
275 | ### Added
276 | - **SMARTS search**: the "🔎" button now lets users choose between a text search or a
277 | SMARTS search. This relies on RDKit's "MinimalLib" JS wrapper which is still in beta,
278 | and will likely break at some point. Use at your own risk!
279 | - **Sorting**: added a "Sort by" button that lets users choose in which order the
280 | molecules should be listed. Default: by index. Fixes: Issue #7
281 | - `MolDrawOptions` **drawing** parameter: this will allow further customization of the
282 | drawing options.
283 | - **Selection**: added checkboxes to each cell. Clicking on a checkbox will add the
284 | molecule's corresponding index and SMILES to the `mols2grid.selection` dictionary.
285 | - New **input** formats: dict and record (list of dicts) are automatically converted to a
286 | pandas DataFrame when used as input to the MolGrid class. The `mols2grid.display`
287 | function only accepts the dict option (since the list format is already used for lists
288 | of RDKit molecules).
289 | - New **input** options: `mol_col` parameter. Adds the ability to directly use an RDKit
290 | mol instead of relying on a SMILES intermediate. This makes using the 2D coordinates of
291 | the input mol a possibility, instead of systematically generating new ones. It also
292 | allows for adding annotations and highlights on drawings. Introduces 2 new parameters:
293 | - `mol_col=None`: Column of the dataframe containing RDKit molecules
294 | - `use_coords=True`: directly use the coordinates from each molecule, or generate new
295 | ones
296 | ### Changed
297 | - The "mols2grid-id" now keeps track of molecules that could not be read by RDKit. This
298 | makes relying on the index of the corresponding entry more reliable.
299 | ### Fixed
300 | - The "mols2grid-id" field is now correctly set in the internal `DataFrame` and the
301 | JavaScript `List`.
302 | - Using the search bar will now only search inside the fields listed in `subset` and
303 | `tooltip` and exclude the `img` field.
304 | - When using the `display` function, the `height` of the iframe is now automatically set
305 | based on the different parameters, instead of a fixed 600px height. Fixes: Issue #6
306 |
307 |
308 | ## [0.0.2] - 2021/03/23
309 | - First release
310 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | # This CITATION.cff file was generated with cffinit.
2 | # Visit https://bit.ly/cffinit to generate yours today!
3 |
4 | cff-version: 1.2.0
5 | title: mols2grid - Interactive molecule viewer for 2D structures
6 | message: Please cite this software using these metadata.
7 | type: software
8 | authors:
9 | - given-names: Cédric
10 | family-names: Bouysset
11 | orcid: 'https://orcid.org/0000-0002-7814-8158'
12 | doi: 10.5281/zenodo.6591473
13 | license: Apache-2.0
14 | repository-code: https://github.com/cbouy/mols2grid
15 | date-released: "2021-03-23"
16 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | This is a short guide to setup a dev environment for mols2grid.
2 |
3 | 1. Install conda or mamba
4 | 2. Create a new environment. Python 3.7+ (prefer 3.8):
5 | ```
6 | conda env create --name mols2grid --file docs/environment.yml
7 | ```
8 | 3. Install all the package dependencies in editable mode:
9 | ```
10 | pip install -e .[dev]
11 | ```
12 |
13 | To run tests locally:
14 | - Install Firefox (needed for UI testing)
15 | - Test your installation:
16 | ```
17 | pytest tests/
18 | ```
19 | - You can select/skip the UI testing by specifying the `webdriver` mark in the pytest
20 | command: `-m webdriver` to select UI tests only, or `-m "not webdriver"` to skip them.
21 |
22 | We use `black` and `isort` for formatting so either install the corresponding extension
23 | from your IDE or install the package with `pip install black isort`. The configuration
24 | is done inside the `pyproject.toml` file.
25 |
26 | Making a pull request will automatically run the tests and documentation build for you.
27 | Don't forget to update the `CHANGELOG.md` file with your changes.
28 |
29 | For versioning, you'll have to update both `package.json` and `mols2grid/_version.py`
30 | files.
31 |
32 | The build and deployment process is run automatically when making a release on
33 | GitHub.
34 | To make a prerelease, bump the versions accordingly (`X.Y.Z-rc1` format) and run
35 | the `build` GitHub Action manually.
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #  mols2grid
2 |
3 | [](https://pypi.python.org/pypi/mols2grid)
4 | [](https://anaconda.org/conda-forge/mols2grid)
5 |
6 | [](https://github.com/cbouy/mols2grid/actions/workflows/ci.yml)
7 | [](https://github.com/cbouy/mols2grid/actions/workflows/build.yml)
8 | [](https://mols2grid.readthedocs.io/en/latest/?badge=latest)
9 | [](https://codecov.io/gh/cbouy/mols2grid)
10 |
11 | [](https://www.rdkit.org/)
12 | [](https://hub.knime.com/cbouy/spaces/Public/latest/Interactive%20Grid%20of%20Molecules)
13 |
14 | **mols2grid** is an interactive molecule viewer for 2D structures, based on RDKit.
15 |
16 | 
17 |
18 | ## [Documentation](https://mols2grid.readthedocs.io/en/latest/contents.html)
19 |
20 | Installation, basic usage, links to resources...*etc.*
21 |
22 | ## [Notebooks](https://mols2grid.readthedocs.io/en/latest/notebooks/quickstart.html)
23 |
24 | Showcases examples of most features, with links to run the notebooks on Google Colab
25 |
26 | ## [API Reference](https://mols2grid.readthedocs.io/en/latest/api/simple.html)
27 |
28 | The manual, in all its glory
29 |
30 | ## [Discussions](https://github.com/cbouy/mols2grid/discussions)
31 |
32 | Feature requests or questions on how to use should be posted here
33 |
34 | ## [Issues](https://github.com/cbouy/mols2grid/issues)
35 |
36 | Bug tracker 🐞
37 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | sourceMap: 'inline',
3 | presets: [
4 | [
5 | '@babel/preset-env',
6 | {
7 | targets: {
8 | node: 'current',
9 | },
10 | },
11 | ],
12 | ],
13 | };
14 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | target: 85%
6 | patch:
7 | default:
8 | target: 0%
9 | comment:
10 | layout: "diff, sunburst, reach, files"
11 | behavior: default
12 | require_changes: false
13 | require_base: yes
14 | require_head: yes
15 |
--------------------------------------------------------------------------------
/css/widget.css:
--------------------------------------------------------------------------------
1 | .mols2grid-widget {
2 | padding: 0;
3 | margin: 0;
4 | width: 0;
5 | height: 0;
6 | max-width: 0;
7 | max-height: 0;
8 | }
9 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/_static/custom.css:
--------------------------------------------------------------------------------
1 | .wy-nav-content {
2 | max-width: 991px !important;
3 | }
--------------------------------------------------------------------------------
/docs/_static/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbouy/mols2grid/d156fc940c3db929409f5ea706b8f598d3eefc13/docs/_static/demo.png
--------------------------------------------------------------------------------
/docs/_static/mols2grid_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbouy/mols2grid/d156fc940c3db929409f5ea706b8f598d3eefc13/docs/_static/mols2grid_logo.png
--------------------------------------------------------------------------------
/docs/api/callbacks.rst:
--------------------------------------------------------------------------------
1 | Callbacks
2 | =========
3 |
4 | A collection of functions to easily create JavaScript callbacks.
5 |
6 | .. automodule:: mols2grid.callbacks
7 | :members:
8 |
--------------------------------------------------------------------------------
/docs/api/molgrid.rst:
--------------------------------------------------------------------------------
1 | MolGrid object
2 | ==============
3 |
4 | The class that creates and controls the grid of molecules. Allows for a more
5 | advanced usage, like filtering the grid using external controls such as
6 | slider widgets. See the notebooks for examples.
7 |
8 | .. autoclass:: mols2grid.MolGrid
9 | :members:
--------------------------------------------------------------------------------
/docs/api/simple.rst:
--------------------------------------------------------------------------------
1 | Simple usage
2 | ============
3 |
4 | The most straightforward way to use mols2grid! For a more advanced usage, see :class:`~mols2grid.MolGrid`.
5 |
6 | .. autofunction:: mols2grid.display
7 |
8 | .. autofunction:: mols2grid.save
9 |
10 | .. autofunction:: mols2grid.get_selection
11 |
--------------------------------------------------------------------------------
/docs/api/utils.rst:
--------------------------------------------------------------------------------
1 | Utilities
2 | =========
3 |
4 | .. autofunction:: mols2grid.sdf_to_dataframe
5 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 |
13 | import os
14 | import sys
15 |
16 | sys.path.insert(0, os.path.abspath('.'))
17 |
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = 'mols2grid'
21 | copyright = '2022, Cédric Bouysset'
22 | author = 'Cédric Bouysset'
23 |
24 |
25 | # -- General configuration ---------------------------------------------------
26 |
27 | github_doc_root = 'https://github.com/cbouy/mols2grid/tree/master/docs/'
28 | needs_sphinx = '4.5.0'
29 |
30 | # Add any Sphinx extension module names here, as strings. They can be
31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
32 | # ones.
33 | extensions = [
34 | 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
35 | 'sphinx.ext.viewcode', 'sphinx.ext.napoleon',
36 | 'sphinx.ext.autosectionlabel',
37 | 'sphinx_rtd_theme',
38 | 'sphinx_mdinclude',
39 | 'nbsphinx',
40 | ]
41 |
42 | autosectionlabel_prefix_document = True
43 | napoleon_google_docstring = False
44 |
45 | source_suffix = ['.rst', '.md']
46 |
47 | # Add any paths that contain templates here, relative to this directory.
48 | templates_path = []
49 |
50 | # List of patterns, relative to source directory, that match files and
51 | # directories to ignore when looking for source files.
52 | # This pattern also affects html_static_path and html_extra_path.
53 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
54 |
55 |
56 | # -- Options for HTML output -------------------------------------------------
57 |
58 | # The theme to use for HTML and HTML Help pages. See the documentation for
59 | # a list of builtin themes.
60 | #
61 | html_theme = 'sphinx_rtd_theme'
62 | pygments_style = "manni"
63 | html_logo = "_static/mols2grid_logo.png"
64 |
65 | # Add any paths that contain custom static files (such as style sheets) here,
66 | # relative to this directory. They are copied after the builtin static files,
67 | # so a file named "default.css" will overwrite the builtin "default.css".
68 | html_static_path = ["_static"]
69 |
70 | ###
71 |
72 | intersphinx_mapping = {'https://docs.python.org/3/': None,
73 | 'https://numpy.org/doc/stable/': None,
74 | 'https://www.rdkit.org/docs/': None,
75 | 'https://pandas.pydata.org/docs/': None,
76 | 'https://ipython.readthedocs.io/en/stable/': None,
77 | }
78 |
79 | # app setup hook
80 | def setup(app):
81 | # custom css
82 | app.add_css_file('custom.css')
83 |
--------------------------------------------------------------------------------
/docs/contents.md:
--------------------------------------------------------------------------------
1 | # 💡 Introduction
2 |
3 | - [](https://pypi.python.org/pypi/mols2grid)
4 | [](https://anaconda.org/conda-forge/mols2grid)
5 |
6 | - [](https://github.com/cbouy/mols2grid/actions/workflows/ci.yml)
7 | [](https://github.com/cbouy/mols2grid/actions/workflows/build.yml)
8 | [](https://mols2grid.readthedocs.io/en/latest/?badge=latest)
9 | [](https://codecov.io/gh/cbouy/mols2grid)
10 |
11 | - [](https://www.rdkit.org/)
12 | [](https://hub.knime.com/cbouy/spaces/Public/latest/Interactive%20Grid%20of%20Molecules)
13 |
14 | **mols2grid** is an interactive molecule viewer for 2D structures, based on RDKit.
15 |
16 | 
17 |
18 | # 🐍 Installation
19 | ---
20 |
21 | mols2grid was developped for Python 3.7+ and requires rdkit (>=2020.03.1), pandas and jinja2 as dependencies.
22 | The easiest way to install it is from conda:
23 | ```shell
24 | conda install -c conda-forge mols2grid
25 | ```
26 |
27 | Alternatively, you can also use pip:
28 | ```shell
29 | pip install rdkit mols2grid
30 | ```
31 |
32 | If you notice that the selections, callbacks and interactive filtering aren't working as intended, you may have to manually activate the extension:
33 | - for Jupyter Lab: `jupyter labextension install mols2grid`
34 | - for Jupyter Notebook: `jupyter nbextension install mols2grid`
35 |
36 | **Compatibility**
37 |
38 | mols2grid is mainly meant to be used in notebooks (Jupyter notebooks, Jupyter Lab, and Google Colab) but it can also be used as a standalone HTML page opened with your favorite web browser, or embedded in a Streamlit app.
39 |
40 | Since Streamlit doesn't seem to support ipywidgets yet, some features aren't functional: retrieving the selection from Python (you can still export it from the GUI) and using Python callbacks.
41 |
42 |
43 |
You can also use mols2grid directly in KNIME, by looking for the `Interactive Grid of Molecules` component on the Knime HUB.
44 | Make sure you have setup Knime's Python integration for the node to work.
45 |
46 |
47 | # 📜 Usage
48 | ---
49 |
50 | You can display a grid from:
51 |
52 | - an SDFile
53 |
54 | ```python
55 | import mols2grid
56 |
57 | mols2grid.display("path/to/molecules.sdf")
58 | ```
59 |
60 | - a `pandas.DataFrame`:
61 |
62 | ```python
63 | mols2grid.display(df, smiles_col="Smiles")
64 | ```
65 |
66 | - a list of RDKit molecules:
67 |
68 | ```python
69 | mols2grid.display(mols)
70 | ```
71 |
72 | Please head to the [notebooks](notebooks/quickstart.html) and [API reference](api/simple.html) sections for a complete overview of the features available.
73 |
74 | # 🚀 Resources
75 | ---
76 | * [Simple example](https://iwatobipen.wordpress.com/2021/06/13/draw-molecules-on-jupyter-notebook-rdkit-mols2grid/) by iwatobipen
77 | * Creating a web app with Streamlit for filtering datasets:
78 | * [Blog post](https://blog.reverielabs.com/building-web-applications-from-python-scripts-with-streamlit/) by Justin Chavez
79 | * [Video tutorial](https://www.youtube.com/watch?v=0rqIwSeUImo) by Data Professor
80 | * [Viewing clustered chemical structures](https://practicalcheminformatics.blogspot.com/2021/07/viewing-clustered-chemical-structures.html) and [Exploratory data analysis](https://practicalcheminformatics.blogspot.com/2021/10/exploratory-data-analysis-with.html) by Pat Walters
81 | * [Advanced notebook (RDKit UGM 2021)](https://colab.research.google.com/github/rdkit/UGM_2021/blob/main/Notebooks/Bouysset_mols2grid.ipynb)
82 |
83 | Feel free to open a pull request if you'd like your snippets to be added to this list!
84 |
85 | # 👏 Acknowledgments
86 | ---
87 | * [@themoenen](https://github.com/themoenen) (contributor)
88 | * [@fredrikw](https://github.com/fredrikw) (contributor)
89 | * [@JustinChavez](https://github.com/JustinChavez) (contributor)
90 | * [@hadim](https://github.com/hadim) (conda feedstock maintainer)
91 |
92 | # 🎓 Citing
93 | ---
94 | You can refer to mols2grid in your research by using the following DOI:
95 |
96 | [](https://zenodo.org/badge/latestdoi/348814588)
97 |
98 | # ⚖ License
99 | ---
100 |
101 | Unless otherwise noted, all files in this directory and all subdirectories are distributed under the Apache License, Version 2.0:
102 | ```text
103 | Copyright 2021-2022 Cédric BOUYSSET
104 |
105 | Licensed under the Apache License, Version 2.0 (the "License");
106 | you may not use this file except in compliance with the License.
107 | You may obtain a copy of the License at
108 |
109 | http://www.apache.org/licenses/LICENSE-2.0
110 |
111 | Unless required by applicable law or agreed to in writing, software
112 | distributed under the License is distributed on an "AS IS" BASIS,
113 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
114 | See the License for the specific language governing permissions and
115 | limitations under the License.
116 | ```
117 |
--------------------------------------------------------------------------------
/docs/environment.yml:
--------------------------------------------------------------------------------
1 | name: mols2grid
2 | channels:
3 | - conda-forge
4 | - defaults
5 | dependencies:
6 | - ipykernel==5.4.3
7 | - ipywidgets==7.6.3
8 | - notebook<7.0.0
9 | - mistune<3.0.0
10 | - jedi==0.17.2
11 | - jinja2==3.0.3
12 | - nbsphinx==0.8.8
13 | - nodejs==17.9.0
14 | - pandas==1.2.1
15 | - rdkit==2022.03.1
16 | - recommonmark==0.7.1
17 | - sphinx==4.5.0
18 | - yarn==1.22.18
19 | - pip:
20 | - py3dmol==1.7.0
21 | - sphinx-rtd-theme==1.0.0
22 | - sphinx-mdinclude==0.5.2
23 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. mols2grid documentation master file, created by
2 | sphinx-quickstart on Fri Apr 1 19:31:58 2022.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to mols2grid's documentation!
7 | =====================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents
12 |
13 | contents
14 |
15 | .. toctree::
16 | :maxdepth: 3
17 | :caption: Notebooks
18 |
19 | notebooks/quickstart
20 | notebooks/customization
21 | notebooks/filtering
22 | notebooks/callbacks
23 |
24 | .. toctree::
25 | :maxdepth: 2
26 | :caption: API Reference
27 |
28 | api/simple
29 | api/molgrid
30 | api/callbacks
31 | api/utils
32 |
33 |
34 | Indices and tables
35 | ==================
36 |
37 | * :ref:`genindex`
38 | * :ref:`modindex`
39 | * :ref:`search`
40 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/notebooks/callbacks.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Callbacks\n",
8 | "\n",
9 | "[](https://colab.research.google.com/github/cbouy/mols2grid/blob/master/docs/notebooks/callbacks.ipynb)\n",
10 | "\n",
11 | "Callbacks are **functions that are executed when you click on a cell's callback button**. They can be written in *JavaScript* or *Python*.\n",
12 | "\n",
13 | "This functionality can be used to display some additional information on the molecule or run some more complex code such as database queries, docking or machine-learning predictions."
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "# uncomment and run if you're on Google Colab\n",
23 | "# !pip install rdkit mols2grid py3Dmol\n",
24 | "# !wget https://raw.githubusercontent.com/rdkit/rdkit/master/Docs/Book/data/solubility.test.sdf"
25 | ]
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": null,
30 | "metadata": {},
31 | "outputs": [],
32 | "source": [
33 | "import urllib.parse\n",
34 | "import urllib.request\n",
35 | "from pathlib import Path\n",
36 | "from urllib.error import HTTPError\n",
37 | "\n",
38 | "import py3Dmol\n",
39 | "from IPython.display import display\n",
40 | "from ipywidgets import widgets\n",
41 | "from rdkit import RDConfig\n",
42 | "\n",
43 | "import mols2grid\n",
44 | "\n",
45 | "\n",
46 | "SDF_FILE = (\n",
47 | " f\"{RDConfig.RDDocsDir}/Book/data/solubility.test.sdf\"\n",
48 | " if Path(RDConfig.RDDocsDir).is_dir()\n",
49 | " else \"solubility.test.sdf\"\n",
50 | ")\n",
51 | "# Optional: read SDF file and sample 50 mols from it (to keep this notebook light)\n",
52 | "df = mols2grid.sdf_to_dataframe(SDF_FILE).sample(50, random_state=0xac1d1c)"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "## Python\n",
60 | "\n",
61 | "*Note: if you are reading this from the documentation web page, clicking on the images will not trigger anything. Try running the notebook on Google Colab instead (see link at the top of the page).*\n",
62 | "\n",
63 | "For Python callbacks, you need to declare a function that takes a dictionnary as first argument. This dictionnary contains all the data related to the molecule you've just clicked on. All the data fields are **parsed as strings**, except for the index, \"mols2grid-id\", which is always parsed as an integer.\n",
64 | "\n",
65 | "For example, the SMILES of the molecule will be available as `data[\"SMILES\"]`. If the field contains spaces, they will be converted to hyphens, *i.e.* a field called `mol weight` will be available as `data[\"mol-weight\"]`.\n",
66 | "\n",
67 | "Also, using print or any other \"output\" functions inside the callback will not display anything by default. You need to use ipywidgets's `Output` widget to capture what the function is trying to display, and then show it.\n",
68 | "\n",
69 | "### Basic print example\n",
70 | "\n",
71 | "In this simple example, we'll just show the content of the data dictionnary."
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": null,
77 | "metadata": {},
78 | "outputs": [],
79 | "source": [
80 | "output = widgets.Output()\n",
81 | "\n",
82 | "\n",
83 | "# the Output widget let's us capture the output generated by the callback function\n",
84 | "# its presence is mandatory if you want to print/display some info with your callback\n",
85 | "@output.capture(clear_output=True, wait=True)\n",
86 | "def show_data(data):\n",
87 | " data.pop(\"img\")\n",
88 | " for key, value in data.items():\n",
89 | " print(key, value)\n",
90 | "\n",
91 | "\n",
92 | "view = mols2grid.display(\n",
93 | " df,\n",
94 | " callback=show_data,\n",
95 | ")\n",
96 | "display(view)\n",
97 | "output"
98 | ]
99 | },
100 | {
101 | "cell_type": "markdown",
102 | "metadata": {},
103 | "source": [
104 | "We can also make more complex operations with callbacks.\n",
105 | "\n",
106 | "### Displaying the 3D structure with py3Dmol\n",
107 | "\n",
108 | "Here, we'll query PubChem for the molecule based on its SMILES, then fetch the 3D structure and display it with py3Dmol."
109 | ]
110 | },
111 | {
112 | "cell_type": "code",
113 | "execution_count": null,
114 | "metadata": {},
115 | "outputs": [],
116 | "source": [
117 | "output = widgets.Output()\n",
118 | "\n",
119 | "\n",
120 | "@output.capture(clear_output=True, wait=True)\n",
121 | "def show_3d(data):\n",
122 | " \"\"\"Query PubChem to download the SDFile with 3D coordinates and\n",
123 | " display the molecule with py3Dmol\n",
124 | " \"\"\"\n",
125 | " url = \"https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/smiles/{}/SDF?record_type=3d\"\n",
126 | " smi = urllib.parse.quote(data[\"SMILES\"])\n",
127 | " try:\n",
128 | " response = urllib.request.urlopen(url.format(smi))\n",
129 | " except HTTPError:\n",
130 | " print(f\"Could not find corresponding match on PubChem\")\n",
131 | " print(data[\"SMILES\"])\n",
132 | " else:\n",
133 | " sdf = response.read().decode()\n",
134 | " view = py3Dmol.view(height=300, width=800)\n",
135 | " view.addModel(sdf, \"sdf\")\n",
136 | " view.setStyle({\"stick\": {}})\n",
137 | " view.zoomTo()\n",
138 | " view.show()\n",
139 | "\n",
140 | "\n",
141 | "view = mols2grid.display(\n",
142 | " df,\n",
143 | " callback=show_3d,\n",
144 | ")\n",
145 | "display(view)\n",
146 | "output"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {},
152 | "source": [
153 | "## JavaScript\n",
154 | "\n",
155 | "We can also write JavaScript callbacks, which have the advantage to be able to run on almost any platform.\n",
156 | "\n",
157 | "JS callbacks don't require to declare a function, and you can directly access and use the `data` object similarly to Python in your callback script.\n",
158 | "\n",
159 | "### Basic JS example"
160 | ]
161 | },
162 | {
163 | "cell_type": "code",
164 | "execution_count": null,
165 | "metadata": {},
166 | "outputs": [],
167 | "source": [
168 | "js_callback = \"\"\"\n",
169 | "// remove image from data\n",
170 | "delete data[\"img\"];\n",
171 | "// convert data object to text\n",
172 | "txt = JSON.stringify(data);\n",
173 | "// show data in alert window\n",
174 | "alert(txt);\n",
175 | "\"\"\"\n",
176 | "\n",
177 | "mols2grid.display(\n",
178 | " df,\n",
179 | " callback=js_callback,\n",
180 | ")"
181 | ]
182 | },
183 | {
184 | "cell_type": "markdown",
185 | "metadata": {},
186 | "source": [
187 | "To display fancy popup windows on click, a helper function is available: `mols2grid.make_popup_callback`.\n",
188 | "\n",
189 | "It requires a title as well as some html code to format and display the information that you'd like to show. All of the values inside the data object can be inserted in the `title` and `html` arguments using `${data[\"field_name\"]}`. Additionally, you can execute a prerequisite JavaScript snippet to create variables that are then also accessible in the html code.\n",
190 | "\n",
191 | "### Display a popup containing descriptors\n",
192 | "\n",
193 | "In the following exemple, we create an RDKit molecule using the SMILES of the molecule (the SMILES field is always present in the data object, no matter your input when creating the grid).\n",
194 | "\n",
195 | "We then create a larger SVG image of the molecule, and calculate some descriptors.\n",
196 | "\n",
197 | "Finally, we inject these variables inside the HTML code. You can also style the popup window through the style argument."
198 | ]
199 | },
200 | {
201 | "cell_type": "code",
202 | "execution_count": null,
203 | "metadata": {},
204 | "outputs": [],
205 | "source": [
206 | "js_callback = mols2grid.make_popup_callback(\n",
207 | " title=\"${data['NAME']}\",\n",
208 | " subtitle=\"${data['SMILES']}\",\n",
209 | " svg=\"${svg}\",\n",
210 | " js=\"\"\"\n",
211 | " var mol = RDKit.get_mol(data[\"SMILES\"]);\n",
212 | " var svg = mol.get_svg(400, 300);\n",
213 | " var desc = JSON.parse(mol.get_descriptors());\n",
214 | " var inchikey = RDKit.get_inchikey_for_inchi(mol.get_inchi());\n",
215 | " mol.delete();\n",
216 | " \"\"\",\n",
217 | " html=\"\"\"\n",
218 | " Molecular weight: ${desc.exactmw} \n",
219 | " HBond Acceptors: ${desc.NumHBA} \n",
220 | " HBond Donors: ${desc.NumHBD} \n",
221 | " TPSA: ${desc.tpsa} \n",
222 | " ClogP: ${desc.CrippenClogP} \n",
223 | " \n",
224 | " InChIKey: ${inchikey}\n",
225 | " \"\"\",\n",
226 | " style=\"border-radius: 10px\",\n",
227 | ")\n",
228 | "\n",
229 | "mols2grid.display(\n",
230 | " df,\n",
231 | " callback=js_callback,\n",
232 | ")"
233 | ]
234 | },
235 | {
236 | "cell_type": "markdown",
237 | "metadata": {},
238 | "source": [
239 | "This functionality is directly available in `mols2grid` by using the `mols2grid.callbacks.info()` function:\n",
240 | "\n",
241 | "```python\n",
242 | "mols2grid.display(\n",
243 | " df, callback=mols2grid.callbacks.info(),\n",
244 | ")\n",
245 | "```"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "It is possible to load additional JS libraries by passing `custom_header=\"\"` to `mols2grid.display`, and they will then be available in the callback.\n",
253 | "\n",
254 | "### Displaying the 3D structure with 3Dmol.js\n",
255 | "\n",
256 | "In the following example, we query PubChem using the SMILES of the molecule (and Cactus as a fallback, but you can also provide another custom REST API), then fetch the 3D structure in SDF format and display it with 3Dmol.js:"
257 | ]
258 | },
259 | {
260 | "cell_type": "code",
261 | "execution_count": null,
262 | "metadata": {},
263 | "outputs": [],
264 | "source": [
265 | "mols2grid.display(\n",
266 | " df,\n",
267 | " callback=mols2grid.callbacks.show_3d(),\n",
268 | ")"
269 | ]
270 | },
271 | {
272 | "cell_type": "markdown",
273 | "metadata": {},
274 | "source": [
275 | "### Opening an external link\n",
276 | "\n",
277 | "There is also a function to open a link based on the data in the molecule: `mols2grid.callbacks.external_link`.\n",
278 | "\n",
279 | "By default, it opens the search window of Leruli using the SMILES string, but the URL and data field used can be configured."
280 | ]
281 | },
282 | {
283 | "cell_type": "code",
284 | "execution_count": null,
285 | "metadata": {},
286 | "outputs": [],
287 | "source": [
288 | "mols2grid.display(\n",
289 | " df,\n",
290 | " callback=mols2grid.callbacks.external_link(),\n",
291 | ")"
292 | ]
293 | }
294 | ],
295 | "metadata": {
296 | "interpreter": {
297 | "hash": "634da3a3bbf8fbf1ddb65b0056d578c92f3c569db0da492ea274ae9d304e5b24"
298 | },
299 | "kernelspec": {
300 | "display_name": "Python 3",
301 | "language": "python",
302 | "name": "python3"
303 | },
304 | "language_info": {
305 | "codemirror_mode": {
306 | "name": "ipython",
307 | "version": 3
308 | },
309 | "file_extension": ".py",
310 | "mimetype": "text/x-python",
311 | "name": "python",
312 | "nbconvert_exporter": "python",
313 | "pygments_lexer": "ipython3",
314 | "version": "3.7.12"
315 | }
316 | },
317 | "nbformat": 4,
318 | "nbformat_minor": 2
319 | }
320 |
--------------------------------------------------------------------------------
/docs/notebooks/customization.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {},
7 | "source": [
8 | "# Customization\n",
9 | "\n",
10 | "[](https://colab.research.google.com/github/cbouy/mols2grid/blob/master/docs/notebooks/customization.ipynb)\n",
11 | "\n",
12 | "The grid can be customized quite extensively, from the content that is displayed to the look of the grid and images."
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": null,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "# uncomment and run if you're on Google Colab\n",
22 | "# !pip install rdkit mols2grid\n",
23 | "# !wget https://raw.githubusercontent.com/rdkit/rdkit/master/Docs/Book/data/solubility.test.sdf"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "from pathlib import Path\n",
33 | "\n",
34 | "from rdkit import RDConfig\n",
35 | "\n",
36 | "import mols2grid\n",
37 | "\n",
38 | "\n",
39 | "SDF_FILE = (\n",
40 | " f\"{RDConfig.RDDocsDir}/Book/data/solubility.test.sdf\"\n",
41 | " if Path(RDConfig.RDDocsDir).is_dir()\n",
42 | " else \"solubility.test.sdf\"\n",
43 | ")"
44 | ]
45 | },
46 | {
47 | "attachments": {},
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "To display all the arguments available, type `help(mols2grid.display)`"
52 | ]
53 | },
54 | {
55 | "cell_type": "code",
56 | "execution_count": null,
57 | "metadata": {},
58 | "outputs": [],
59 | "source": [
60 | "mols2grid.display(\n",
61 | " SDF_FILE,\n",
62 | " # rename fields for the output document\n",
63 | " rename={\"SOL\": \"Solubility\", \"SOL_classification\": \"Class\", \"NAME\": \"Name\"},\n",
64 | " # set what's displayed on the grid\n",
65 | " subset=[\"ID\", \"img\", \"Solubility\"],\n",
66 | " # set what's displayed on the hover tooltip\n",
67 | " tooltip=[\"Name\", \"SMILES\", \"Class\", \"Solubility\"],\n",
68 | " # style for the grid labels and tooltips\n",
69 | " style={\n",
70 | " \"Solubility\": lambda x: \"color: red; font-weight: bold;\" if x < -3 else \"\",\n",
71 | " \"__all__\": lambda x: \"background-color: azure;\" if x[\"Solubility\"] > -1 else \"\",\n",
72 | " },\n",
73 | " # change the precision and format (or other transformations)\n",
74 | " transform={\"Solubility\": lambda x: round(x, 2)},\n",
75 | " # sort the grid in a different order by default\n",
76 | " sort_by=\"Name\",\n",
77 | " # molecule drawing parameters\n",
78 | " fixedBondLength=25,\n",
79 | " clearBackground=False,\n",
80 | ")"
81 | ]
82 | },
83 | {
84 | "attachments": {},
85 | "cell_type": "markdown",
86 | "metadata": {},
87 | "source": [
88 | "See [rdkit.Chem.Draw.rdMolDraw2D.MolDrawOptions](https://www.rdkit.org/docs/source/rdkit.Chem.Draw.rdMolDraw2D.html#rdkit.Chem.Draw.rdMolDraw2D.MolDrawOptions) for the molecule drawing options available.\n",
89 | "\n",
90 | "The grid's look can also be customized to an even greater extent:"
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": null,
96 | "metadata": {},
97 | "outputs": [],
98 | "source": [
99 | "# some unnecessarily complicated CSS stylesheet 🌈\n",
100 | "# use .m2g-cell to select each grid's cell\n",
101 | "# or .data for every data field\n",
102 | "# or .data- for a specific field\n",
103 | "css_style = \"\"\"\n",
104 | "/* rainbow background */\n",
105 | ".m2g-cell {\n",
106 | " background: linear-gradient(124deg, #ff2400, #e81d1d, #e8b71d, #e3e81d, #1de840, #1ddde8, #2b1de8, #dd00f3, #dd00f3);\n",
107 | " background-size: 500% 500%;\n",
108 | " -webkit-animation: rainbow 10s ease infinite;\n",
109 | " animation: rainbow 10s ease infinite;\n",
110 | "}\n",
111 | ".m2g-cell:hover {\n",
112 | " border-color: red !important;\n",
113 | "}\n",
114 | "/* rainbow font color */\n",
115 | ".data {\n",
116 | " font-weight: bold;\n",
117 | " -webkit-text-stroke: 1px black;\n",
118 | " background: linear-gradient(to right, #ef5350, #f48fb1, #7e57c2, #2196f3, #26c6da, #43a047, #eeff41, #f9a825, #ff5722);\n",
119 | " -webkit-background-clip: text;\n",
120 | " color: transparent;\n",
121 | "}\n",
122 | "/* background animation */\n",
123 | "@-webkit-keyframes rainbow {\n",
124 | " 0% {background-position: 0% 50%}\n",
125 | " 50% {background-position: 100% 50%}\n",
126 | " 100% {background-position: 0% 50%}\n",
127 | "}\n",
128 | "@keyframes rainbow { \n",
129 | " 0% {background-position: 0% 50%}\n",
130 | " 50% {background-position: 100% 50%}\n",
131 | " 100% {background-position: 0% 50%}\n",
132 | "}\n",
133 | "\"\"\"\n",
134 | "\n",
135 | "mols2grid.display(\n",
136 | " SDF_FILE,\n",
137 | " # RDKit drawing options\n",
138 | " comicMode=True,\n",
139 | " fixedBondLength=20,\n",
140 | " bondLineWidth=1,\n",
141 | " # custom atom colour palette (all white)\n",
142 | " atomColourPalette={z: (1, 1, 1) for z in range(1, 295)},\n",
143 | " # mols2grid options\n",
144 | " subset=[\"NAME\", \"img\"],\n",
145 | " custom_css=css_style,\n",
146 | " fontfamily='\"Comic Sans MS\", \"Comic Sans\", cursive;',\n",
147 | " # image size\n",
148 | " size=(130, 80),\n",
149 | " # number of results per page\n",
150 | " n_items_per_page=20,\n",
151 | " # border around each cell\n",
152 | " border=\"5px ridge cyan\",\n",
153 | " # gap between cells\n",
154 | " gap=3,\n",
155 | " # disable selection\n",
156 | " selection=False,\n",
157 | ")"
158 | ]
159 | }
160 | ],
161 | "metadata": {
162 | "interpreter": {
163 | "hash": "634da3a3bbf8fbf1ddb65b0056d578c92f3c569db0da492ea274ae9d304e5b24"
164 | },
165 | "kernelspec": {
166 | "display_name": "Python 3",
167 | "language": "python",
168 | "name": "python3"
169 | },
170 | "language_info": {
171 | "codemirror_mode": {
172 | "name": "ipython",
173 | "version": 3
174 | },
175 | "file_extension": ".py",
176 | "mimetype": "text/x-python",
177 | "name": "python",
178 | "nbconvert_exporter": "python",
179 | "pygments_lexer": "ipython3",
180 | "version": "3.9.13"
181 | }
182 | },
183 | "nbformat": 4,
184 | "nbformat_minor": 2
185 | }
186 |
--------------------------------------------------------------------------------
/docs/notebooks/filtering.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Filtering\n",
8 | "\n",
9 | "[](https://colab.research.google.com/github/cbouy/mols2grid/blob/master/docs/notebooks/filtering.ipynb)\n",
10 | "\n",
11 | "It's possible to integrate the grid with other widgets to complement the searchbar."
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "# uncomment and run if you're on Google Colab\n",
21 | "# !pip install rdkit mols2grid\n",
22 | "# !wget https://raw.githubusercontent.com/rdkit/rdkit/master/Docs/Book/data/solubility.test.sdf"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": null,
28 | "metadata": {},
29 | "outputs": [],
30 | "source": [
31 | "from pathlib import Path\n",
32 | "\n",
33 | "from ipywidgets import interact, widgets\n",
34 | "from rdkit import RDConfig\n",
35 | "from rdkit.Chem import Descriptors\n",
36 | "\n",
37 | "import mols2grid\n",
38 | "\n",
39 | "\n",
40 | "SDF_FILE = (\n",
41 | " f\"{RDConfig.RDDocsDir}/Book/data/solubility.test.sdf\"\n",
42 | " if Path(RDConfig.RDDocsDir).is_dir()\n",
43 | " else \"solubility.test.sdf\"\n",
44 | ")"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "We'll use ipywidgets to add sliders for the molecular weight and the other molecular descriptors, and define a function that queries the internal dataframe using the values in the sliders.\n",
52 | "\n",
53 | "Everytime the sliders are moved, the function is called to filter our grid."
54 | ]
55 | },
56 | {
57 | "cell_type": "code",
58 | "execution_count": null,
59 | "metadata": {},
60 | "outputs": [],
61 | "source": [
62 | "df = mols2grid.sdf_to_dataframe(SDF_FILE)\n",
63 | "# compute some descriptors\n",
64 | "df[\"MolWt\"] = df[\"mol\"].apply(Descriptors.ExactMolWt)\n",
65 | "df[\"LogP\"] = df[\"mol\"].apply(Descriptors.MolLogP)\n",
66 | "df[\"NumHDonors\"] = df[\"mol\"].apply(Descriptors.NumHDonors)\n",
67 | "df[\"NumHAcceptors\"] = df[\"mol\"].apply(Descriptors.NumHAcceptors)"
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "metadata": {},
74 | "outputs": [],
75 | "source": [
76 | "grid = mols2grid.MolGrid(\n",
77 | " df,\n",
78 | " size=(120, 100),\n",
79 | " name=\"filters\",\n",
80 | ")\n",
81 | "view = grid.display(n_items_per_page=12)\n",
82 | "\n",
83 | "\n",
84 | "@interact(\n",
85 | " MolWt=widgets.IntRangeSlider(value=[0, 600], min=0, max=600, step=10),\n",
86 | " LogP=widgets.IntRangeSlider(value=[-10, 10], min=-10, max=10, step=1),\n",
87 | " NumHDonors=widgets.IntRangeSlider(value=[0, 20], min=0, max=20, step=1),\n",
88 | " NumHAcceptors=widgets.IntRangeSlider(value=[0, 20], min=0, max=20, step=1),\n",
89 | ")\n",
90 | "def filter_grid(MolWt, LogP, NumHDonors, NumHAcceptors):\n",
91 | " results = grid.dataframe.query(\n",
92 | " \"@MolWt[0] <= MolWt <= @MolWt[1] and \"\n",
93 | " \"@LogP[0] <= LogP <= @LogP[1] and \"\n",
94 | " \"@NumHDonors[0] <= NumHDonors <= @NumHDonors[1] and \"\n",
95 | " \"@NumHAcceptors[0] <= NumHAcceptors <= @NumHAcceptors[1]\"\n",
96 | " )\n",
97 | " return grid.filter_by_index(results.index)\n",
98 | "\n",
99 | "\n",
100 | "view"
101 | ]
102 | }
103 | ],
104 | "metadata": {
105 | "kernelspec": {
106 | "display_name": "Python 3.8.12 ('molgrid')",
107 | "language": "python",
108 | "name": "python3"
109 | },
110 | "language_info": {
111 | "codemirror_mode": {
112 | "name": "ipython",
113 | "version": 3
114 | },
115 | "file_extension": ".py",
116 | "mimetype": "text/x-python",
117 | "name": "python",
118 | "nbconvert_exporter": "python",
119 | "pygments_lexer": "ipython3",
120 | "version": "3.8.12"
121 | },
122 | "orig_nbformat": 4,
123 | "vscode": {
124 | "interpreter": {
125 | "hash": "634da3a3bbf8fbf1ddb65b0056d578c92f3c569db0da492ea274ae9d304e5b24"
126 | }
127 | }
128 | },
129 | "nbformat": 4,
130 | "nbformat_minor": 2
131 | }
132 |
--------------------------------------------------------------------------------
/docs/notebooks/quickstart.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Quickstart\n",
8 | "\n",
9 | "[](https://colab.research.google.com/github/cbouy/mols2grid/blob/master/docs/notebooks/quickstart.ipynb)\n",
10 | "\n",
11 | "The easiest way to use mols2grid is through the `mols2grid.display` function. The input can be a DataFrame, a list of RDKit molecules, or an SDFile."
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "# uncomment and run if you're on Google Colab\n",
21 | "# !pip install rdkit mols2grid\n",
22 | "# !wget https://raw.githubusercontent.com/rdkit/rdkit/master/Docs/Book/data/solubility.test.sdf"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": null,
28 | "metadata": {},
29 | "outputs": [],
30 | "source": [
31 | "from pathlib import Path\n",
32 | "\n",
33 | "from rdkit import RDConfig\n",
34 | "\n",
35 | "import mols2grid\n",
36 | "\n",
37 | "\n",
38 | "SDF_FILE = (\n",
39 | " f\"{RDConfig.RDDocsDir}/Book/data/solubility.test.sdf\"\n",
40 | " if Path(RDConfig.RDDocsDir).is_dir()\n",
41 | " else \"solubility.test.sdf\"\n",
42 | ")"
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {},
48 | "source": [
49 | "Let's start with an SDFile (`.sdf` and `.sdf.gz` are both supported):"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "metadata": {},
56 | "outputs": [],
57 | "source": [
58 | "mols2grid.display(SDF_FILE)"
59 | ]
60 | },
61 | {
62 | "cell_type": "markdown",
63 | "metadata": {},
64 | "source": [
65 | "From this interface, you can:\n",
66 | "\n",
67 | "- Make simple text searches using the searchbar on the top right.\n",
68 | "- Make substructure queries by clicking on `SMARTS` instead of `Text` and typing in the searchbar.\n",
69 | "- Sort molecules by clicking on `Sort` and selecting a field (click the arrows on the right side of the `Sort` dropdown to reverse the order).\n",
70 | "- View metadata by hovering your mouse over the *`i`* button of a cell, you can also press that button to anchor the information.\n",
71 | "- Select a couple of molecules (click on a cell or on a checkbox, or navigate using your keyboard arrows and press the `ENTER` key).\n",
72 | "- Export the selection to a SMILES or CSV file, or directly to the clipboard (this last functionality might be blocked depending on how you are running the notebook). If no selection was made, the entire grid is exported."
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {},
78 | "source": [
79 | "We can also use a pandas DataFrame as input, containing a column of RDKit molecules (specified using `mol_col=...`) or SMILES strings (specified using `smiles_col=...`):"
80 | ]
81 | },
82 | {
83 | "cell_type": "code",
84 | "execution_count": null,
85 | "metadata": {},
86 | "outputs": [],
87 | "source": [
88 | "df = mols2grid.sdf_to_dataframe(SDF_FILE)\n",
89 | "subset_df = df.sample(50, random_state=0xac1d1c)\n",
90 | "mols2grid.display(subset_df, mol_col=\"mol\")"
91 | ]
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "Finally, we can also use a list of RDKit molecules:"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": null,
103 | "metadata": {},
104 | "outputs": [],
105 | "source": [
106 | "mols = subset_df[\"mol\"].to_list()\n",
107 | "mols2grid.display(mols)"
108 | ]
109 | },
110 | {
111 | "cell_type": "markdown",
112 | "metadata": {},
113 | "source": [
114 | "But the main point of mols2grid is that the widget let's you access your selections from Python afterwards:"
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "execution_count": null,
120 | "metadata": {},
121 | "outputs": [],
122 | "source": [
123 | "mols2grid.get_selection()"
124 | ]
125 | },
126 | {
127 | "cell_type": "markdown",
128 | "metadata": {},
129 | "source": [
130 | "If you were using a DataFrame, you can get the subset corresponding to your selection with:"
131 | ]
132 | },
133 | {
134 | "cell_type": "code",
135 | "execution_count": null,
136 | "metadata": {},
137 | "outputs": [],
138 | "source": [
139 | "df.iloc[list(mols2grid.get_selection().keys())]"
140 | ]
141 | },
142 | {
143 | "cell_type": "markdown",
144 | "metadata": {},
145 | "source": [
146 | "Finally, you can save the grid as a standalone HTML document. Simply replace `display` by `save` and add the path to the output file with `output=\"path/to/molecules.html\"`"
147 | ]
148 | },
149 | {
150 | "cell_type": "code",
151 | "execution_count": null,
152 | "metadata": {},
153 | "outputs": [],
154 | "source": [
155 | "mols2grid.save(mols, output=\"quickstart-grid.html\")"
156 | ]
157 | }
158 | ],
159 | "metadata": {
160 | "interpreter": {
161 | "hash": "634da3a3bbf8fbf1ddb65b0056d578c92f3c569db0da492ea274ae9d304e5b24"
162 | },
163 | "kernelspec": {
164 | "display_name": "Python 3.8.6 ('molgrid')",
165 | "language": "python",
166 | "name": "python3"
167 | },
168 | "language_info": {
169 | "codemirror_mode": {
170 | "name": "ipython",
171 | "version": 3
172 | },
173 | "file_extension": ".py",
174 | "mimetype": "text/x-python",
175 | "name": "python",
176 | "nbconvert_exporter": "python",
177 | "pygments_lexer": "ipython3",
178 | "version": "3.8.17"
179 | },
180 | "orig_nbformat": 4
181 | },
182 | "nbformat": 4,
183 | "nbformat_minor": 2
184 | }
185 |
--------------------------------------------------------------------------------
/install.json:
--------------------------------------------------------------------------------
1 | {
2 | "packageManager": "python",
3 | "packageName": "mols2grid",
4 | "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package mols2grid"
5 | }
6 |
--------------------------------------------------------------------------------
/mols2grid.json:
--------------------------------------------------------------------------------
1 | {
2 | "load_extensions": {
3 | "mols2grid/extension": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/mols2grid/__init__.py:
--------------------------------------------------------------------------------
1 | from ._version import __version__
2 | from .callbacks import make_popup_callback
3 | from .dispatch import display, save
4 | from .molgrid import MolGrid
5 | from .select import get_selection, list_grids
6 | from .utils import sdf_to_dataframe
7 | from .widget import _jupyter_labextension_paths, _jupyter_nbextension_paths
8 |
9 | try:
10 | from google.colab import output
11 | except ImportError:
12 | pass
13 | else:
14 | output.enable_custom_widget_manager()
15 |
--------------------------------------------------------------------------------
/mols2grid/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "2.0.0"
2 |
--------------------------------------------------------------------------------
/mols2grid/callbacks.py:
--------------------------------------------------------------------------------
1 | from typing import NamedTuple, Optional
2 |
3 | from .utils import env
4 |
5 |
6 | class _JSCallback(NamedTuple):
7 | """Class that holds JavaScript code for running a callback function. If an external
8 | library is required for the callback to function correctly, it can be passed in
9 | the optional ``library_src`` as a ``"""
138 | return _JSCallback(code=code, library_src=library)
139 |
140 |
141 | def external_link(
142 | url="https://leruli.com/search/{}/home",
143 | field="SMILES",
144 | url_encode=False,
145 | b64_encode=True,
146 | ) -> _JSCallback:
147 | """Opens an external link using ``url`` as a template string and the value in the
148 | corresponding ``field``. The value can be URL-encoded or base64-encoded if needed.
149 |
150 | Parameters
151 | ----------
152 | url : str
153 | Template string used to generate the URL that will be opened.
154 | field : str
155 | Field name used to generate the URL that will be opened. The value can be
156 | encoded (see below).
157 | url_encode : bool
158 | Encode the value fetched from the specified field to replace characters that are
159 | not allowed in a URL e.g., spaces become ``%20``.
160 | b64_encode : bool
161 | Base64-encode the value fetched from the field.
162 |
163 | Raises
164 | ------
165 | ValueError : Both ``url_encode`` and ``b64_encode`` have been specified.
166 | """
167 | if url_encode and b64_encode:
168 | raise ValueError("Setting both URL and B64 encoding is not supported")
169 | code = env.get_template("js/callbacks/external_link.js").render(
170 | url=url,
171 | field=field,
172 | url_encode=url_encode,
173 | b64_encode=b64_encode,
174 | )
175 | return _JSCallback(code=code)
176 |
--------------------------------------------------------------------------------
/mols2grid/dispatch.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | from functools import singledispatch
3 | from pathlib import Path
4 |
5 | from pandas import DataFrame, Series
6 |
7 | from .molgrid import MolGrid
8 |
9 | _SIGNATURE = {
10 | method: dict(inspect.signature(getattr(MolGrid, method)).parameters.items())
11 | for method in ["render", "to_interactive", "to_static", "display"]
12 | }
13 | for method in ["render", "to_interactive", "to_static", "display"]:
14 | _SIGNATURE[method].pop("self")
15 | if method in ["render", "display"]:
16 | _SIGNATURE[method].pop("kwargs")
17 |
18 |
19 | def _prepare_kwargs(kwargs, kind):
20 | """Separate kwargs for the init and render methods of MolGrid"""
21 | template = kwargs.pop("template", _SIGNATURE["render"]["template"].default)
22 | render_kwargs = {
23 | param: kwargs.pop(param, sig.default)
24 | for param, sig in _SIGNATURE[f"to_{template}"].items()
25 | }
26 | if kind == "display":
27 | render_kwargs.update(
28 | {
29 | param: kwargs.pop(param, sig.default)
30 | for param, sig in _SIGNATURE["display"].items()
31 | }
32 | )
33 | return template, kwargs, render_kwargs
34 |
35 |
36 | @singledispatch
37 | def display(arg, **kwargs):
38 | """Display molecules on an interactive grid.
39 |
40 | Parameters: Data
41 | ----------------
42 | arg : pandas.DataFrame, SDF file or list of molecules
43 | The input containing your molecules.
44 | smiles_col : str or None, default="SMILES"
45 | If a pandas dataframe is used, name of the column with SMILES.
46 | mol_col : str or None, default=None
47 | If a pandas dataframe is used, name of the column with RDKit molecules.
48 | If available, coordinates and atom/bonds annotations from this will be
49 | used for depiction.
50 |
51 | Parameters: Display
52 | -------------------
53 | template : str, default="interactive"
54 | Either ``"interactive"`` or ``"static"``. See ``render()`` for more details.
55 | size : tuple, default=(130, 90)
56 | The size of the drawing canvas. The cell minimum width is set to the
57 | width of the image, so if the cell padding is increased, the image will
58 | be displayed smaller.
59 | useSVG : bool, default=True
60 | Use SVG images instead of PNG.
61 | prerender : bool, default=False
62 | Prerender images for the entire dataset, or generate them on-the-fly.
63 | Prerendering is slow and memory-hungry, but required when ``template="static"``
64 | or ``useSVG=False``.
65 | subset: list or None, default=None
66 | Columns to be displayed in each cell of the grid. Each column's
67 | value will be displayed from top to bottom in the order provided.
68 | The ``"img"`` and ``"mols2grid-id"`` columns are displayed by default,
69 | however you can still add the ``"img"`` column if you wish to change
70 | the display order.
71 | tooltip : list, None or False, default=None
72 | Columns to be displayed inside the tooltip. When no subset is set,
73 | all columns will be listed in the tooltip by default. Use ``False``
74 | to hide the tooltip.
75 | tooltip_fmt : str, default="{key}: {value}"
76 | Format string of each key/value pair in the tooltip.
77 | tooltip_trigger : str, default="focus"
78 | Only available for the "static" template.
79 | Sequence of triggers for the tooltip: ``click``, ``hover`` or ``focus``
80 | tooltip_placement : str, default="auto"
81 | Position of the tooltip: ``auto``, ``top``, ``bottom``, ``left`` or
82 | ``right``
83 | transform : dict or None, default=None
84 | Functions applied to specific items in all cells. The dict must follow
85 | a ``key: function`` structure where the key must correspond to one of
86 | the columns in ``subset`` or ``tooltip``. The function takes the item's
87 | value as input and transforms it, for example::
88 |
89 | transform={
90 | "Solubility": lambda x: f"{x:.2f}",
91 | "Melting point": lambda x: f"MP: {5/9*(x-32):.1f}°C"
92 | }
93 |
94 | These transformations only affect columns in ``subset`` and
95 | ``tooltip``, and do not interfere with ``style``.
96 | sort_by : str or None, default=None
97 | Sort the grid according to the following field (which must be
98 | present in ``subset`` or ``tooltip``).
99 | truncate: bool, default=True/False
100 | Whether to truncate the text in each cell if it's too long.
101 | Defaults to ``True`` for interactive grids, ``False`` for static grid.
102 | n_items_per_page, default=24
103 | Only available for the "interactive" template.
104 | Number of items to display per page. A multiple of 12 is recommended
105 | for optimal display.
106 | n_cols : int, default=5
107 | Only available for the "static" template.
108 | Number of columns in the table.
109 | selection : bool, default=True
110 | Only available for the "interactive" template.
111 | Enables the selection of molecules and displays a checkbox at the
112 | top of each cell. In the context of a Jupyter Notebook, this gives
113 | you access to your selection (index and SMILES) through
114 | :func:`mols2grid.get_selection()` or :meth:`MolGrid.get_selection()`.
115 | In all cases, you can export your selection by clicking on the triple-dot menu.
116 | cache_selection : bool, default=False
117 | Only available for the "interactive" template.
118 | Restores the selection from a previous grid with the same name.
119 | use_iframe : bool, default=False
120 | Whether to use an iframe to display the grid. When the grid is displayed
121 | inside a Jupyter Notebook or JupyterLab, this will default to ``True``.
122 | iframe_width : str, default="100%
123 | Width of the iframe
124 | iframe_height : int or None, default=None
125 | Height of the frame. When set to ``None``, the height is set dynamically
126 | based on the content.
127 |
128 | Parameters: Mols
129 | ----------------
130 | removeHs : bool, default=False
131 | Remove hydrogen atoms from the drawings.
132 | use_coords : bool, default=False
133 | Use the coordinates of the molecules (only relevant when an SDF file, a
134 | list of molecules or a DataFrame of RDKit molecules were used as input.)
135 | coordGen : bool, default=True
136 | Use the CoordGen library instead of the RDKit one to depict the
137 | molecules in 2D.
138 | MolDrawOptions : rdkit.Chem.Draw.rdMolDraw2D.MolDrawOptions or None, default=None
139 | Drawing options. Useful for making highly customized drawings.
140 | substruct_highlight : bool or None, default=None
141 | Highlight substructure when using the SMARTS search. Active by default
142 | when ``prerender=False``.
143 | single_highlight : bool, default=False
144 | Highlight only the first match of the substructure query.
145 |
146 | Parameters: CSS
147 | ---------------
148 | border : str, default="1px solid #cccccc"
149 | Styling of the border around each cell.
150 | gap : int, default=0
151 | Size in pixels of the gap between cells.
152 | pad : int, default=10
153 | Size in pixels of the cell padding.
154 | fontsize : str, default="12px"
155 | Font size of the text displayed in each cell.
156 | fontfamily : str, default="'DejaVu', sans-serif"
157 | Font used for the text in each cell.
158 | textalign : str, default="center"
159 | Alignment of the text in each cell.
160 | background_color : str, default="white"
161 | Only available for the "interactive" template.
162 | Background color of a cell.
163 | hover_color : str, default="rgba(0,0,0,0.05)"
164 | Only available for the "interactive" template.
165 | Background color when hovering a cell
166 | custom_css : str or None, default=None
167 | Custom CSS properties applied to the generated HTML. Please note that
168 | the CSS will apply to the entire page if no iframe is used (see
169 | ``use_iframe`` for more details).
170 | style : dict or None, default=None
171 | CSS styling applied to each item in a cell. The dict must follow a
172 | ``key: function`` structure where the key must correspond to one of the
173 | columns in ``subset`` or ``tooltip``. The function takes the item's
174 | value as input, and outputs a valid CSS styling. For example, if you
175 | want to color the text corresponding to the "Solubility" column in your
176 | dataframe::
177 |
178 | style={"Solubility": lambda x: "color: red" if x < -5 else ""}
179 |
180 | You can also style a whole cell using the ``__all__`` key, the
181 | corresponding function then has access to all values for each cell::
182 |
183 | style={"__all__": lambda x: "color: red" if x["Solubility"] < -5 else ""}
184 |
185 | Parameters: Customization
186 | -------------------------
187 | name : str, default="default"
188 | Name of the grid. Used when retrieving selections from multiple grids
189 | at the same time
190 | rename : dict or None, default=None
191 | Rename the properties in the final document.
192 | custom_header : str or None, default=None
193 | Custom libraries to be loaded in the header of the document.
194 | callback : str, callable or None, default=None
195 | Only available for the "interactive" template.
196 | JavaScript or Python callback to be executed when clicking on an image.
197 | A dictionnary containing the data for the full cell is directly available
198 | as ``data`` in JS. For Python, the callback function must have ``data``
199 | as the first argument to the function. All the values in the ``data`` dict
200 | are parsed as strings, except "mols2grid-id" which is always an integer.
201 | Note that fields containing spaces in their name will be replaced by
202 | hyphens, i.e. "mol weight" becomes available as ``data["mol-weight"]``.
203 |
204 | Returns
205 | -------
206 | view : IPython.core.display.HTML
207 |
208 | Notes
209 | -----
210 | You can also directly use RDKit's :class:`~rdkit.Chem.Draw.rdMolDraw2D.MolDrawOptions`
211 | parameters as arguments.
212 | Additionally, ``atomColourPalette`` is available to customize the atom
213 | palette if you're not prerendering image (``prerender=False``).
214 |
215 | .. versionadded:: 0.1.0
216 | Added ``sort_by``, ``custom_css``, ``custom_header`` and ``callback``
217 | arguments. Added the ability to style an entire cell with
218 | ``style={"__all__": }``.
219 |
220 | .. versionadded:: 0.2.0
221 | Added ``substruct_highlight`` argument.
222 |
223 | .. versionchanged:: 0.2.2
224 | If both ``subset`` and ``tooltip`` are ``None``, the index and image
225 | will be directly displayed on the grid while the remaining fields will
226 | be in the tooltip.
227 |
228 | .. versionchanged:: 1.0.0
229 | ``callback`` can now be a *lambda* function. If ``prerender=True``,
230 | substructure highlighting will be automatically disabled if it wasn't
231 | explicitely set to ``True`` instead of raising an error.
232 |
233 | """
234 | raise TypeError(f"No display method registered for type {type(arg)!r}")
235 |
236 |
237 | @display.register(DataFrame)
238 | @display.register(dict)
239 | def _(df, **kwargs):
240 | template, kwargs, render_kwargs = _prepare_kwargs(kwargs, "display")
241 | return MolGrid(df, **kwargs).display(template=template, **render_kwargs)
242 |
243 |
244 | @display.register(str)
245 | @display.register(Path)
246 | def _(sdf, **kwargs):
247 | template, kwargs, render_kwargs = _prepare_kwargs(kwargs, "display")
248 | return MolGrid.from_sdf(sdf, **kwargs).display(template=template, **render_kwargs)
249 |
250 |
251 | @display.register(Series)
252 | @display.register(list)
253 | @display.register(tuple)
254 | def _(mols, **kwargs):
255 | template, kwargs, render_kwargs = _prepare_kwargs(kwargs, "display")
256 | return MolGrid.from_mols(mols, **kwargs).display(template=template, **render_kwargs)
257 |
258 |
259 | @singledispatch
260 | def save(arg, **kwargs):
261 | """Generate an interactive grid of molecules and save it.
262 |
263 | Parameters
264 | ----------
265 | arg : pandas.DataFrame, SDF file or list of molecules
266 | The input containing your molecules.
267 | output : str
268 | Name and path of the output document.
269 |
270 | Notes
271 | -----
272 | See :func:`display` for the full list of arguments.
273 | """
274 | raise TypeError(f"No save method registered for type {type(arg)!r}")
275 |
276 |
277 | @save.register(DataFrame)
278 | def _(df, **kwargs):
279 | template, kwargs, render_kwargs = _prepare_kwargs(kwargs, "save")
280 | output = kwargs.pop("output")
281 | return MolGrid(df, **kwargs).save(output, template=template, **render_kwargs)
282 |
283 |
284 | @save.register(str)
285 | @save.register(Path)
286 | def _(sdf, **kwargs):
287 | template, kwargs, render_kwargs = _prepare_kwargs(kwargs, "save")
288 | output = kwargs.pop("output")
289 | return MolGrid.from_sdf(sdf, **kwargs).save(
290 | output, template=template, **render_kwargs
291 | )
292 |
293 |
294 | @save.register(Series)
295 | @save.register(list)
296 | @save.register(tuple)
297 | def _(mols, **kwargs):
298 | template, kwargs, render_kwargs = _prepare_kwargs(kwargs, "save")
299 | output = kwargs.pop("output")
300 | return MolGrid.from_mols(mols, **kwargs).save(
301 | output, template=template, **render_kwargs
302 | )
303 |
--------------------------------------------------------------------------------
/mols2grid/nbextension/extension.js:
--------------------------------------------------------------------------------
1 | // Entry point for the notebook bundle containing custom model definitions.
2 | //
3 | define(function() {
4 | "use strict";
5 |
6 | window['requirejs'].config({
7 | map: {
8 | '*': {
9 | 'mols2grid': 'nbextensions/mols2grid/index',
10 | },
11 | }
12 | });
13 | // Export the required load_ipython_extension function
14 | return {
15 | load_ipython_extension : function() {}
16 | };
17 | });
--------------------------------------------------------------------------------
/mols2grid/select.py:
--------------------------------------------------------------------------------
1 | import warnings
2 | from ast import literal_eval
3 |
4 |
5 | class SelectionRegister:
6 | """Register for grid selections
7 |
8 | Attributes
9 | ----------
10 | SELECTIONS : dict
11 | Stores each grid selection according to their name
12 | current_selection : str
13 | Name of the most recently updated grid
14 | """
15 |
16 | def __init__(self):
17 | self.SELECTIONS = {}
18 |
19 | def _update_current_grid(self, name):
20 | self.current_selection = name
21 |
22 | def _init_grid(self, name):
23 | overwrite = self.SELECTIONS.get(name, False)
24 | if overwrite:
25 | warnings.warn(
26 | f"Overwriting non-empty {name!r} grid selection: {str(overwrite)}"
27 | )
28 | self.SELECTIONS[name] = {}
29 | self._update_current_grid(name)
30 |
31 | def selection_updated(self, name, event):
32 | self.SELECTIONS[name] = literal_eval(event.new)
33 | self._update_current_grid(name)
34 |
35 | def get_selection(self, name=None):
36 | """Returns the selection for a specific MolGrid instance
37 |
38 | Parameters
39 | ----------
40 | name : str or None
41 | Name of the grid to fetch the selection from. If `None`, the most
42 | recently updated grid is returned
43 | """
44 | name = self.current_selection if name is None else name
45 | return self.SELECTIONS[name]
46 |
47 | def list_grids(self):
48 | """Returns a list of grid names"""
49 | return list(self.SELECTIONS.keys())
50 |
51 | def _clear(self):
52 | """Clears all selections"""
53 | if hasattr(self, "current_selection"):
54 | del self.current_selection
55 | self.SELECTIONS.clear()
56 |
57 |
58 | register = SelectionRegister()
59 | get_selection = register.get_selection
60 | list_grids = register.list_grids
61 |
--------------------------------------------------------------------------------
/mols2grid/templates/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbouy/mols2grid/d156fc940c3db929409f5ea706b8f598d3eefc13/mols2grid/templates/__init__.py
--------------------------------------------------------------------------------
/mols2grid/templates/css/common.css:
--------------------------------------------------------------------------------
1 | /**
2 | * General styling
3 | */
4 | body {
5 | font-family: {{ fontfamily }};
6 | }
7 | h1,h2,h3,h4 {
8 | margin: 0 0 10px 0;
9 | }
10 | h1 {
11 | font-size: 26px;
12 | }
13 | h2 {
14 | font-size: 20px;
15 | font-weight: 400;
16 | }
17 | h3 {
18 | font-size: 16px;
19 | }
20 | p {
21 | margin: 0 0 10px 0;
22 | }
23 |
24 |
25 | /* Remove body margin inside iframe */
26 | body.m2g-inside-iframe {
27 | margin: 0;
28 | }
29 |
30 | /* In-cell text */
31 | #mols2grid .data:not(.data-img) {
32 | height: 16px;
33 | line-height: 16px;
34 | }
35 | /* Text truncation */
36 | #mols2grid .data {
37 | /* Break text into multiple lines (default for static)... */
38 | word-wrap: {{ 'break-word' if not truncate else 'normal' }};
39 |
40 | /* ...or truncate it (default for interactive). */
41 | overflow: {{ 'hidden' if truncate else 'visible' }};
42 | white-space: {{ 'nowrap' if truncate else 'normal' }};
43 | text-overflow: {{ 'ellipsis' if truncate else 'clip' }};
44 | }
45 |
46 |
47 | /**
48 | * Popover
49 | * - - -
50 | * Note: this is a bootstrap variable which is not namespaced.
51 | * To avoid any contamination, we only style it when the
52 | * x-placement parameter is set.
53 | */
54 | .popover[x-placement] {
55 | font-family: {{ fontfamily }};
56 | background: white;
57 | border: solid 1px rgba(0,0,0,.2);
58 | font-size: {{ fontsize }};
59 | padding: 10px;
60 | border-radius: 5px;
61 | box-shadow: 0 0 20px rgba(0,0,0,.15);
62 | user-select: none;
63 | }
64 | .popover[x-placement] h3 {
65 | margin: 0;
66 | }
67 | .popover[x-placement] .arrow {
68 | width: 10px;
69 | height: 10px;
70 | background: #fff;
71 | border: solid 1px rgba(0,0,0,.2);
72 | box-sizing: border-box;
73 | position: absolute;
74 | transform-origin: 5px 5px;
75 | clip-path: polygon(0 0, 100% 0, 100% 100%);
76 | }
77 | .popover[x-placement='left'] .arrow {
78 | transform: rotate(45deg);
79 | top: 50%;
80 | right: -5px;
81 | }
82 | .popover[x-placement='right'] .arrow {
83 | transform: rotate(-135deg);
84 | top: 50%;
85 | left: -5px;
86 | }
87 | .popover[x-placement='top'] .arrow {
88 | transform: rotate(135deg);
89 | left: 50%;
90 | bottom: -5px;
91 | }
92 | .popover[x-placement='bottom'] .arrow {
93 | transform: rotate(-45deg);
94 | left: 50%;
95 | top: -5px;
96 | }
--------------------------------------------------------------------------------
/mols2grid/templates/css/static.css:
--------------------------------------------------------------------------------
1 | /* Note: #mols2grid is the table element */
2 | #mols2grid {
3 | border-collapse: {{ 'collapse' if gap == 0 else 'separate' }};
4 | border-spacing: {{ gap }}px;
5 | /* Compensate for gap so table stays aligned with content above */
6 | margin: {{ 0 if gap == 0 else -gap }}px;
7 | }
8 | #mols2grid, tr, td {
9 | border: none;
10 | }
11 | #mols2grid tr {
12 | page-break-inside: avoid !important;
13 | }
14 |
15 | /* Cell */
16 | #mols2grid td {
17 | border: {{ border }};
18 | text-align: {{ textalign }};
19 | vertical-align: top;
20 | max-width: {{ cell_width }}px;
21 | width: {{ cell_width }}px;
22 | font-family: {{ fontfamily }};
23 | font-size: {{ fontsize }};
24 | margin: 0;
25 | padding: {{ pad }}px;
26 | position: relative;
27 | }
28 |
29 | /* Call focus state */
30 | #mols2grid td:focus-within,
31 | #mols2grid td:focus {
32 | outline: solid 2px #555;
33 | border-color: transparent;
34 | }
35 |
36 | /* ID */
37 | #mols2grid .data-mols2grid-id {
38 | position: absolute;
39 | top: 0;
40 | left: 0;
41 | padding: 5px;
42 | }
43 |
44 | /* Tooltip margin */
45 | /* Adjusted so it plays well with the extruded outline */
46 | .popover[x-placement] {
47 | margin: 1px 2px 2px 1px;
48 | }
49 |
50 |
51 | #mols2grid td div img {
52 | max-width: {{ image_width }}px;
53 | width: {{ image_width }}px;
54 | height: auto;
55 | padding: 0;
56 | }
--------------------------------------------------------------------------------
/mols2grid/templates/html/common_header.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/mols2grid/templates/html/iframe.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
29 |
--------------------------------------------------------------------------------
/mols2grid/templates/html/interactive_header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/mols2grid/templates/interactive.html:
--------------------------------------------------------------------------------
1 | {#