Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADD Custom color scheme API #839

Merged
merged 10 commits into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nglview-js-widgets",
"version": "2.6.2",
"version": "2.6.5",
"description": "nglview-js-widgets",
"author": "Hai Nguyen <[email protected]>, Alexander Rose <[email protected]>",
"license": "MIT",
Expand Down
34 changes: 34 additions & 0 deletions js/src/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var widgets = require("@jupyter-widgets/base")
var NGL = require("ngl")


var BaseView = widgets.DOMWidgetView.extend({

render: function(){
this.handleMessage();
this.displayed.then(() =>{
this.model.set("_ready", true)
this.touch()
})
},

executeCode: function(code){
eval(code);
},

handleMessage: function(){
this.model.on("msg:custom", function(msg){
this.on_msg(msg)
}.bind(this))
},

on_msg: function(msg){
if (msg.type == 'callMethod'){
this[msg.methodName].apply(this, msg.args, msg.kwargs)
}
}
})

module.exports = {
"BaseView": BaseView
}
60 changes: 60 additions & 0 deletions js/src/color.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
var _ = require('underscore')
var NGL = require("ngl")
var BaseView = require("./base").BaseView
var widgets = require("@jupyter-widgets/base")


var ColormakerRegistryModel = widgets.DOMWidgetModel.extend({
defaults: function(){
return _.extend(widgets.DOMWidgetModel.prototype.defaults(), {
_model_name: 'ColormakerRegistryModel',
_model_module: 'nglview-js-widgets',
_model_module_version: require("../package.json").version,
_view_name: "ColormakerRegistryView",
_view_module: "nglview-js-widgets",
_view_module_version: require("../package.json").version,
});
}
})


var ColormakerRegistryView = BaseView.extend({
addSelectionScheme: function(label, args){
var id = NGL.ColormakerRegistry.addSelectionScheme(args, label)
this._updateId(id, label)
},

addSelectionSchemeOriginal: function(label, args){
var id = NGL.ColormakerRegistry.addSelectionScheme(args, label);
var scheme = NGL.ColormakerRegistry.userSchemes[id];
NGL.ColormakerRegistry.removeScheme(id);
// hard code the scheme ID
NGL.ColormakerRegistry.add(label, scheme);
},

addScheme: function(label, func_str){
var func = Function("return " + func_str)()
console.log(func)
var id = NGL.ColormakerRegistry.addScheme(function(params){
this.atomColor = func
})
this._updateId(id, label)
},

_updateId: function(oldId, newId){
var scheme = NGL.ColormakerRegistry.userSchemes[oldId]
console.log(oldId, scheme)
NGL.ColormakerRegistry.add(newId, scheme)
NGL.ColormakerRegistry.removeScheme(oldId)
},

removeScheme: function(schemeId){
NGL.ColormakerRegistry.removeScheme(schemeId)
},
})


module.exports = {
ColormakerRegistryView: ColormakerRegistryView,
ColormakerRegistryModel: ColormakerRegistryModel
}
2 changes: 1 addition & 1 deletion js/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module.exports = {};

var loadedModules = [
require("./widget_ngl.js"),
//require("./ngl.js"),
require("./color.js"),
]

for (var i in loadedModules) {
Expand Down
44 changes: 37 additions & 7 deletions js/src/widget_ngl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
var Jupyter
var widgets = require("@jupyter-widgets/base")
var NGL = require('ngl')
var ColormakerRegistryModel = require('./color').ColormakerRegistryModel
var BaseView = require('./base').BaseView
var $ = require('jquery')
var _ = require('underscore')
require("./lib/signals.min.js")
Expand Down Expand Up @@ -336,10 +338,13 @@ var NGLView = widgets.DOMWidgetView.extend({
}
},

execute_code: function(code){
executeCode: function(code){
eval(code);
},

handleCustomColor: function(){
},

handle_embed: function(){
var that = this;
var ngl_coordinate_resource = that.model.get("_ngl_coordinate_resource");
Expand All @@ -350,8 +355,33 @@ var NGLView = widgets.DOMWidgetView.extend({
var label

// reconstruct colors
for (label in ngl_color_dict){
that.addColorScheme(ngl_color_dict[label], label);
if (this.model.comm === undefined){
var model_dict = this.model.widget_manager._models
var models = []
for (let k in model_dict){
models.push(model_dict[k])
}
Promise.all(models).then(models => {
for (var i in models){
var model = models[i]
if (model instanceof ColormakerRegistryModel){
var k = Object.keys(model.views)[0] // singleton
model.views[k].then(view =>{
view.model.get("_msg_ar").forEach(msg =>{
view.on_msg(msg)
})
})
}
}
})

// Outside the notebook
// Old API (_ColorScheme)
for (label in ngl_color_dict){
if (!NGL.ColormakerRegistry.hasScheme(label)){
that.addColorScheme(ngl_color_dict[label], label);
}
}
}

_.each(ngl_msg_archive, function(msg){
Expand Down Expand Up @@ -1125,7 +1155,7 @@ var FullscreenModel = widgets.DOMWidgetModel.extend({
}
})

var FullscreenView = widgets.DOMWidgetView.extend({
var FullscreenView = BaseView.extend({
render: function() {
this.stage = new NGL.Stage()
var that = this
Expand Down Expand Up @@ -1153,13 +1183,13 @@ var FullscreenView = widgets.DOMWidgetView.extend({
})
},

execute_code: function(code){
executeCode: function(code){
eval(code);
},

on_msg: function(msg){
if ('execute_code' in msg){
this.execute_code(msg.execute_code)
if ('executeCode' in msg){
this.executeCode(msg.executeCode)
}
}

Expand Down
2 changes: 1 addition & 1 deletion nglview/_frontend.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__frontend_version__ = '2.6.2'
__frontend_version__ = '2.6.5'
24 changes: 24 additions & 0 deletions nglview/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from ipywidgets import DOMWidget
from traitlets import Bool, List


class BaseWidget(DOMWidget):
_msg_q = []
_msg_ar = List().tag(sync=True)
_ready = Bool(False).tag(sync=True)

def _js(self, code):
self._call("executeCode", code)

def _call(self, method_name, *args, **kwargs):
msg = {"type": "callMethod",
"methodName": method_name,
"args": args, "kwargs": kwargs}
if not self._ready:
# fire later
self._msg_q.append(msg)
else:
self.send(msg)
msg_ar = self._msg_ar[:]
msg_ar.append(msg)
self._msg_ar = msg_ar # trigger sync
100 changes: 100 additions & 0 deletions nglview/color.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
from ipywidgets import DOMWidget
from traitlets import Unicode, Bool, observe
from .base import BaseWidget
from ._frontend import __frontend_version__
from IPython.display import display
import time


COLOR_SCHEMES = [
" ", "picking", "random", "uniform", "atomindex", "residueindex",
"chainindex", "modelindex", "sstruc", "element", "resname", "bfactor",
Expand All @@ -7,6 +15,7 @@
_USER_COLOR_DICT = {}



class _ColorScheme:
_color_dict = {}

Expand All @@ -19,3 +28,94 @@ def __init__(self, args, label):
@property
def data(self):
return {'data': self._color_scheme, 'label': self._label}


def _singleton(cls):
# https://www.python.org/dev/peps/pep-0318/#examples
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance


@_singleton
class _ColormakerRegistry(BaseWidget):
_view_name = Unicode("ColormakerRegistryView").tag(sync=True)
_view_module = Unicode("nglview-js-widgets").tag(sync=True)
_view_module_version = Unicode(__frontend_version__).tag(sync=True)
_model_name = Unicode("ColormakerRegistryModel").tag(sync=True)
_model_module = Unicode("nglview-js-widgets").tag(sync=True)
_model_module_version = Unicode(__frontend_version__).tag(sync=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
display(self)

@observe("_ready")
def _on_ready(self, change):
if change.new:
while self._msg_q:
msg = self._msg_q.pop(0)
self.send(msg)

def _ipython_display_(self, **kwargs):
if self._ready:
return str(self)
super()._ipython_display_(**kwargs)

def add_selection_scheme(self, scheme_id, arg):
"""
Examples
--------
>>> ColormakerRegistry.add_selection_scheme("my_custom_scheme",
[['blue', '1-10']])
>>> view.add_cartoon(color="my_custom_scheme")
"""
self._call("addSelectionScheme", scheme_id, arg)

def add_scheme_func(self, scheme_id, func_str):
"""

Examples
--------
>>> func_str = '''
this.atomColor = function (atom) {
if (atom.serial < 1000) {
return 0x0000FF // blue
} else if (atom.serial > 2000) {
return 0xFF0000 // red
} else {
return 0x00FF00 // green
}
}
'''
>>> ColormakerRegistry.add_scheme_func('awesome', func_str)
>>> view.add_cartoon(color='awesome')
"""

code = """
var schemeId = NGL.ColormakerRegistry.addScheme(function (params) {
%s
})

this._updateId(schemeId, '%s')
""" % (func_str, scheme_id)
self._js(code)

def add_scheme(self, scheme_id, obj):
"""
Parameters
----------
obj: List of List or str (of JS function)
"""
if isinstance(obj, list):
self.add_selection_scheme(scheme_id, obj)
elif isinstance(obj, str):
self.add_scheme_func(scheme_id, obj)
else:
raise ValueError(f"{obj} must be either list of list or string")


ColormakerRegistry = _ColormakerRegistry()
Loading