From cd61c656b7d50781cb9ba22f7e8a5a08b3403c9d Mon Sep 17 00:00:00 2001 From: Jochem Smit Date: Fri, 29 Nov 2024 17:38:56 +0100 Subject: [PATCH] add click event support (#16) * add click event support * minor tweaks --- examples/click_events.py | 119 +++++++++++++++++++++++++++++++++++++++ src/ipymolstar/panel.py | 3 + src/ipymolstar/widget.js | 19 +++++++ src/ipymolstar/widget.py | 2 + 4 files changed, 143 insertions(+) create mode 100644 examples/click_events.py diff --git a/examples/click_events.py b/examples/click_events.py new file mode 100644 index 0000000..3e906a9 --- /dev/null +++ b/examples/click_events.py @@ -0,0 +1,119 @@ +""" +This example shows to how use click interactions +""" + +from string import Template + +import solara +from ipymolstar import PDBeMolstar + +url_template = Template( + "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/$cid/record/SDF?record_type=3d&response_type=save&response_basename=Conformer3D_COMPOUND_CID_$cid" +) + + +AA_LUT = { + "ARG": 6322, + "HIS": 6274, + "LYS": 5962, + "ASP": 5960, + "GLU": 33032, + "SER": 5951, + "THR": 6288, + "ASN": 6267, + "GLN": 5961, + "CYS": 5862, + "SEC": 6326983, + "GLY": 750, + "PRO": 145742, + "ALA": 5950, + "VAL": 6287, + "ILE": 6306, + "LEU": 6106, + "MET": 6137, + "PHE": 6140, + "TYR": 6057, + "TRP": 6305, +} + +custom_data_initial = dict( + url=url_template.substitute(cid=AA_LUT["TRP"]), + format="sdf", + binary=False, +) + + +molecule_id = "1QYN" + +# %% +s = """empty + -ISIS- + + 0 0 0 0 0 0 0 0 0 0999 V2000 +M END +$$$$ +""" + +b = s.encode() +empty_data = { + "data": b, + "format": "sdf", + "binary": True, +} + +# %% + + +@solara.component +def Page(): + solara.Title("ipymolstar - Click Events") + click_event = solara.use_reactive(None) + custom_data = solara.use_reactive(empty_data) + molecule_id = solara.use_reactive("1QYN") + amino_acid = solara.use_reactive("") + + def on_click(event): + click_event.set(event) + aa_tla = event["comp_id"] + + if aa_tla in AA_LUT: + amino_acid.set(aa_tla) + custom_data.set( + dict( + url=url_template.substitute(cid=AA_LUT[aa_tla]), + format="sdf", + binary=False, + ) + ) + else: + amino_acid.set("") + custom_data.set(empty_data) + + with solara.Sidebar(): + solara.InputText( + label="Molecule ID", value=molecule_id, on_value=molecule_id.set + ) + + with solara.Columns(): + with solara.Card(f"Protein: {molecule_id.value}"): + PDBeMolstar.element( # type: ignore + molecule_id=molecule_id.value.lower(), + hide_controls_icon=True, + hide_expand_icon=True, + hide_settings_icon=True, + hide_selection_icon=False, + select_interaction=False, # dont show local interactions on click + click_focus=False, # dont zoom on click + on_click_event=on_click, + ) + + with solara.Card(f"Amino Acid: {amino_acid.value}"): + PDBeMolstar.element( # type: ignore + molecule_id="", + custom_data=custom_data.value, + hide_controls_icon=True, + hide_expand_icon=True, + hide_settings_icon=True, + hide_selection_icon=False, + click_focus=False, + ) diff --git a/src/ipymolstar/panel.py b/src/ipymolstar/panel.py index b1b50ee..34b0821 100644 --- a/src/ipymolstar/panel.py +++ b/src/ipymolstar/panel.py @@ -104,8 +104,11 @@ class PDBeMolstar(AnyWidgetComponent): _reset = param.Dict(default=None) _update = param.Dict(default=None) _args = param.Dict(default={}) + mouseover_event = param.Dict(default={}) mouseout_event = param.Boolean(default=False) + click_event = param.Dict(default={}) + click_focus = param.Boolean(default=True) def __init__(self, theme="light", **params): _stylesheets = [THEMES[theme]["css"]] diff --git a/src/ipymolstar/widget.js b/src/ipymolstar/widget.js index fbebcab..e63b64e 100644 --- a/src/ipymolstar/widget.js +++ b/src/ipymolstar/widget.js @@ -87,6 +87,14 @@ function getCustomData(model) { return customData; } +const EmptyFocusBindings = { + clickCenterFocus: { triggers: [], action: '', description: '' }, + clickCenterFocusSelectMode: { triggers: [], action: '', description: '' }, + clickResetCameraOnEmpty: { triggers: [], action: '', description: '' }, + clickResetCameraOnEmptySelectMode: { triggers: [], action: '', description: '' } +}; + + function getOptions(model) { var options = { moleculeId: model.get("molecule_id"), @@ -108,6 +116,7 @@ function getOptions(model) { encoding: model.get("encoding"), lowPrecisionCoords: model.get("low_precision_coords"), selectInteraction: model.get("select_interaction"), + // selectBindings: EmptySelectBindings, granularity: model.get("granularity"), subscribeEvents: model.get("subscribe_events"), hideControls: model.get("hide_controls"), @@ -120,6 +129,10 @@ function getOptions(model) { reactive: model.get("reactive"), }; + if (model.get('click_focus') == false) { + options.focusBindings = EmptyFocusBindings; + } + return options; } @@ -246,6 +259,12 @@ function render({ model, el }) { model.save_changes(); }); + document.addEventListener("PDB.molstar.click", (e) => { + const eventData = e.eventData; + model.set("click_event", eventData); + model.save_changes(); + }); + return () => { unsubscribes.forEach((unsubscribe) => unsubscribe()); }; diff --git a/src/ipymolstar/widget.py b/src/ipymolstar/widget.py index c57f54e..3d7eb41 100644 --- a/src/ipymolstar/widget.py +++ b/src/ipymolstar/widget.py @@ -165,6 +165,8 @@ class PDBeMolstar(anywidget.AnyWidget): mouseover_event = traitlets.Dict().tag(sync=True) mouseout_event = traitlets.Bool().tag(sync=True) + click_event = traitlets.Dict().tag(sync=True) + click_focus = traitlets.Bool(True).tag(sync=True) def __init__(self, theme="light", **kwargs): _css = THEMES[theme]["css"]