From 33aec53cedd1912d63652ebbbcb86f69d9dd5cac Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sat, 7 Dec 2024 23:20:02 -0500 Subject: [PATCH 01/22] init --- js/src/event_handler.ts | 0 js/src/stage_manager.ts | 0 js/src/ui_manager.ts | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 js/src/event_handler.ts create mode 100644 js/src/stage_manager.ts create mode 100644 js/src/ui_manager.ts diff --git a/js/src/event_handler.ts b/js/src/event_handler.ts new file mode 100644 index 00000000..e69de29b diff --git a/js/src/stage_manager.ts b/js/src/stage_manager.ts new file mode 100644 index 00000000..e69de29b diff --git a/js/src/ui_manager.ts b/js/src/ui_manager.ts new file mode 100644 index 00000000..e69de29b From f16707383b84ce02271c109abf9852ef7da319e9 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sat, 7 Dec 2024 23:59:59 -0500 Subject: [PATCH 02/22] more --- js/src/embed_handler.ts | 80 +++++ js/src/event_handler.ts | 165 ++++++++++ js/src/representation_handler.ts | 107 +++++++ js/src/stage_manager.ts | 66 ++++ js/src/ui_manager.ts | 81 +++++ js/src/widget_ngl.ts | 527 +++---------------------------- 6 files changed, 550 insertions(+), 476 deletions(-) create mode 100644 js/src/embed_handler.ts create mode 100644 js/src/representation_handler.ts diff --git a/js/src/embed_handler.ts b/js/src/embed_handler.ts new file mode 100644 index 00000000..9bb619e0 --- /dev/null +++ b/js/src/embed_handler.ts @@ -0,0 +1,80 @@ + +import { NGLView } from "./widget_ngl"; + +export class EmbedHandler { + view: NGLView; + + constructor(view: NGLView) { + this.view = view; + } + + isEmbeded() { + return (this.view.model.get("_ngl_serialize") || (this.view.model.comm == undefined)); + } + + async handleEmbed() { + var that = this.view; + var ngl_msg_archive = that.model.get("_ngl_msg_archive"); + var ngl_stage_params = that.model.get('_ngl_full_stage_parameters'); + const camera_orientation = that.model.get("_camera_orientation"); + + if ( + Object.keys(ngl_stage_params).length === 0 + && camera_orientation.length === 0 + ) { + console.log("No state stored; initializing embedded widget for the first time."); + for (const msg of ngl_msg_archive) { + await that.on_msg(msg); + } + return; + } + + var loadfile_list = []; + + _.each(ngl_msg_archive, function (msg: any) { + if (msg.methodName == 'loadFile') { + if (msg.kwargs && msg.kwargs.defaultRepresentation) { + msg.kwargs.defaultRepresentation = false; + } + loadfile_list.push(that._getLoadFilePromise(msg)); + } + }); + + await Promise.all(loadfile_list); + that.stage.setParameters(ngl_stage_params); + that.set_camera_orientation(camera_orientation); + that.touch(); + + if (that.model.comm === undefined) { + var ngl_coordinate_resource = that.model.get("_ngl_coordinate_resource"); + var n_frames = ngl_coordinate_resource['n_frames'] || 1; + that.model.set("max_frame", n_frames - 1); + that.touch(); + var model = await that.getPlayerModel(); + var pmodel = model.get("children")[0]; + that.listenTo(pmodel, "change:value", function () { + that.updateCoordinatesFromDict(ngl_coordinate_resource, pmodel.get("value")); + }); + } + + for (const msg of that.model.get("_ngl_msg_archive")) { + if (msg.fire_embed) { + await that.on_msg(msg); + } + } + + that._set_representation_from_repr_dict(that.model.get("_ngl_repr_dict")); + that.handleResize(); + } + + _handleEmbedBeforeStage() { + var that = this.view; + var ngl_color_dict = that.model.get("_ngl_color_dict"); + var label; + for (label in ngl_color_dict) { + if (!NGL.ColormakerRegistry.hasScheme(label)) { + that.addColorScheme(ngl_color_dict[label], label); + } + } + } +} \ No newline at end of file diff --git a/js/src/event_handler.ts b/js/src/event_handler.ts index e69de29b..9bf764f0 100644 --- a/js/src/event_handler.ts +++ b/js/src/event_handler.ts @@ -0,0 +1,165 @@ +import { NGLView } from "./widget_ngl"; + +export class EventHandler { + view: NGLView; + + constructor(view: NGLView) { + this.view = view; + } + + handleSignals = () => { + var container = this.view.stage.viewer.container; + var that = this.view; + container.addEventListener('mouseover', (e) => { + that._ngl_focused = 1; + e; // linter + that.mouseOverDisplay('block'); + }, false); + + container.addEventListener('mouseout', (e) => { + that._ngl_focused = 0; + e; // linter + that.mouseOverDisplay('none'); + }, false); + + container.addEventListener('contextmenu', (e) => { + e.stopPropagation(); + e.preventDefault(); + }, true); + + this.view.stage.signals.componentAdded.add((component) => { + this.view.comp_uuids.push(component.uuid); + var len = this.view.stage.compList.length; + this.view.model.set("n_components", len); + this.view.touch(); + var comp = this.view.stage.compList[len - 1]; + comp.signals.representationRemoved.add(() => { + that.request_repr_dict(); + }); + comp.signals.representationAdded.add((repr) => { + that.request_repr_dict(); + repr.signals.parametersChanged.add(() => { + console.log("repr.parametersChanged"); + that.request_repr_dict(); + }); + }); + }, this.view); + + this.view.stage.signals.componentRemoved.add(async (component) => { + var that = this.view; + var cindex = this.view.comp_uuids.indexOf(component.uuid); + this.view.comp_uuids.splice(cindex, 1); + var n_components = this.view.stage.compList.length; + this.view.model.set("n_components", n_components); + this.view.touch(); + console.log('componentRemoved', component, component.uuid); + + var pviews = []; + for (var k in this.view.model.views) { + pviews.push(this.view.model.views[k]); + } + + var views = await Promise.all(pviews); + console.log(views); + var update_backend = false; + for (var k in views) { + var view = views[k]; + if ((view.uuid != that.uuid) && (view.stage.compList.length > n_components)) { + view.stage.removeComponent(view.stage.compList[cindex]); + update_backend = true; + } + } + if (update_backend) { + console.log("should update backend"); + that.send({ "type": "removeComponent", "data": cindex }); + } + }, this.view); + + this.view.stage.signals.parametersChanged.add(() => { + this.view.requestUpdateStageParameters(); + }, this.view); + + this.view.stage.viewerControls.signals.changed.add(() => { + setTimeout(() => { + this.view.serialize_camera_orientation(); + }, 100); + + var m = this.view.stage.viewerControls.getOrientation(); + if (that._synced_model_ids.length > 0 && that._ngl_focused == 1) { + that._synced_model_ids.forEach(async (mid) => { + var model = await that.model.widget_manager.get_model(mid); + for (var k in model.views) { + var view = await model.views[k]; + if (view.uuid != that.uuid) { + view.stage.viewerControls.orient(m); + } + } + }); + } + }); + } + + handleMessage = () => { + this.view.model.on("msg:custom", (msg) => { + this.view.on_msg(msg); + }, this.view); + + if (this.view.model.comm) { + this.view.model.comm.on_msg((msg) => { + var buffers = msg.buffers; + var content = msg.content.data.content; + if (buffers.length && content) { + content.buffers = buffers; + } + this.view.model._handle_comm_msg.call(this.view.model, msg); + }); + } + } + + handlePicking = () => { + this.view.$pickingInfo = $("
") + .css("position", "absolute") + .css("top", "5%") + .css("left", "3%") + .css("background-color", "white") + .css("padding", "2px 5px 2px 5px") + .css("opacity", "0.7") + .appendTo(this.view.$container); + + this.view.stage.signals.clicked.add((pd) => { + if (pd) { + this.view.model.set('picked', {}); //refresh signal + this.view.touch(); + + var pd2 = {} as any; + var pickingText = ""; + if (pd.atom) { + pd2.atom1 = pd.atom.toObject(); + pd2.atom1.name = pd.atom.qualifiedName(); + pickingText = "Atom: " + pd2.atom1.name; + } else if (pd.bond) { + pd2.bond = pd.bond.toObject(); + pd2.atom1 = pd.bond.atom1.toObject(); + pd2.atom1.name = pd.bond.atom1.qualifiedName(); + pd2.atom2 = pd.bond.atom2.toObject(); + pd2.atom2.name = pd.bond.atom2.qualifiedName(); + pickingText = "Bond: " + pd2.atom1.name + " - " + pd2.atom2.name; + } + if (pd.instance) pd2.instance = pd.instance; + + var n_components = this.view.stage.compList.length; + for (var i = 0; i < n_components; i++) { + var comp = this.view.stage.compList[i]; + if (comp.uuid == pd.component.uuid) { + pd2.component = i; + } + } + + this.view.model.set('picked', pd2); + this.view.touch(); + + this.view.$pickingInfo.text(pickingText); + } + }); + } +} \ No newline at end of file diff --git a/js/src/representation_handler.ts b/js/src/representation_handler.ts new file mode 100644 index 00000000..e0192966 --- /dev/null +++ b/js/src/representation_handler.ts @@ -0,0 +1,107 @@ + +import { NGLView } from "./widget_ngl"; + +export class RepresentationHandler { + view: NGLView; + + constructor(view: NGLView) { + this.view = view; + } + + requestReprParameters(component_index, repr_index) { + var comp = this.view.stage.compList[component_index]; + var repr = comp.reprList[repr_index]; + var msg = repr.repr.getParameters(); + + if (msg) { + msg['name'] = repr.name; + this.view.send({ + 'type': 'repr_parameters', + 'data': msg + }); + } + } + + request_repr_dict() { + var repr_dict = this.getReprDictFrontEnd(); + this.view.send({ + 'type': 'request_repr_dict', + 'data': repr_dict, + }); + var that = this.view; + if (that._synced_repr_model_ids.length > 0) { + that._synced_repr_model_ids.forEach(async function (mid) { + var model = await that.model.widget_manager.get_model(mid); + for (var k in model.views) { + var view = await model.views[k]; + if (view.uuid != that.uuid) { + view._set_representation_from_repr_dict(repr_dict); + } + } + }); + } + } + + getReprDictFrontEnd() { + var repr_dict = {}; + var n_components = this.view.stage.compList.length; + for (var i = 0; i < n_components; i++) { + var comp = this.view.stage.compList[i]; + repr_dict[i] = {}; + var msgi = repr_dict[i]; + for (var j = 0; j < comp.reprList.length; j++) { + var repr = comp.reprList[j]; + msgi[j] = {}; + msgi[j]['type'] = repr.name; + msgi[j]['params'] = repr.repr.getParameters(); + } + } + return repr_dict; + } + + syncReprForAllViews() { + var repr_dict_backend = this.view.model.get("_ngl_repr_dict"); + var repr_dict_frontend = this.getReprDictFrontEnd(); + if (JSON.stringify(repr_dict_frontend) !== JSON.stringify(repr_dict_backend)) { + this._set_representation_from_repr_dict(repr_dict_backend); + } + } + + async syncReprWithMe() { + var that = this.view; + var repr_dict = this.getReprDictFrontEnd(); + for (var k in this.view.model.views) { + var v = await this.view.model.views[k]; + if (v.uuid != that.uuid) { + v._set_representation_from_repr_dict(repr_dict); + } + } + this.request_repr_dict(); + } + + setSyncRepr(model_ids) { + this.view._synced_repr_model_ids = model_ids; + } + + set_representation_from_backend() { + var repr_dict = this.view.model.get('_ngl_repr_dict'); + this._set_representation_from_repr_dict(repr_dict); + } + + _set_representation_from_repr_dict(repr_dict) { + var compList = this.view.stage.compList; + if (compList.length > 0) { + for (var index in repr_dict) { + var comp = compList[index]; + comp.removeAllRepresentations(); + var reprlist = repr_dict[index]; + for (var j in reprlist) { + var repr = reprlist[j]; + if (repr) { + comp.addRepresentation(repr.type, repr.params); + } + } + } + } + } +} \ No newline at end of file diff --git a/js/src/stage_manager.ts b/js/src/stage_manager.ts index e69de29b..ed7da3f5 100644 --- a/js/src/stage_manager.ts +++ b/js/src/stage_manager.ts @@ -0,0 +1,66 @@ +import * as NGL from "ngl"; +import { NGLView } from "./widget_ngl"; + +export class StageManager { + stage: NGL.Stage; + view: NGLView; + + constructor(view: NGLView) { + this.view = view; + } + + createStage() { + var stage_params = { + ...this.view.model.get("_ngl_full_stage_parameters") + }; + if (!("backgroundColor" in stage_params)) { + stage_params["backgroundColor"] = "white"; + } + this.stage = new NGL.Stage(undefined); + this.view.$container = $(this.stage.viewer.container); + this.view.$el.append(this.view.$container); + this.stage.setParameters(stage_params); + this.view.$container = $(this.stage.viewer.container); + this.view.handleResizable(); + this.view.ngl_view_id = this.view.uuid; + this.view.touch(); + var that = this.view; + var width = this.view.model.get("_view_width") || this.view.$el.parent().width() + "px"; + var height = this.view.model.get("_view_height") || "300px"; + this.setSize(width, height); + this.view.uiManager.createFullscreenBtn(); + this.view.uiManager.createIPlayer(); + this.view.GUIStyleChanged(); + + this.view.$container.resizable( + "option", "maxWidth", this.view.$el.parent().width() + ); + if (this.view.isEmbeded()) { + console.log("Embed mode for NGLView"); + that.handleEmbed(); + } else { + this.view.requestUpdateStageParameters(); + if (this.view.model.views.length == 1) { + this.view.serialize_camera_orientation(); + } else { + this.view.set_camera_orientation(that.model.get("_camera_orientation")); + } + } + } + + handleResize() { + var width = this.view.$el.width(); + var height = this.view.$el.height() + "px"; + if (this.view.stage_widget) { + width = width - $(this.view.stage_widget.sidebar.dom).width(); + } + width = width + "px"; + this.setSize(width, height); + } + + setSize(width: string, height: string) { + this.stage.viewer.container.style.width = width; + this.stage.viewer.container.style.height = height; + this.stage.handleResize(); + } +} \ No newline at end of file diff --git a/js/src/ui_manager.ts b/js/src/ui_manager.ts index e69de29b..a7cd7ba5 100644 --- a/js/src/ui_manager.ts +++ b/js/src/ui_manager.ts @@ -0,0 +1,81 @@ +import { StageManager } from "./stage_manager"; +import { NGLView } from "./widget_ngl"; + +export class UIManager { + view: NGLView; + + constructor(view: NGLView) { + this.view = view; + } + + async createFullscreenBtn() { + this.view.btn_pview_fullscreen = this.view.createView("_ibtn_fullscreen"); + var view = await this.view.btn_pview_fullscreen; + var stage = this.view.stage; + + var pe = view.el; + pe.style.position = 'absolute'; + pe.style.zIndex = 100; + pe.style.top = '5%'; + pe.style.right = '5%'; + pe.style.opacity = '0.7'; + pe.style.width = '35px'; + pe.style.background = 'white'; + pe.style.opacity = '0.3'; + pe.style.display = 'none'; + pe.onclick = function () { + this.view.stage.toggleFullscreen(); + }.bind(this); + stage.viewer.container.append(view.el); + stage.signals.fullscreenChanged.add((isFullscreen) => { + if (isFullscreen) { + view.model.set("icon", "compress"); + } else { + view.model.set("icon", "expand"); + } + }); + } + + async createIPlayer() { + this.view.player_pview = this.view.createView("_iplayer"); + var view = await this.view.player_pview; + var that = this.view; + var pe = view.el; + pe.style.position = 'absolute'; + pe.style.zIndex = 100; + pe.style.bottom = '5%'; + pe.style.left = '10%'; + pe.style.opacity = '0.7'; + that.stage.viewer.container.append(view.el); + pe.style.display = 'none'; + } + + async createImageBtn() { + this.view.image_btn_pview = this.view.createView("_ibtn_image"); + var view = await this.view.image_btn_pview; + var pe = view.el; + pe.style.position = 'absolute'; + pe.style.zIndex = 100; + pe.style.top = '5%'; + pe.style.right = '10%'; + pe.style.opacity = '0.7'; + pe.style.width = '35px'; + this.view.stage.viewer.container.append(view.el); + } + + async createGUI() { + this.view.pgui_view = this.view.createView("_igui"); + var view = await this.view.pgui_view; + var pe = view.el; + pe.style.position = 'absolute'; + pe.style.zIndex = 100; + pe.style.top = '5%'; + pe.style.right = '10%'; + pe.style.width = '300px'; + this.view.stage.viewer.container.append(view.el); + } + + createNglGUI() { + this.view.stage_widget = new StageManager(this.view); + } +} \ No newline at end of file diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 1220a738..02afef3b 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -15,6 +15,12 @@ import { FullscreenModel, FullscreenView } from "./fullscreen" import { ColormakerRegistryModel, ColormakerRegistryView } from "./color" import { ThemeManagerModel, ThemeManagerView } from "./theme" import { MolstarModel, MolstarView } from "./molstarview/widget" +import { StageManager } from "./stage_manager" +import { EventHandler } from "./event_handler" +import { UIManager } from "./ui_manager" +import { EmbedHandler } from "./embed_handler" +import { RepresentationHandler } from "./representation_handler" +import { MessageHandler } from "./message_handler" // From NGL @@ -56,19 +62,31 @@ export class NGLModel extends widgets.DOMWidgetModel { } } -export - class NGLView extends widgets.DOMWidgetView { - stage: NGL.Stage +export class NGLView extends widgets.DOMWidgetView { + stageManager: StageManager; + eventHandler: EventHandler; + uiManager: UIManager; + embedHandler: EmbedHandler; + representationHandler: RepresentationHandler; + messageHandler: MessageHandler; + + constructor(options: any) { + super(options); + this.stageManager = new StageManager(this); + this.eventHandler = new EventHandler(this); + this.uiManager = new UIManager(this); + this.embedHandler = new EmbedHandler(this); + this.representationHandler = new RepresentationHandler(this); + this.messageHandler = new MessageHandler(this); + } render() { this.beforeDisplay(); this.displayed.then(() => { - // move all below code inside 'displayed' - // to make sure the NGLView and NGLModel are created - this.createStage(); - this.handlePicking(); - this.handleSignals(); - this.handleMessage(); + this.stageManager.createStage(); + this.eventHandler.handlePicking(); + this.eventHandler.handleSignals(); + this.eventHandler.handleMessage(); this.finalizeDisplay(); }); } @@ -77,82 +95,18 @@ export this.model.on("change:_parameters", this.parametersChanged, this); this.model.on("change:gui_style", this.GUIStyleChanged, this); this.model.set('_ngl_version', NGL.Version); - this._ngl_focused = 0 - this.uuid = generateUUID() - this.stage_widget = undefined - this.comp_uuids = [] + this._ngl_focused = 0; + this.uuid = generateUUID(); + this.stage_widget = undefined; + this.comp_uuids = []; this._synced_model_ids = this.model.get("_synced_model_ids"); - this._synced_repr_model_ids = this.model.get("_synced_repr_model_ids") + this._synced_repr_model_ids = this.model.get("_synced_repr_model_ids"); - if (this.isEmbeded()) { - // embed mode - this._handleEmbedBeforeStage() + if (this.embedHandler.isEmbeded()) { + this.embedHandler._handleEmbedBeforeStage(); } } - createStage() { - // init NGL stage - var stage_params = { - // Shallow copy so that _ngl_full_stage_parameters is not updated yet - ...this.model.get("_ngl_full_stage_parameters") - }; - if (!("backgroundColor" in stage_params)) { - stage_params["backgroundColor"] = "white" - } - this.stage = new NGL.Stage(undefined) - this.$container = $(this.stage.viewer.container); - this.$el.append(this.$container) - this.stage.setParameters(stage_params); - this.$container = $(this.stage.viewer.container); - this.handleResizable() - this.ngl_view_id = this.uuid - this.touch(); - var that = this; - var width = this.model.get("_view_width") || this.$el.parent().width() + "px"; - var height = this.model.get("_view_height") || "300px"; - this.setSize(width, height); - this.createFullscreenBtn(); // FIXME: move up? - this.createIPlayer(); // FIXME: move up? - this.GUIStyleChanged(); // must be called after displaying to get correct width and height - - this.$container.resizable( - "option", "maxWidth", this.$el.parent().width() - ); - if (this.isEmbeded()) { - console.log("Embed mode for NGLView") - that.handleEmbed(); - } else { - this.requestUpdateStageParameters(); - if (this.model.views.length == 1) { - this.serialize_camera_orientation(); - } else { - this.set_camera_orientation(that.model.get("_camera_orientation")); - } - } - } - - isEmbeded() { - return (this.model.get("_ngl_serialize") || (this.model.comm == undefined)) - } - - handleMessage() { - this.model.on("msg:custom", function (msg) { - this.on_msg(msg); - }, this); - - if (this.model.comm) { - this.model.comm.on_msg(function (msg) { - var buffers = msg.buffers; - var content = msg.content.data.content; - if (buffers.length && content) { - content.buffers = buffers; - } - this.model._handle_comm_msg.call(this.model, msg); - }.bind(this)); - } - } - - finalizeDisplay() { // for callbacks from Python // must be after initializing NGL.Stage @@ -176,149 +130,6 @@ export // this.touch() } - handleSignals() { - var container = this.stage.viewer.container; - var that = this; - container.addEventListener('mouseover', function (e) { - that._ngl_focused = 1; - e; // linter - that.mouseOverDisplay('block') - }, false); - - container.addEventListener('mouseout', function (e) { - that._ngl_focused = 0; - e; // linter - that.mouseOverDisplay('none') - }, false); - - container.addEventListener('contextmenu', function (e) { - e.stopPropagation(); - e.preventDefault(); - }, true); - - this.stage.signals.componentAdded.add(function (component) { - this.comp_uuids.push(component.uuid) - var len = this.stage.compList.length; - this.model.set("n_components", len); - this.touch(); - var comp = this.stage.compList[len - 1]; - comp.signals.representationRemoved.add(function () { - that.request_repr_dict(); - }); - comp.signals.representationAdded.add(function (repr) { - that.request_repr_dict(); - repr.signals.parametersChanged.add(function () { - console.log("repr.parametersChanged") - that.request_repr_dict(); - }) - }); - }, this); - - this.stage.signals.componentRemoved.add(async function (component) { - var that = this - var cindex = this.comp_uuids.indexOf(component.uuid) - this.comp_uuids.splice(cindex, 1) - var n_components = this.stage.compList.length - this.model.set("n_components", n_components) - this.touch() - console.log('componentRemoved', component, component.uuid) - - var pviews = [] - for (var k in this.model.views) { - pviews.push(this.model.views[k]) - } - - var views = await Promise.all(pviews) - console.log(views) - var update_backend = false - for (var k in views) { - var view = views[k] - if ((view.uuid != that.uuid) && (view.stage.compList.length > n_components)) { - // remove component from NGL's GUI - // pass - view.stage.removeComponent(view.stage.compList[cindex]) - update_backend = true - } - } - if (update_backend) { - console.log("should update backend") - that.send({ "type": "removeComponent", "data": cindex }) - } - }, this); - - this.stage.signals.parametersChanged.add(function () { - this.requestUpdateStageParameters(); - }, this); - - this.stage.viewerControls.signals.changed.add(function () { - setTimeout(() => { - // https://github.com/nglviewer/nglview/issues/948#issuecomment-898121063 - this.serialize_camera_orientation(); - }, 100); - - var m = this.stage.viewerControls.getOrientation(); - if (that._synced_model_ids.length > 0 && that._ngl_focused == 1) { - that._synced_model_ids.forEach(async function (mid) { - var model = await that.model.widget_manager.get_model(mid) - for (var k in model.views) { - var view = await model.views[k] - if (view.uuid != that.uuid) { - view.stage.viewerControls.orient(m); - } - } - }) - } - }.bind(this)); - - } - - handlePicking() { - this.$pickingInfo = $("
") - .css("position", "absolute") - .css("top", "5%") - .css("left", "3%") - .css("background-color", "white") - .css("padding", "2px 5px 2px 5px") - .css("opacity", "0.7") - .appendTo(this.$container); - - this.stage.signals.clicked.add((pd) => { - if (pd) { - this.model.set('picked', {}); //refresh signal - this.touch(); - - var pd2 = {} as any; - var pickingText = ""; - if (pd.atom) { - pd2.atom1 = pd.atom.toObject(); - pd2.atom1.name = pd.atom.qualifiedName(); - pickingText = "Atom: " + pd2.atom1.name; - } else if (pd.bond) { - pd2.bond = pd.bond.toObject(); - pd2.atom1 = pd.bond.atom1.toObject(); - pd2.atom1.name = pd.bond.atom1.qualifiedName(); - pd2.atom2 = pd.bond.atom2.toObject(); - pd2.atom2.name = pd.bond.atom2.qualifiedName(); - pickingText = "Bond: " + pd2.atom1.name + " - " + pd2.atom2.name; - } - if (pd.instance) pd2.instance = pd.instance; - - var n_components = this.stage.compList.length; - for (var i = 0; i < n_components; i++) { - var comp = this.stage.compList[i]; - if (comp.uuid == pd.component.uuid) { - pd2.component = i; - } - } - - this.model.set('picked', pd2); - this.touch(); - - this.$pickingInfo.text(pickingText); - } - }); - } - async mouseOverDisplay(type) { var that = this; if (this.btn_pview_fullscreen) { @@ -372,87 +183,6 @@ export eval(code); } - _handleEmbedBeforeStage() { - // Only need to reconstruct colors in embeding mode (outside notebook) - // FIXME: remove this function - var that = this - var ngl_color_dict = that.model.get("_ngl_color_dict"); - var label - // Old API (_ColorScheme) - for (label in ngl_color_dict) { - if (!NGL.ColormakerRegistry.hasScheme(label)) { - that.addColorScheme(ngl_color_dict[label], label); - } - } - } - - async handleEmbed() { - var that = this; - var ngl_msg_archive = that.model.get("_ngl_msg_archive"); - var ngl_stage_params = that.model.get('_ngl_full_stage_parameters'); - const camera_orientation = that.model.get("_camera_orientation"); - - if ( - Object.keys(ngl_stage_params).length === 0 - && camera_orientation.length === 0 - ) { - console.log("No state stored; initializing embedded widget for the first time."); - for (const msg of ngl_msg_archive) { - await that.on_msg(msg); - } - return - } - - var loadfile_list = []; - - _.each(ngl_msg_archive, function (msg: any) { - if (msg.methodName == 'loadFile') { - if (msg.kwargs && msg.kwargs.defaultRepresentation) { - // no need to add default representation as all representations - // are serialized separately, also it unwantedly sets the orientation - msg.kwargs.defaultRepresentation = false - } - loadfile_list.push(that._getLoadFilePromise(msg)); - } - }); - - - await Promise.all(loadfile_list) - that.stage.setParameters(ngl_stage_params); - that.set_camera_orientation(camera_orientation); - that.touch(); - - // Outside notebook - if (that.model.comm === undefined) { - var ngl_coordinate_resource = that.model.get("_ngl_coordinate_resource"); - var n_frames = ngl_coordinate_resource['n_frames'] || 1 - that.model.set("max_frame", n_frames - 1); // trigger updating slider and player's max - that.touch() - var model = await that.getPlayerModel() - var pmodel = model.get("children")[0]; - that.listenTo(pmodel, - "change:value", function () { - that.updateCoordinatesFromDict(ngl_coordinate_resource, - pmodel.get("value")) - }) - } - - - // fire any msg with "fire_embed" - for (const msg of that.model.get("_ngl_msg_archive")) { - if (msg.fire_embed) { - await that.on_msg(msg); - } - } - - // Must call _set_representation_from_repr_dict after "fire_embed" - // User might add Shape (buffer component) to the view and the buffer component - // is not created yet via loadFile - // https://github.com/nglviewer/nglview/issues/1003 - that._set_representation_from_repr_dict(that.model.get("_ngl_repr_dict")) - that.handleResize() // FIXME: really need this? - } - updateCoordinatesFromDict(cdict, frame_index) { // update coordinates for given "index" // cdict = Dict[int, List[base64]] @@ -481,87 +211,31 @@ export } requestReprParameters(component_index, repr_index) { - var comp = this.stage.compList[component_index]; - var repr = comp.reprList[repr_index]; - var msg = repr.repr.getParameters(); - - if (msg) { - msg['name'] = repr.name; - this.send({ - 'type': 'repr_parameters', - 'data': msg - }); - } + this.representationHandler.requestReprParameters(component_index, repr_index); } request_repr_dict() { - var repr_dict = this.getReprDictFrontEnd() - this.send({ - // make sure we are using "request_repr_dict" name - // in backend too. - 'type': 'request_repr_dict', - 'data': repr_dict, - }); - var that = this - if (that._synced_repr_model_ids.length > 0) { - that._synced_repr_model_ids.forEach(async function (mid) { - var model = await that.model.widget_manager.get_model(mid) - for (var k in model.views) { - var view = await model.views[k]; - // not sync with itself - if (view.uuid != that.uuid) { - view._set_representation_from_repr_dict(repr_dict) - } - } - }) - } + this.representationHandler.request_repr_dict(); } getReprDictFrontEnd() { - var repr_dict = {}; - var n_components = this.stage.compList.length; - for (var i = 0; i < n_components; i++) { - var comp = this.stage.compList[i]; - repr_dict[i] = {}; - var msgi = repr_dict[i]; - for (var j = 0; j < comp.reprList.length; j++) { - var repr = comp.reprList[j]; - msgi[j] = {}; - msgi[j]['type'] = repr.name; - msgi[j]['params'] = repr.repr.getParameters(); - } - } - return repr_dict + return this.representationHandler.getReprDictFrontEnd(); } syncReprForAllViews() { - var repr_dict_backend = this.model.get("_ngl_repr_dict") - var repr_dict_frontend = this.getReprDictFrontEnd() - if (JSON.stringify(repr_dict_frontend) !== JSON.stringify(repr_dict_backend)) { - this._set_representation_from_repr_dict(repr_dict_backend) - } + this.representationHandler.syncReprForAllViews(); } async syncReprWithMe() { - // Make sure views of the same model has the same representations - // Only needed if we use Sidebar that connects to specific view. - var that = this - var repr_dict = this.getReprDictFrontEnd() - for (var k in this.model.views) { - var v = await this.model.views[k] - if (v.uuid != that.uuid) { - v._set_representation_from_repr_dict(repr_dict) - } - } - this.request_repr_dict() + await this.representationHandler.syncReprWithMe(); } setSyncRepr(model_ids) { - this._synced_repr_model_ids = model_ids + this.representationHandler.setSyncRepr(model_ids); } setSyncCamera(model_ids) { - this._synced_model_ids = model_ids + this._synced_model_ids = model_ids; } viewXZPlane() { @@ -571,25 +245,11 @@ export } set_representation_from_backend() { - var repr_dict = this.model.get('_ngl_repr_dict') - this._set_representation_from_repr_dict(repr_dict) + this.representationHandler.set_representation_from_backend(); } _set_representation_from_repr_dict(repr_dict) { - var compList = this.stage.compList - if (compList.length > 0) { - for (var index in repr_dict) { - var comp = compList[index]; - comp.removeAllRepresentations(); - var reprlist = repr_dict[index]; - for (var j in reprlist) { - var repr = reprlist[j]; - if (repr) { - comp.addRepresentation(repr.type, repr.params); - } - } - } - } + this.representationHandler._set_representation_from_repr_dict(repr_dict); } async createView(trait_name) { @@ -692,6 +352,7 @@ export } removeRepresentation(component_index, repr_index) { + // value = True/False var component = this.stage.compList[component_index]; var repr = component.reprList[repr_index] @@ -854,7 +515,7 @@ export i, p = 0, encoded1, encoded2, encoded3, encoded4; - if (base64[base64.length - 1] === "=") { + if (base64[base.length - 1] === "=") { bufferLength--; if (base64[base64.length - 2] === "=") { bufferLength--; @@ -976,7 +637,6 @@ export } async handleMovieMaking(render_params) { - console.log('handleMovieMaking: render_params', render_params) if (this.ngl_view_id == this.get_last_child_id()) { var blob = await this.stage.makeImage(render_params) var reader = new FileReader(); @@ -1058,112 +718,27 @@ export } async on_msg(msg) { - if (msg.type === 'call_method') { - await this.handleCallMethod(msg); - } else if (msg.type === 'base64_single') { - this.handleBase64Single(msg.data); - } else if (msg.type === 'binary_single') { - this.handleBinarySingle(msg); - } else if (msg.type === 'movie_image_data') { - this.handleMovieMaking(msg.render_params); - } else if (msg.type === 'get') { - this.handleGetRequest(msg.data); - } + this.messageHandler.on_msg(msg); } async handleCallMethod(msg) { - var index, component, func, stage; - var new_args = msg.args.slice(); - new_args.push(msg.kwargs); - - if (msg.methodName === 'addRepresentation' && msg.reconstruc_color_scheme) { - msg.kwargs.color = this.addColorScheme(msg.kwargs.color, msg.kwargs.color_label); - } - - // FIXME: Property 'volume' does not exist on type 'Component'. - // if ("colorVolume" in msg.kwargs) { - // index = msg.kwargs["colorVolume"]; - // msg.kwargs["colorVolume"] = this.stage.compList[index].volume; - // } - - switch (msg.target) { - case 'Stage': - await this.handleStageMethod(msg, new_args); - break; - case 'Viewer': - this.stage.viewer[msg.methodName].apply(this.stage.viewer, new_args); - break; - case 'viewerControls': - this.stage.viewerControls[msg.methodName].apply(this.stage.viewerControls, new_args); - break; - case 'compList': - component = this.stage.compList[msg.component_index]; - component[msg.methodName].apply(component, new_args); - break; - case 'Widget': - this[msg.methodName].apply(this, new_args); - break; - case 'Representation': - component = this.stage.compList[msg.component_index]; - var repr = component.reprList[msg.repr_index]; - repr[msg.methodName].apply(repr, new_args); - break; - default: - console.log('Unknown target: ' + msg.target); - break; - } + this.messageHandler.handleCallMethod(msg); } async handleStageMethod(msg, new_args) { - var stage_func = this.stage[msg.methodName]; - if (msg.methodName === 'removeComponent') { - var component = this.stage.compList[msg.args[0]]; - this.stage.removeComponent(component); - } else if (msg.methodName === 'loadFile') { - if (this.model.views.length > 1 && msg.kwargs && msg.kwargs.defaultRepresentation) { - msg.kwargs.defaultRepresentation = false; - } - await this._handleStageLoadFile(msg); - } else { - stage_func.apply(this.stage, new_args); - } + this.messageHandler.handleStageMethod(msg, new_args); } handleBase64Single(coordinatesDict) { - var keys = Object.keys(coordinatesDict); - for (var i = 0; i < keys.length; i++) { - var traj_index = parseInt(keys[i], 10); - var coordinates = this.decode_base64(coordinatesDict[traj_index]); - if (coordinates && coordinates.byteLength > 0) { - this.updateCoordinates(coordinates, traj_index); - } - } + this.messageHandler.handleBase64Single(coordinatesDict); } handleBinarySingle(msg) { - var coordinateMeta = msg.data; - var keys = Object.keys(coordinateMeta); - for (var i = 0; i < keys.length; i++) { - var traj_index = parseInt(keys[i], 10); - var coordinates = new Float32Array(msg.buffers[i].buffer); - if (coordinates.byteLength > 0) { - this.updateCoordinates(coordinates, traj_index); - } - } - if (msg.movie_making) { - this.handleMovieMaking(msg.render_params); - } + this.messageHandler.handleBinarySingle(msg); } handleGetRequest(data) { - if (data === 'camera') { - this.send(JSON.stringify(this.stage.viewer.camera)); - } else if (data === 'parameters') { - this.send(JSON.stringify(this.stage.parameters)); - } else { - console.log("Number of components", this.stage.compList.length); - console.log("ngl_view_id", this.ngl_view_id); - } + this.messageHandler.handleGetRequest(data); } } From 2f785f262e0689e15f6ff1b3dd4a82fc7a665335 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 11:43:49 -0500 Subject: [PATCH 03/22] more --- js/src/widget_ngl.ts | 49 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 02afef3b..37a33b62 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -342,56 +342,23 @@ export class NGLView extends widgets.DOMWidgetView { setVisibilityForRepr(component_index, repr_index, value) { - // value = True/False - var component = this.stage.compList[component_index]; - var repr = component.reprList[repr_index]; - - if (repr) { - repr.setVisibility(value); - } + this.representationHandler.setVisibilityForRepr(component_index, repr_index, value); } removeRepresentation(component_index, repr_index) { - // value = True/False - var component = this.stage.compList[component_index]; - var repr = component.reprList[repr_index] - - if (repr) { - component.removeRepresentation(repr); - } + this.representationHandler.removeRepresentation(component_index, repr_index); } - removeRepresentationsByName = (repr_name, component_index) => { - var component = this.stage.compList[component_index]; - - if (component) { - component.reprList.forEach((repr) => { - if (repr.name == repr_name) { - component.removeRepresentation(repr); - } - }); - } + removeRepresentationsByName(repr_name, component_index) { + this.representationHandler.removeRepresentationsByName(repr_name, component_index); } - updateRepresentationForComponent = (repr_index, component_index, params) => { - var component = this.stage.compList[component_index]; - var repr = component.reprList[repr_index]; - if (repr) { - repr.setParameters(params); - } + updateRepresentationForComponent(repr_index, component_index, params) { + this.representationHandler.updateRepresentationForComponent(repr_index, component_index, params); } - updateRepresentationsByName = (repr_name: string, component_index: number, params: any): void => { - const component = this.stage.compList[component_index]; - - if (component) { - component.reprList.forEach((repr: any) => { - if (repr.name === repr_name) { - repr.setParameters(params); - this.request_repr_dict(); - } - }); - } + updateRepresentationsByName(repr_name, component_index, params) { + this.representationHandler.updateRepresentationsByName(repr_name, component_index, params); } setColorByResidue = (colors, component_index, repr_index) => { From 7db365e8c0dcfaa5197f01d1955ee4c28d2853d9 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 11:48:13 -0500 Subject: [PATCH 04/22] fix --- js/src/embed_handler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/embed_handler.ts b/js/src/embed_handler.ts index 9bb619e0..cd1476f9 100644 --- a/js/src/embed_handler.ts +++ b/js/src/embed_handler.ts @@ -1,5 +1,6 @@ import { NGLView } from "./widget_ngl"; +import * as NGL from 'ngl'; export class EmbedHandler { view: NGLView; From f3bc82f0df159c50eb41f780613447bdd0f2c30c Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 11:56:47 -0500 Subject: [PATCH 05/22] fix --- js/src/embed_handler.ts | 2 +- js/src/representation_handler.ts | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/js/src/embed_handler.ts b/js/src/embed_handler.ts index cd1476f9..29171374 100644 --- a/js/src/embed_handler.ts +++ b/js/src/embed_handler.ts @@ -1,6 +1,6 @@ - import { NGLView } from "./widget_ngl"; import * as NGL from 'ngl'; +import * as _ from 'lodash'; export class EmbedHandler { view: NGLView; diff --git a/js/src/representation_handler.ts b/js/src/representation_handler.ts index e0192966..76b9cd1d 100644 --- a/js/src/representation_handler.ts +++ b/js/src/representation_handler.ts @@ -1,4 +1,3 @@ - import { NGLView } from "./widget_ngl"; export class RepresentationHandler { @@ -104,4 +103,35 @@ export class RepresentationHandler { } } } + + setVisibilityForRepr(component_index, repr_index, value) { + var comp = this.view.stage.compList[component_index]; + var repr = comp.reprList[repr_index]; + repr.setVisibility(value); + } + + removeRepresentation(component_index, repr_index) { + var comp = this.view.stage.compList[component_index]; + comp.removeRepresentation(comp.reprList[repr_index]); + } + + removeRepresentationsByName(repr_name, component_index) { + var comp = this.view.stage.compList[component_index]; + comp.reprList = comp.reprList.filter(repr => repr.name !== repr_name); + } + + updateRepresentationForComponent(repr_index, component_index, params) { + var comp = this.view.stage.compList[component_index]; + var repr = comp.reprList[repr_index]; + repr.setParameters(params); + } + + updateRepresentationsByName(repr_name, component_index, params) { + var comp = this.view.stage.compList[component_index]; + comp.reprList.forEach(repr => { + if (repr.name === repr_name) { + repr.setParameters(params); + } + }); + } } \ No newline at end of file From 4bdabeb75eb86d09d7f5771e42e3f0519dbd7adc Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 12:02:44 -0500 Subject: [PATCH 06/22] this --- js/src/widget_ngl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 37a33b62..af6bd448 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -482,7 +482,7 @@ export class NGLView extends widgets.DOMWidgetView { i, p = 0, encoded1, encoded2, encoded3, encoded4; - if (base64[base.length - 1] === "=") { + if (base64[base64.length - 1] === "=") { bufferLength--; if (base64[base64.length - 2] === "=") { bufferLength--; From 4302bdc7e9e2918191eff2e2918c39d2a3e6e5b4 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 12:21:49 -0500 Subject: [PATCH 07/22] import --- js/src/stage_manager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/stage_manager.ts b/js/src/stage_manager.ts index ed7da3f5..7cf7fa45 100644 --- a/js/src/stage_manager.ts +++ b/js/src/stage_manager.ts @@ -1,5 +1,6 @@ import * as NGL from "ngl"; import { NGLView } from "./widget_ngl"; +import * as $ from "jquery"; export class StageManager { stage: NGL.Stage; From e214b096f8a9583e620670ee05bcd5a8149b1a6a Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 12:22:25 -0500 Subject: [PATCH 08/22] this --- js/src/message_handler.ts | 130 +++++++++++++++++++++++++++++++++++ notebooks/custom_color.ipynb | 21 ++++-- 2 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 js/src/message_handler.ts diff --git a/js/src/message_handler.ts b/js/src/message_handler.ts new file mode 100644 index 00000000..d4884204 --- /dev/null +++ b/js/src/message_handler.ts @@ -0,0 +1,130 @@ + +import { NGLView } from "./widget_ngl"; + +export class MessageHandler { + view: NGLView; + + constructor(view: NGLView) { + this.view = view; + } + + async on_msg(msg) { + if (msg.type === 'call_method') { + await this.handleCallMethod(msg); + } else if (msg.type === 'base64_single') { + this.handleBase64Single(msg.data); + } else if (msg.type === 'binary_single') { + this.handleBinarySingle(msg); + } else if (msg.type === 'movie_image_data') { + this.handleMovieMaking(msg.render_params); + } else if (msg.type === 'get') { + this.handleGetRequest(msg.data); + } + } + + async handleCallMethod(msg) { + var index, component, func, stage; + var new_args = msg.args.slice(); + new_args.push(msg.kwargs); + + if (msg.methodName === 'addRepresentation' && msg.reconstruc_color_scheme) { + msg.kwargs.color = this.view.addColorScheme(msg.kwargs.color, msg.kwargs.color_label); + } + + switch (msg.target) { + case 'Stage': + await this.handleStageMethod(msg, new_args); + break; + case 'Viewer': + this.view.stage.viewer[msg.methodName].apply(this.view.stage.viewer, new_args); + break; + case 'viewerControls': + this.view.stage.viewerControls[msg.methodName].apply(this.view.stage.viewerControls, new_args); + break; + case 'compList': + component = this.view.stage.compList[msg.component_index]; + component[msg.methodName].apply(component, new_args); + break; + case 'Widget': + this.view[msg.methodName].apply(this.view, new_args); + break; + case 'Representation': + component = this.view.stage.compList[msg.component_index]; + var repr = component.reprList[msg.repr_index]; + repr[msg.methodName].apply(repr, new_args); + break; + default: + console.log('Unknown target: ' + msg.target); + break; + } + } + + async handleStageMethod(msg, new_args) { + var stage_func = this.view.stage[msg.methodName]; + if (msg.methodName === 'removeComponent') { + var component = this.view.stage.compList[msg.args[0]]; + this.view.stage.removeComponent(component); + } else if (msg.methodName === 'loadFile') { + if (this.view.model.views.length > 1 && msg.kwargs && msg.kwargs.defaultRepresentation) { + msg.kwargs.defaultRepresentation = false; + } + await this.view._handleStageLoadFile(msg); + } else { + stage_func.apply(this.view.stage, new_args); + } + } + + handleBase64Single(coordinatesDict) { + var keys = Object.keys(coordinatesDict); + for (var i = 0; i < keys.length; i++) { + var traj_index = parseInt(keys[i], 10); + var coordinates = this.view.decode_base64(coordinatesDict[traj_index]); + if (coordinates && coordinates.byteLength > 0) { + this.view.updateCoordinates(coordinates, traj_index); + } + } + } + + handleBinarySingle(msg) { + var coordinateMeta = msg.data; + var keys = Object.keys(coordinateMeta); + for (var i = 0; i < keys.length; i++) { + var traj_index = parseInt(keys[i], 10); + var coordinates = new Float32Array(msg.buffers[i].buffer); + if (coordinates.byteLength > 0) { + this.view.updateCoordinates(coordinates, traj_index); + } + } + if (msg.movie_making) { + this.handleMovieMaking(msg.render_params); + } + } + + handleGetRequest(data) { + if (data === 'camera') { + this.view.send(JSON.stringify(this.view.stage.viewer.camera)); + } else if (data === 'parameters') { + this.view.send(JSON.stringify(this.view.stage.parameters)); + } else { + console.log("Number of components", this.view.stage.compList.length); + console.log("ngl_view_id", this.view.ngl_view_id); + } + } + + async handleMovieMaking(render_params) { + if (this.view.ngl_view_id == this.view.get_last_child_id()) { + var blob = await this.view.stage.makeImage(render_params); + var reader = new FileReader(); + var arr_str; + reader.onload = function () { + arr_str = (reader.result as string).replace("data:image/png;base64,", ""); + this.view.send({ + "data": arr_str, + "type": "movie_image_data", + }); + this.view.send({ 'type': 'async_message', 'data': 'ok' }); + }.bind(this); + reader.readAsDataURL(blob); + } + } +} \ No newline at end of file diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index 0f588f7d..8c3d56bc 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -9,9 +9,22 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "27fdf6904f564c8bb7eb3018e5894a79", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import nglview as nv\n", "from nglview.color import ColormakerRegistry\n", @@ -23,13 +36,13 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "dc83a8ad7bdd4324a39b84e5953d1a1f", + "model_id": "995c01dbc00c4c95ac73ab27f37072b3", "version_major": 2, "version_minor": 0 }, From 3608d72f8016e7c7312dfdc9c3b293fcbb4c42c0 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 12:25:39 -0500 Subject: [PATCH 09/22] this --- js/src/widget_ngl.ts | 12 +----------- notebooks/custom_color.ipynb | 4 ++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index af6bd448..08d49221 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -268,17 +268,7 @@ export class NGLView extends widgets.DOMWidgetView { } async createIPlayer() { - this.player_pview = this.createView("_iplayer"); - var view = await this.player_pview - var that = this; - var pe = view.el - pe.style.position = 'absolute' - pe.style.zIndex = 100 - pe.style.bottom = '5%' - pe.style.left = '10%' - pe.style.opacity = '0.7' - that.stage.viewer.container.append(view.el); - pe.style.display = 'none' + this.uiManager.createIPlayer(); } async createImageBtn() { diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index 8c3d56bc..e0d062fc 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -15,7 +15,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "27fdf6904f564c8bb7eb3018e5894a79", + "model_id": "7a1704d30e6a4b1c8448f2e849a0a395", "version_major": 2, "version_minor": 0 }, @@ -42,7 +42,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "995c01dbc00c4c95ac73ab27f37072b3", + "model_id": "89fd84daabec4183951abfbb6fbd710d", "version_major": 2, "version_minor": 0 }, From 7a86461fbb3c27fb4f6decf5b747186a29974433 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Sun, 8 Dec 2024 12:27:54 -0500 Subject: [PATCH 10/22] more --- js/src/widget_ngl.ts | 51 ++++---------------------------------------- 1 file changed, 4 insertions(+), 47 deletions(-) diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 08d49221..9341f7a3 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -33,7 +33,7 @@ function generateUUID() { let r for (let i = 0; i < 36; i++) { - if (i === 8 || i === 13 || i === 18 || i === 23) { + if (i === 8 || i === 13 || i === 18) { uuid[i] = '-' } else if (i === 14) { uuid[i] = '4' @@ -272,60 +272,17 @@ export class NGLView extends widgets.DOMWidgetView { } async createImageBtn() { - this.image_btn_pview = this.createView("_ibtn_image"); - var view = await this.image_btn_pview - var pe = view.el - pe.style.position = 'absolute' - pe.style.zIndex = 100 - pe.style.top = '5%' - pe.style.right = '10%' - pe.style.opacity = '0.7' - pe.style.width = '35px' - this.stage.viewer.container.append(view.el); + this.uiManager.createImageBtn(); } async createFullscreenBtn() { - this.btn_pview_fullscreen = this.createView("_ibtn_fullscreen"); - var view = await this.btn_pview_fullscreen - var stage = this.stage; - - var pe = view.el - pe.style.position = 'absolute' - pe.style.zIndex = 100 - pe.style.top = '5%' - pe.style.right = '5%' - pe.style.opacity = '0.7' - pe.style.width = '35px' - pe.style.background = 'white' - pe.style.opacity = '0.3' - pe.style.display = 'none' - pe.onclick = function () { - this.stage.toggleFullscreen(); - }.bind(this) - stage.viewer.container.append(view.el); - stage.signals.fullscreenChanged.add((isFullscreen) => { - if (isFullscreen) { - view.model.set("icon", "compress"); - } else { - view.model.set("icon", "expand"); - } - }); + this.uiManager.createFullscreenBtn(); } - async createGUI() { - this.pgui_view = this.createView("_igui"); - var view = await this.pgui_view - var pe = view.el - pe.style.position = 'absolute' - pe.style.zIndex = 100 - pe.style.top = '5%' - pe.style.right = '10%' - pe.style.width = '300px' - this.stage.viewer.container.append(view.el); + this.uiManager.createGUI(); } - createNglGUI() { this.stage_widget = new StageWidget(this) } From 1c8032f676aae4691beb69273fb1153665170f61 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 19:28:35 -0500 Subject: [PATCH 11/22] fix --- js/src/event_handler.ts | 44 ++++++++++++++++++++++++++++++++++++ js/src/widget_ngl.ts | 2 +- notebooks/custom_color.ipynb | 21 ++++------------- notebooks/molviewspec.ipynb | 17 +++----------- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/js/src/event_handler.ts b/js/src/event_handler.ts index 9bf764f0..87da1dd7 100644 --- a/js/src/event_handler.ts +++ b/js/src/event_handler.ts @@ -162,4 +162,48 @@ export class EventHandler { } }); } + + async mouseOverDisplay(type) { + var that = this.view; + if (this.view.btn_pview_fullscreen) { + var btn = await this.view.btn_pview_fullscreen + btn.el.style.display = type + if (that.stage_widget) { + // If NGL's GUI exists, use its fullscreen button. + btn.el.style.display = 'none' + } + } + + if (this.view.player_pview) { + var v = await this.view.player_pview + v.el.style.display = type + // Need to check if max_frame is available (otherwise NaN) + // https://github.com/jupyter-widgets/ipywidgets/issues/2485 + if (!that.model.get("max_frame") || (that.model.get("max_frame") == 0)) { + // always hide if there's no trajectory. + v.el.style.display = 'none' + } + } + } + + parametersChanged() { + var _parameters = this.view.model.get("_parameters"); + this.view.setParameters(_parameters); + } + + GUIStyleChanged() { + var style = this.view.model.get("gui_style"); + if (style === 'ngl') { + this.view.createNglGUI(); + } else { + if (this.view.stage_widget) { + this.view.stage_widget.dispose() + this.view.stage_widget = undefined + this.view.$container.resizable("enable") + var width = this.view.$el.parent().width() + "px"; + var height = this.view.$el.parent().height() + "px"; + this.view.setSize(width, height); + } + } + } } \ No newline at end of file diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 9341f7a3..d0febd07 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -117,7 +117,7 @@ export class NGLView extends widgets.DOMWidgetView { var state_params = this.stage.getParameters(); this.model.set('_ngl_original_stage_parameters', state_params); this.touch(); - if (!this.isEmbeded() && this.stage.compList.length < this.model.get("n_components")) { + if (!this.embedHandler.isEmbeded() && this.stage.compList.length < this.model.get("n_components")) { // only call this in notebook to avoid calling handleEmbed twice in embeded mode. this.handleEmbed() } diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index e0d062fc..f4885cae 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -9,22 +9,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7a1704d30e6a4b1c8448f2e849a0a395", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import nglview as nv\n", "from nglview.color import ColormakerRegistry\n", @@ -36,13 +23,13 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "89fd84daabec4183951abfbb6fbd710d", + "model_id": "df19d313ef054a71a2df80e07fcd399a", "version_major": 2, "version_minor": 0 }, diff --git a/notebooks/molviewspec.ipynb b/notebooks/molviewspec.ipynb index f19ead5d..392931ad 100644 --- a/notebooks/molviewspec.ipynb +++ b/notebooks/molviewspec.ipynb @@ -2,26 +2,14 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "fedefdce-ce21-4c3c-90bd-27909fe17413", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e74f2c3635cc4b89987bd72f1c25880b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f2dd1db95dca410fa8c99c6839f22ae0", + "model_id": "4c5e21b11a2a443184148aa64268245b", "version_major": 2, "version_minor": 0 }, @@ -44,6 +32,7 @@ " .assembly_structure(assembly_id='1')\n", " .component()\n", " .representation()\n", + " .color(color=\"blue\")\n", ")\n", "state = builder.get_state()\n", "\n", From cefa690b54cfb24c056644a38d9abb63be26a638 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 19:41:31 -0500 Subject: [PATCH 12/22] fix --- js/src/stage_manager.ts | 7 +++---- js/src/widget_ngl.ts | 1 + notebooks/custom_color.ipynb | 21 +++++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/js/src/stage_manager.ts b/js/src/stage_manager.ts index 7cf7fa45..cdf0e6b7 100644 --- a/js/src/stage_manager.ts +++ b/js/src/stage_manager.ts @@ -25,7 +25,6 @@ export class StageManager { this.view.handleResizable(); this.view.ngl_view_id = this.view.uuid; this.view.touch(); - var that = this.view; var width = this.view.model.get("_view_width") || this.view.$el.parent().width() + "px"; var height = this.view.model.get("_view_height") || "300px"; this.setSize(width, height); @@ -36,15 +35,15 @@ export class StageManager { this.view.$container.resizable( "option", "maxWidth", this.view.$el.parent().width() ); - if (this.view.isEmbeded()) { + if (this.view.embedHandler.isEmbeded()) { console.log("Embed mode for NGLView"); - that.handleEmbed(); + this.view.handleEmbed(); } else { this.view.requestUpdateStageParameters(); if (this.view.model.views.length == 1) { this.view.serialize_camera_orientation(); } else { - this.view.set_camera_orientation(that.model.get("_camera_orientation")); + this.view.set_camera_orientation(this.view.model.get("_camera_orientation")); } } } diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index d0febd07..dfe79915 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -69,6 +69,7 @@ export class NGLView extends widgets.DOMWidgetView { embedHandler: EmbedHandler; representationHandler: RepresentationHandler; messageHandler: MessageHandler; + model: NGLModel; constructor(options: any) { super(options); diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index f4885cae..cd1e538e 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -9,9 +9,22 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2f35ac973752467299eee18f28847171", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import nglview as nv\n", "from nglview.color import ColormakerRegistry\n", @@ -23,13 +36,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "df19d313ef054a71a2df80e07fcd399a", + "model_id": "e42fc7641b0d499aaf2e0659eb861bb7", "version_major": 2, "version_minor": 0 }, From 1152dec721874441c3d6965f3d4c6561e3ea6b1a Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 20:41:46 -0500 Subject: [PATCH 13/22] remove that --- js/src/embed_handler.ts | 46 +++++++++++++++++------------------- js/src/ui_manager.ts | 3 +-- notebooks/custom_color.ipynb | 4 ++-- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/js/src/embed_handler.ts b/js/src/embed_handler.ts index 29171374..a0c69bc5 100644 --- a/js/src/embed_handler.ts +++ b/js/src/embed_handler.ts @@ -14,10 +14,9 @@ export class EmbedHandler { } async handleEmbed() { - var that = this.view; - var ngl_msg_archive = that.model.get("_ngl_msg_archive"); - var ngl_stage_params = that.model.get('_ngl_full_stage_parameters'); - const camera_orientation = that.model.get("_camera_orientation"); + var ngl_msg_archive = this.view.model.get("_ngl_msg_archive"); + var ngl_stage_params = this.view.model.get('_ngl_full_stage_parameters'); + const camera_orientation = this.view.model.get("_camera_orientation"); if ( Object.keys(ngl_stage_params).length === 0 @@ -25,56 +24,55 @@ export class EmbedHandler { ) { console.log("No state stored; initializing embedded widget for the first time."); for (const msg of ngl_msg_archive) { - await that.on_msg(msg); + await this.view.on_msg(msg); } return; } var loadfile_list = []; - _.each(ngl_msg_archive, function (msg: any) { + _.each(ngl_msg_archive, (msg: any) => { if (msg.methodName == 'loadFile') { if (msg.kwargs && msg.kwargs.defaultRepresentation) { msg.kwargs.defaultRepresentation = false; } - loadfile_list.push(that._getLoadFilePromise(msg)); + loadfile_list.push(this.view._getLoadFilePromise(msg)); } }); await Promise.all(loadfile_list); - that.stage.setParameters(ngl_stage_params); - that.set_camera_orientation(camera_orientation); - that.touch(); + this.view.stage.setParameters(ngl_stage_params); + this.view.set_camera_orientation(camera_orientation); + this.view.touch(); - if (that.model.comm === undefined) { - var ngl_coordinate_resource = that.model.get("_ngl_coordinate_resource"); + if (this.view.model.comm === undefined) { + var ngl_coordinate_resource = this.view.model.get("_ngl_coordinate_resource"); var n_frames = ngl_coordinate_resource['n_frames'] || 1; - that.model.set("max_frame", n_frames - 1); - that.touch(); - var model = await that.getPlayerModel(); + this.view.model.set("max_frame", n_frames - 1); + this.view.touch(); + var model = await this.view.getPlayerModel(); var pmodel = model.get("children")[0]; - that.listenTo(pmodel, "change:value", function () { - that.updateCoordinatesFromDict(ngl_coordinate_resource, pmodel.get("value")); + this.view.listenTo(pmodel, "change:value", () => { + this.view.updateCoordinatesFromDict(ngl_coordinate_resource, pmodel.get("value")); }); } - for (const msg of that.model.get("_ngl_msg_archive")) { + for (const msg of this.view.model.get("_ngl_msg_archive")) { if (msg.fire_embed) { - await that.on_msg(msg); + await this.view.on_msg(msg); } } - that._set_representation_from_repr_dict(that.model.get("_ngl_repr_dict")); - that.handleResize(); + this.view._set_representation_from_repr_dict(this.view.model.get("_ngl_repr_dict")); + this.view.handleResize(); } _handleEmbedBeforeStage() { - var that = this.view; - var ngl_color_dict = that.model.get("_ngl_color_dict"); + var ngl_color_dict = this.view.model.get("_ngl_color_dict"); var label; for (label in ngl_color_dict) { if (!NGL.ColormakerRegistry.hasScheme(label)) { - that.addColorScheme(ngl_color_dict[label], label); + this.view.addColorScheme(ngl_color_dict[label], label); } } } diff --git a/js/src/ui_manager.ts b/js/src/ui_manager.ts index a7cd7ba5..c80e9e1d 100644 --- a/js/src/ui_manager.ts +++ b/js/src/ui_manager.ts @@ -39,14 +39,13 @@ export class UIManager { async createIPlayer() { this.view.player_pview = this.view.createView("_iplayer"); var view = await this.view.player_pview; - var that = this.view; var pe = view.el; pe.style.position = 'absolute'; pe.style.zIndex = 100; pe.style.bottom = '5%'; pe.style.left = '10%'; pe.style.opacity = '0.7'; - that.stage.viewer.container.append(view.el); + this.view.stage.viewer.container.append(view.el); pe.style.display = 'none'; } diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index cd1e538e..74d10251 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -15,7 +15,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "2f35ac973752467299eee18f28847171", + "model_id": "70326173cf5d4ff5a85cdc8cf76d1bcc", "version_major": 2, "version_minor": 0 }, @@ -42,7 +42,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e42fc7641b0d499aaf2e0659eb861bb7", + "model_id": "2203a46f80094f71988df970f3053835", "version_major": 2, "version_minor": 0 }, From 6bda704235a1163df1ac87f230c36ca2963dd5c8 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 20:43:26 -0500 Subject: [PATCH 14/22] more --- js/src/event_handler.ts | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/js/src/event_handler.ts b/js/src/event_handler.ts index 87da1dd7..8b9f94b2 100644 --- a/js/src/event_handler.ts +++ b/js/src/event_handler.ts @@ -9,17 +9,16 @@ export class EventHandler { handleSignals = () => { var container = this.view.stage.viewer.container; - var that = this.view; container.addEventListener('mouseover', (e) => { - that._ngl_focused = 1; + this.view._ngl_focused = 1; e; // linter - that.mouseOverDisplay('block'); + this.view.mouseOverDisplay('block'); }, false); container.addEventListener('mouseout', (e) => { - that._ngl_focused = 0; + this.view._ngl_focused = 0; e; // linter - that.mouseOverDisplay('none'); + this.view.mouseOverDisplay('none'); }, false); container.addEventListener('contextmenu', (e) => { @@ -34,19 +33,18 @@ export class EventHandler { this.view.touch(); var comp = this.view.stage.compList[len - 1]; comp.signals.representationRemoved.add(() => { - that.request_repr_dict(); + this.view.request_repr_dict(); }); comp.signals.representationAdded.add((repr) => { - that.request_repr_dict(); + this.view.request_repr_dict(); repr.signals.parametersChanged.add(() => { console.log("repr.parametersChanged"); - that.request_repr_dict(); + this.view.request_repr_dict(); }); }); }, this.view); this.view.stage.signals.componentRemoved.add(async (component) => { - var that = this.view; var cindex = this.view.comp_uuids.indexOf(component.uuid); this.view.comp_uuids.splice(cindex, 1); var n_components = this.view.stage.compList.length; @@ -64,14 +62,14 @@ export class EventHandler { var update_backend = false; for (var k in views) { var view = views[k]; - if ((view.uuid != that.uuid) && (view.stage.compList.length > n_components)) { + if ((view.uuid != this.view.uuid) && (view.stage.compList.length > n_components)) { view.stage.removeComponent(view.stage.compList[cindex]); update_backend = true; } } if (update_backend) { console.log("should update backend"); - that.send({ "type": "removeComponent", "data": cindex }); + this.view.send({ "type": "removeComponent", "data": cindex }); } }, this.view); @@ -85,12 +83,12 @@ export class EventHandler { }, 100); var m = this.view.stage.viewerControls.getOrientation(); - if (that._synced_model_ids.length > 0 && that._ngl_focused == 1) { - that._synced_model_ids.forEach(async (mid) => { - var model = await that.model.widget_manager.get_model(mid); + if (this.view._synced_model_ids.length > 0 && this.view._ngl_focused == 1) { + this.view._synced_model_ids.forEach(async (mid) => { + var model = await this.view.model.widget_manager.get_model(mid); for (var k in model.views) { var view = await model.views[k]; - if (view.uuid != that.uuid) { + if (view.uuid != this.view.uuid) { view.stage.viewerControls.orient(m); } } @@ -164,11 +162,10 @@ export class EventHandler { } async mouseOverDisplay(type) { - var that = this.view; if (this.view.btn_pview_fullscreen) { var btn = await this.view.btn_pview_fullscreen btn.el.style.display = type - if (that.stage_widget) { + if (this.view.stage_widget) { // If NGL's GUI exists, use its fullscreen button. btn.el.style.display = 'none' } @@ -179,7 +176,7 @@ export class EventHandler { v.el.style.display = type // Need to check if max_frame is available (otherwise NaN) // https://github.com/jupyter-widgets/ipywidgets/issues/2485 - if (!that.model.get("max_frame") || (that.model.get("max_frame") == 0)) { + if (!this.view.model.get("max_frame") || (this.view.model.get("max_frame") == 0)) { // always hide if there's no trajectory. v.el.style.display = 'none' } From 830b7d0259a177f72eb8d92f97e0a6e7f5117553 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 20:46:57 -0500 Subject: [PATCH 15/22] more that --- js/src/representation_handler.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/js/src/representation_handler.ts b/js/src/representation_handler.ts index 76b9cd1d..766e645c 100644 --- a/js/src/representation_handler.ts +++ b/js/src/representation_handler.ts @@ -27,13 +27,12 @@ export class RepresentationHandler { 'type': 'request_repr_dict', 'data': repr_dict, }); - var that = this.view; - if (that._synced_repr_model_ids.length > 0) { - that._synced_repr_model_ids.forEach(async function (mid) { - var model = await that.model.widget_manager.get_model(mid); + if (this.view._synced_repr_model_ids.length > 0) { + this.view._synced_repr_model_ids.forEach(async (mid) => { + var model = await this.view.model.widget_manager.get_model(mid); for (var k in model.views) { var view = await model.views[k]; - if (view.uuid != that.uuid) { + if (view.uuid != this.view.uuid) { view._set_representation_from_repr_dict(repr_dict); } } @@ -67,11 +66,10 @@ export class RepresentationHandler { } async syncReprWithMe() { - var that = this.view; var repr_dict = this.getReprDictFrontEnd(); for (var k in this.view.model.views) { var v = await this.view.model.views[k]; - if (v.uuid != that.uuid) { + if (v.uuid != this.view.uuid) { v._set_representation_from_repr_dict(repr_dict); } } From 07c303b9ee059238e9117cf1c931f983c566a363 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 20:47:57 -0500 Subject: [PATCH 16/22] that --- js/src/widget_ngl.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index dfe79915..62835ecb 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -132,25 +132,23 @@ export class NGLView extends widgets.DOMWidgetView { } async mouseOverDisplay(type) { - var that = this; if (this.btn_pview_fullscreen) { - var btn = await this.btn_pview_fullscreen - btn.el.style.display = type - if (that.stage_widget) { + var btn = await this.btn_pview_fullscreen; + btn.el.style.display = type; + if (this.stage_widget) { // If NGL's GUI exists, use its fullscreen button. - btn.el.style.display = 'none' + btn.el.style.display = 'none'; } } - var that = this; if (this.player_pview) { - var v = await this.player_pview - v.el.style.display = type + var v = await this.player_pview; + v.el.style.display = type; // Need to check if max_frame is available (otherwise NaN) // https://github.com/jupyter-widgets/ipywidgets/issues/2485 - if (!that.model.get("max_frame") || (that.model.get("max_frame") == 0)) { + if (!this.model.get("max_frame") || (this.model.get("max_frame") == 0)) { // always hide if there's no trajectory. - v.el.style.display = 'none' + v.el.style.display = 'none'; } } } From fcef217e2ee10357f40c52247789c16d0435fabd Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 21:13:08 -0500 Subject: [PATCH 17/22] annotate --- js/src/ui_manager.ts | 3 ++- js/src/widget_ngl.ts | 19 +++++++++++++++++-- notebooks/custom_color.ipynb | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/js/src/ui_manager.ts b/js/src/ui_manager.ts index c80e9e1d..07ba1a31 100644 --- a/js/src/ui_manager.ts +++ b/js/src/ui_manager.ts @@ -1,5 +1,6 @@ import { StageManager } from "./stage_manager"; import { NGLView } from "./widget_ngl"; +import * as NGL from 'ngl'; export class UIManager { view: NGLView; @@ -11,7 +12,7 @@ export class UIManager { async createFullscreenBtn() { this.view.btn_pview_fullscreen = this.view.createView("_ibtn_fullscreen"); var view = await this.view.btn_pview_fullscreen; - var stage = this.view.stage; + var stage: NGL.Stage = this.view.stage; var pe = view.el; pe.style.position = 'absolute'; diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 62835ecb..a563d2c5 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -1,4 +1,4 @@ -var widgets = require("@jupyter-widgets/base") +import * as widgets from "@jupyter-widgets/base" import * as NGL from "ngl" import * as $ from 'jquery' import * as _ from 'underscore' @@ -63,6 +63,7 @@ export class NGLModel extends widgets.DOMWidgetModel { } export class NGLView extends widgets.DOMWidgetView { + stage: NGL.Stage; stageManager: StageManager; eventHandler: EventHandler; uiManager: UIManager; @@ -70,6 +71,20 @@ export class NGLView extends widgets.DOMWidgetView { representationHandler: RepresentationHandler; messageHandler: MessageHandler; model: NGLModel; + player_pview: Promise; + btn_pview_fullscreen: Promise; + image_btn_pview: Promise; + pgui_view: Promise; + $container: any; + ngl_view_id: string; + uuid: string; + stage_widget: typeof StageWidget; + _ngl_focused: number; + comp_uuids: string[]; + _synced_model_ids: string[]; + _synced_repr_model_ids: string[]; + atomColor: any; + constructor(options: any) { super(options); @@ -120,7 +135,7 @@ export class NGLView extends widgets.DOMWidgetView { this.touch(); if (!this.embedHandler.isEmbeded() && this.stage.compList.length < this.model.get("n_components")) { // only call this in notebook to avoid calling handleEmbed twice in embeded mode. - this.handleEmbed() + this.embedHandler.handleEmbed() } var ngl_view_ids = this.model.get("_ngl_view_id") ngl_view_ids.push(this.ngl_view_id) diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index 74d10251..8591dd69 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -15,7 +15,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "70326173cf5d4ff5a85cdc8cf76d1bcc", + "model_id": "336252d1c1f140b88fee28aa82c74d3d", "version_major": 2, "version_minor": 0 }, @@ -42,7 +42,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "2203a46f80094f71988df970f3053835", + "model_id": "64950eebf7bc4d838346392316a52e04", "version_major": 2, "version_minor": 0 }, From 7eb0880d01c252145c1e622893c9278895dddcb1 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 21:26:45 -0500 Subject: [PATCH 18/22] fix --- js/src/gui.js | 11 ++++++----- js/src/stage_manager.ts | 9 ++++++--- js/src/ui_manager.ts | 3 ++- js/src/widget_ngl.ts | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/js/src/gui.js b/js/src/gui.js index 2d0fbb1e..7ae8a4aa 100644 --- a/js/src/gui.js +++ b/js/src/gui.js @@ -2,10 +2,11 @@ * @file Gui * @author Alexander Rose */ -var NGL = require('ngl'); -var UI = require('./ui/ui.js').UI; -var signals = require("./lib/signals.min.js"); +// NOTE: This file is a modified version of the original file +import * as NGL from 'ngl'; +import { UI } from './ui/ui.js'; +import signals from './lib/signals.min.js'; HTMLElement.prototype.getBoundingClientRect = (function () { // workaround for ie11 behavior with disconnected dom nodes @@ -139,7 +140,7 @@ NGL.Preferences.prototype = { // Stage -class StageWidget{ +export class StageWidget{ constructor(view){ // view: NGLView of NGLModel var el @@ -268,7 +269,7 @@ class StageWidget{ var w = ew - sw + 'px' - stage.viewer.container.style.width = w + stage.viewer.container.style.width = w stage.handleResize() } diff --git a/js/src/stage_manager.ts b/js/src/stage_manager.ts index cdf0e6b7..a16fa662 100644 --- a/js/src/stage_manager.ts +++ b/js/src/stage_manager.ts @@ -1,6 +1,7 @@ import * as NGL from "ngl"; import { NGLView } from "./widget_ngl"; import * as $ from "jquery"; +import { WidgetView } from "@jupyter-widgets/base"; export class StageManager { stage: NGL.Stage; @@ -10,7 +11,7 @@ export class StageManager { this.view = view; } - createStage() { + async createStage() { var stage_params = { ...this.view.model.get("_ngl_full_stage_parameters") }; @@ -37,10 +38,12 @@ export class StageManager { ); if (this.view.embedHandler.isEmbeded()) { console.log("Embed mode for NGLView"); - this.view.handleEmbed(); + this.view.embedHandler.handleEmbed(); } else { this.view.requestUpdateStageParameters(); - if (this.view.model.views.length == 1) { + const viewsDict = await this.view.model.views; + const views = await Promise.all(Object.values(viewsDict)); + if (views.length === 1) { this.view.serialize_camera_orientation(); } else { this.view.set_camera_orientation(this.view.model.get("_camera_orientation")); diff --git a/js/src/ui_manager.ts b/js/src/ui_manager.ts index 07ba1a31..89895c5f 100644 --- a/js/src/ui_manager.ts +++ b/js/src/ui_manager.ts @@ -1,3 +1,4 @@ +import { StageWidget } from "./gui"; import { StageManager } from "./stage_manager"; import { NGLView } from "./widget_ngl"; import * as NGL from 'ngl'; @@ -76,6 +77,6 @@ export class UIManager { } createNglGUI() { - this.view.stage_widget = new StageManager(this.view); + this.view.stage_widget = new StageWidget(this.view); } } \ No newline at end of file diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index a563d2c5..f47824cc 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -78,7 +78,7 @@ export class NGLView extends widgets.DOMWidgetView { $container: any; ngl_view_id: string; uuid: string; - stage_widget: typeof StageWidget; + stage_widget: StageWidget; _ngl_focused: number; comp_uuids: string[]; _synced_model_ids: string[]; From b6bb2071a32044ff16569baac50900c058cd7769 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 21:34:08 -0500 Subject: [PATCH 19/22] fix --- js/src/event_handler.ts | 6 +++--- js/src/message_handler.ts | 3 ++- js/src/representation_handler.ts | 4 ++-- js/src/widget_ngl.ts | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/js/src/event_handler.ts b/js/src/event_handler.ts index 8b9f94b2..80e0cba9 100644 --- a/js/src/event_handler.ts +++ b/js/src/event_handler.ts @@ -1,4 +1,4 @@ -import { NGLView } from "./widget_ngl"; +import { NGLModel, NGLView } from "./widget_ngl"; export class EventHandler { view: NGLView; @@ -85,9 +85,9 @@ export class EventHandler { var m = this.view.stage.viewerControls.getOrientation(); if (this.view._synced_model_ids.length > 0 && this.view._ngl_focused == 1) { this.view._synced_model_ids.forEach(async (mid) => { - var model = await this.view.model.widget_manager.get_model(mid); + var model = await this.view.model.widget_manager.get_model(mid) as NGLModel; for (var k in model.views) { - var view = await model.views[k]; + var view = await model.views[k] as NGLView; if (view.uuid != this.view.uuid) { view.stage.viewerControls.orient(m); } diff --git a/js/src/message_handler.ts b/js/src/message_handler.ts index d4884204..75bd140b 100644 --- a/js/src/message_handler.ts +++ b/js/src/message_handler.ts @@ -65,7 +65,8 @@ export class MessageHandler { var component = this.view.stage.compList[msg.args[0]]; this.view.stage.removeComponent(component); } else if (msg.methodName === 'loadFile') { - if (this.view.model.views.length > 1 && msg.kwargs && msg.kwargs.defaultRepresentation) { + var views = this.view.model.views; + if (Object.keys(views).length > 1 && msg.kwargs && msg.kwargs.defaultRepresentation) { msg.kwargs.defaultRepresentation = false; } await this.view._handleStageLoadFile(msg); diff --git a/js/src/representation_handler.ts b/js/src/representation_handler.ts index 766e645c..9db1e9ed 100644 --- a/js/src/representation_handler.ts +++ b/js/src/representation_handler.ts @@ -31,7 +31,7 @@ export class RepresentationHandler { this.view._synced_repr_model_ids.forEach(async (mid) => { var model = await this.view.model.widget_manager.get_model(mid); for (var k in model.views) { - var view = await model.views[k]; + var view = await model.views[k] as NGLView; if (view.uuid != this.view.uuid) { view._set_representation_from_repr_dict(repr_dict); } @@ -68,7 +68,7 @@ export class RepresentationHandler { async syncReprWithMe() { var repr_dict = this.getReprDictFrontEnd(); for (var k in this.view.model.views) { - var v = await this.view.model.views[k]; + var v = await this.view.model.views[k] as NGLView; if (v.uuid != this.view.uuid) { v._set_representation_from_repr_dict(repr_dict); } diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index f47824cc..14af9d7f 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -84,6 +84,7 @@ export class NGLView extends widgets.DOMWidgetView { _synced_model_ids: string[]; _synced_repr_model_ids: string[]; atomColor: any; + $pickingInfo: any; constructor(options: any) { From 30e6bbe9d73057eb2f4ef6eb6e61e3925e55efcc Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 21:36:23 -0500 Subject: [PATCH 20/22] fix --- js/src/widget_ngl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/widget_ngl.ts b/js/src/widget_ngl.ts index 14af9d7f..104fa1ad 100644 --- a/js/src/widget_ngl.ts +++ b/js/src/widget_ngl.ts @@ -101,6 +101,7 @@ export class NGLView extends widgets.DOMWidgetView { this.beforeDisplay(); this.displayed.then(() => { this.stageManager.createStage(); + this.stage = this.stageManager.stage; this.eventHandler.handlePicking(); this.eventHandler.handleSignals(); this.eventHandler.handleMessage(); From aec22f3ad724e85210d7e383104758916ae17a55 Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 21:40:05 -0500 Subject: [PATCH 21/22] more --- notebooks/custom_color.ipynb | 4 ++-- notebooks/gui.ipynb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/notebooks/custom_color.ipynb b/notebooks/custom_color.ipynb index 8591dd69..4d244d80 100644 --- a/notebooks/custom_color.ipynb +++ b/notebooks/custom_color.ipynb @@ -15,7 +15,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "336252d1c1f140b88fee28aa82c74d3d", + "model_id": "b747055cafac4c2cadca2f855772bb48", "version_major": 2, "version_minor": 0 }, @@ -42,7 +42,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "64950eebf7bc4d838346392316a52e04", + "model_id": "79cbdfde509d4a1cae5ff1ef8ea9069f", "version_major": 2, "version_minor": 0 }, diff --git a/notebooks/gui.ipynb b/notebooks/gui.ipynb index e16b1109..e87e6976 100644 --- a/notebooks/gui.ipynb +++ b/notebooks/gui.ipynb @@ -17,7 +17,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "42bb11fe3851422d9dce75d8f5275a47", + "model_id": "13ecad24b961425baec4a72bbef73f75", "version_major": 2, "version_minor": 0 }, @@ -29,7 +29,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "bb3b67e8b81741ea8197aa725561be11", + "model_id": "e292b42184b9423192d0ab718cdc870f", "version_major": 2, "version_minor": 0 }, @@ -43,7 +43,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ed471be3289c41999a87041a7aa12fab", + "model_id": "3d3dcd008a8f4a8c8c3221a33a26b2a4", "version_major": 2, "version_minor": 0 }, From d6720e79b940c4fb7b03b3dc0114a59a7fe9ea6e Mon Sep 17 00:00:00 2001 From: Hai Nguyen Date: Tue, 10 Dec 2024 21:44:56 -0500 Subject: [PATCH 22/22] fix --- js/src/event_handler.ts | 1 + js/src/ui_manager.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/event_handler.ts b/js/src/event_handler.ts index 80e0cba9..6183b48f 100644 --- a/js/src/event_handler.ts +++ b/js/src/event_handler.ts @@ -1,4 +1,5 @@ import { NGLModel, NGLView } from "./widget_ngl"; +import * as $ from 'jquery' export class EventHandler { view: NGLView; diff --git a/js/src/ui_manager.ts b/js/src/ui_manager.ts index 89895c5f..d32a2eb4 100644 --- a/js/src/ui_manager.ts +++ b/js/src/ui_manager.ts @@ -1,5 +1,4 @@ import { StageWidget } from "./gui"; -import { StageManager } from "./stage_manager"; import { NGLView } from "./widget_ngl"; import * as NGL from 'ngl';