Skip to content

Commit

Permalink
Update 3.2.0 (#11)
Browse files Browse the repository at this point in the history
* update urls

* chore: remove comment

* feat: add custom tooltip support

* feat: clear tooltips

* feat: pass keepColors and keepRepresentations flags

* feat: allow changing visual style

* chore: remove comments

* chore: add query / reset typing

* chore: add color typed dict

* towards color for select and highlight setting

* feat: add rainbow residues solara example

* add tooltip example

* tweak example notebook
  • Loading branch information
Jhsmit authored May 10, 2024
1 parent 9c60cce commit e5b6098
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 113 deletions.
130 changes: 74 additions & 56 deletions example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,25 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"metadata": {},
"outputs": [],
"source": [
"view = PDBeMolstar(\n",
" molecule_id=\"1qyn\",\n",
" theme=\"light\",\n",
" hide_water=True,\n",
" visual_style=\"cartoon\",\n",
" spin=True,\n",
" spin=False,\n",
" lighting='glossy',\n",
")\n",
"\n",
"view.tooltips = { 'data': [\n",
" { 'struct_asym_id': 'A', 'tooltip': 'Custom tooltip for chain A' }, \n",
" { 'struct_asym_id': 'B', 'tooltip': 'Custom tooltip for chain B' }, \n",
" { 'struct_asym_id': 'C', 'tooltip': 'Custom tooltip for chain C' }, \n",
" { 'struct_asym_id': 'D', 'tooltip': 'Custom tooltip for chain D' }, \n",
" ] }\n",
"\n",
"view"
]
},
Expand All @@ -33,20 +40,7 @@
"metadata": {},
"outputs": [],
"source": [
"# load local files via custom_data\n",
"from pathlib import Path \n",
"fpth = Path().resolve() / 'assets' / '6vsb.bcif'\n",
"custom_data = {\n",
" 'data': fpth.read_bytes(),\n",
" 'format': 'cif',\n",
" 'binary': True,\n",
" }\n",
"view = PDBeMolstar(\n",
" custom_data=custom_data, \n",
" hide_water=True,\n",
" hide_carbs=True,\n",
")\n",
"view"
"view.clear_tooltips()"
]
},
{
Expand All @@ -55,7 +49,48 @@
"metadata": {},
"outputs": [],
"source": [
"view.molecule_id = \"1cbs\""
"# color residues\n",
"data = [\n",
" {\n",
" \"start_residue_number\": 0,\n",
" \"end_residue_number\": 153,\n",
" \"struct_asym_id\": \"A\",\n",
" \"color\": {\"r\": 100, \"g\": 150, \"b\": 120},\n",
" \"focus\": False,\n",
" },\n",
" {\n",
" \"start_residue_number\": 30,\n",
" \"end_residue_number\": 153,\n",
" \"struct_asym_id\": \"B\",\n",
" \"color\": {\"r\": 213, \"g\": 52, \"b\": 235},\n",
" \"focus\": False,\n",
" },\n",
"]\n",
"\n",
"view.color(data, non_selected_color={\"r\": 0, \"g\": 87, \"b\": 0})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# color residues, keep previous coloring\n",
"data = [\n",
" {\n",
" \"struct_asym_id\": \"C\",\n",
" \"color\": {\"r\": 255, \"g\": 255, \"b\": 0},\n",
" \"focus\": False,\n",
" },\n",
"]\n",
"\n",
"color_data = {\n",
" 'data': data, \n",
" \"nonSelectedColor\": None,\n",
" \"keepColors\": True\n",
"}\n",
"view.color_data = color_data"
]
},
{
Expand All @@ -64,7 +99,12 @@
"metadata": {},
"outputs": [],
"source": [
"view.hide_water = True"
"# keep current colors, change representation of chain D\n",
"data = [\n",
" {\"struct_asym_id\": \"D\",'representation': 'gaussian-surface', 'representationColor': '#ff00ff'}\n",
"]\n",
"color_data = {'data': data, \"keepColors\": True, 'keepRepresentations': False}\n",
"view.color_data = color_data"
]
},
{
Expand All @@ -73,7 +113,20 @@
"metadata": {},
"outputs": [],
"source": [
"view.hide_water"
"# load local files via custom_data\n",
"from pathlib import Path \n",
"fpth = Path().resolve() / 'assets' / '6vsb.bcif'\n",
"custom_data = {\n",
" 'data': fpth.read_bytes(),\n",
" 'format': 'cif',\n",
" 'binary': True,\n",
" }\n",
"view = PDBeMolstar(\n",
" custom_data=custom_data, \n",
" hide_water=True,\n",
" hide_carbs=True,\n",
")\n",
"view"
]
},
{
Expand Down Expand Up @@ -137,41 +190,6 @@
"view.clear_highlight()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# color residues\n",
"data = [\n",
" {\n",
" \"start_residue_number\": 0,\n",
" \"end_residue_number\": 153,\n",
" \"struct_asym_id\": \"A\",\n",
" \"color\": {\"r\": 100, \"g\": 150, \"b\": 120},\n",
" \"focus\": False,\n",
" },\n",
" {\n",
" \"start_residue_number\": 30,\n",
" \"end_residue_number\": 153,\n",
" \"struct_asym_id\": \"B\",\n",
" \"color\": {\"r\": 213, \"g\": 52, \"b\": 235},\n",
" \"focus\": False,\n",
" },\n",
"]\n",
"view.color(data, non_selected_color={\"r\": 0, \"g\": 87, \"b\": 0})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"view.loaded"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
97 changes: 97 additions & 0 deletions examples/rainbow_residues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import statistics
from dataclasses import asdict, dataclass
from io import StringIO
from pathlib import Path

import requests
import solara
import solara.lab
from Bio.PDB import PDBParser, Residue, Structure
from ipymolstar import THEMES, PDBeMolstar
from matplotlib import colormaps
from matplotlib.colors import Normalize
from solara.alias import rv

AMINO_ACIDS = [
"ALA",
"ARG",
"ASN",
"ASP",
"CYS",
"GLN",
"GLU",
"GLY",
"HIS",
"ILE",
"LEU",
"LYS",
"MET",
"PHE",
"PRO",
"PYL",
"SEC",
"SER",
"THR",
"TRP",
"TYR",
"VAL",
]

# %%

root = Path(__file__).parent.parent
pdb_path = root / "assets" / "1qyn.pdb"

parser = PDBParser(QUIET=True)
structure = parser.get_structure("1qyn", pdb_path)
MAX_R = max(r.id[1] for r in structure.get_residues())

# %%

custom_data = {"data": pdb_path.read_bytes(), "format": "pdb", "binary": False}


# %%
def color_residues(
structure: Structure.Structure, auth: bool = False, phase: int = 0
) -> dict:
_, resn, _ = zip(
*[r.id for r in structure.get_residues() if r.get_resname() in AMINO_ACIDS]
)

rmin, rmax = min(resn), max(resn)
# todo check for off by one errors
norm = Normalize(vmin=rmin, vmax=rmax)
auth_str = "_auth" if auth else ""

cmap = colormaps["hsv"]
data = []
for i in range(rmin, rmax + 1):
range_size = rmax + 1 - rmin
j = rmin + ((i - rmin + phase) % range_size)
r, g, b, a = cmap(norm(i), bytes=True)
color = {"r": int(r), "g": int(g), "b": int(b)}
elem = {
f"start{auth_str}_residue_number": j,
f"end{auth_str}_residue_number": j,
"color": color,
"focus": False,
}
data.append(elem)

color_data = {"data": data, "nonSelectedColor": None}
return color_data


@solara.component
def Page():
phase = solara.use_reactive(0)
color_data = color_residues(structure, auth=True, phase=phase.value)
with solara.Card():
PDBeMolstar.element(
custom_data=custom_data, hide_water=True, color_data=color_data
)
solara.FloatSlider(label="Phase", min=0, max=MAX_R, value=phase, step=1)


Page()
2 changes: 1 addition & 1 deletion src/ipymolstar/pdbe-dark.css
Original file line number Diff line number Diff line change
@@ -1 +1 @@
@import url('https://www.ebi.ac.uk/pdbe/pdb-component-library/css/pdbe-molstar-3.1.3.css');
@import url('https://cdn.jsdelivr.net/npm/pdbe-molstar@3.2.0/build/pdbe-molstar.css');
2 changes: 1 addition & 1 deletion src/ipymolstar/pdbe-light.css
Original file line number Diff line number Diff line change
@@ -1 +1 @@
@import url('https://www.ebi.ac.uk/pdbe/pdb-component-library/css/pdbe-molstar-light-3.1.3.css');
@import url('https://cdn.jsdelivr.net/npm/[email protected]/build/pdbe-molstar-light.css');
62 changes: 16 additions & 46 deletions src/ipymolstar/widget.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as myModule from "https://www.ebi.ac.uk/pdbe/pdb-component-library/js/pdbe-molstar-plugin-3.1.3.js";
// import * as myModule from "https://cdn.jsdelivr.net/npm/pdbe-molstar@latest/build/pdbe-molstar-plugin.js"
import * as myModule from "https://cdn.jsdelivr.net/npm/[email protected]/build/pdbe-molstar-plugin.js"

function standardize_color(str) {
var ctx = document.createElement("canvas").getContext("2d");
Expand Down Expand Up @@ -168,6 +167,12 @@ function render({ model, el }) {
viewerInstance.visual.select(selectValue);
}
},
"change:tooltips": () => {
const tooltipValue = model.get("tooltips");
if (tooltipValue !== null) {
viewerInstance.visual.tooltips(tooltipValue);
}
},
};

let otherCallbacks = {
Expand All @@ -177,6 +182,12 @@ function render({ model, el }) {
"change:custom_data": () => {
viewerInstance.visual.update(getOptions(model), true);
},
"change:visual_style": () => {
viewerInstance.visual.update(getOptions(model), true);
},
// "change:lighting": () => {
// viewerInstance.visual.update(getOptions(model), true);
// },
"change:bg_color": () => {
viewerInstance.canvas.setBgColor(toRgb(model.get("bg_color")));
},
Expand All @@ -186,6 +197,9 @@ function render({ model, el }) {
viewerInstance.visual.reset(resetValue);
}
},
"change:_clear_tooltips": () => {
viewerInstance.visual.clearTooltips();
}
};

let combinedCallbacks = Object.assign(
Expand All @@ -210,50 +224,6 @@ function render({ model, el }) {
model.save_changes();
});

// TODO return unsubscribe

// these require re-render
// model.on("change:visual_style", () => {
// viewerInstance.visual.update({visualStyle: model.get('visual_style')});
// console.log(model.get('visual_style'));
// });

// model.on("change:lighting", () => {
// viewerInstance.visual.update({lighting: model.get('lighting')});
// });

// model.on("change:_focus", () => {
// const focusValue = model.get("_focus");
// if (focusValue !== null) {
// viewerInstance.visual.focus(focusValue);
// }
// });
// model.on("change:_highlight", () => {
// const highlightValue = model.get("_highlight");
// if (highlightValue !== null) {
// viewerInstance.visual.highlight(highlightValue);
// }
// });
// model.on("change:_clear_highlight", () => {
// 1;
// viewerInstance.visual.clearHighlight();
// });
// model.on("change:_clear_selection", () => {
// viewerInstance.visual.clearSelection(model.get("_args")["number"]);
// });
// });
// model.on("change:_update", () => {
// const updateValue = model.get("_update");
// if (updateValue !== null) {
// viewerInstance.visual.update(updateValue);
// }

// });
// model.on("change:hide_coarse", () => {
// viewerInstance.visual.visibility({ water: !model.get("hide_coarse") });
// });

// this could be a loop?
return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe());
};
Expand Down
Loading

0 comments on commit e5b6098

Please sign in to comment.