cyjs as a jupyter widget


In [1]:
%%javascript
require.config({
   paths: {cytoscape: 'http://localhost:8099/js/cytoscape-2.7.10'}
   })



In [2]:
# ensure the library is available
import requests
r = requests.get('http://localhost:8099/js/cytoscape-2.7.10.js')
assert(r.status_code == 200)

In [3]:
import ipywidgets as widgets
import json
from traitlets import Int, Unicode, Tuple, CInt, Dict, validate, observe

class cyjsWidget(widgets.DOMWidget):
    
    _view_name = Unicode('CyjsView').tag(sync=True)
    _view_module = Unicode('cyjs').tag(sync=True)
    frameWidth = Int(400).tag(sync=True)
    frameHeight = Int(300).tag(sync=True)
    msgStringFromPython = Unicode("{}").tag(sync=True)
    msgStringToPython = Unicode("{}").tag(sync=True)
    msg = {};
    status = "initial status message\n"
    selectedNodes = [];
        
    def setSize(self, width, height):
       self.status += "setSize(%d, %d)\n" % (width, height)
       self.frameWidth = width
       self.frameHeight = height
        
    def fit(self, margin=50):
      self.status += "entering fit (%d)\n" % margin
      self. msgStringFromPython = json.dumps({"cmd": "fit", "status": "request",
                                              "callback": "", "payload": margin});
        
    def requestSelectedNodes(self):
      self.selectedNodes = [];
      self.status += "entering requestSelectedNodes\n";
      self. msgStringFromPython = json.dumps({"cmd": "getSelectedNodes", "status": "request",
                                              "callback": "", "payload": ""});
    def getSelectedNodes(self):
      return(self.selectedNodes)

    def selectNodes(self, nodes):
      self. msgStringFromPython = json.dumps({"cmd": "selectNodes", "status": "request",
                                              "callback": "", "payload": nodes});
       
    def clearSelection(self):
      self. msgStringFromPython = json.dumps({"cmd": "clearSelection", "status": "request",
                                              "callback": "", "payload": ""});

        
    @observe('msgStringToPython')
    def msg_arrived(self, change):
        self.status += "---- python - msg arrived\n"
        tmp = change['new']
        self.status += "len of tmp: %d\n" % len(tmp)
        self.status += "type of tmp: %s\n" % type(tmp)
        self.msgStringFromPython = tmp
        self.status += "%s\n" % tmp
        self.dispatch(self.msgStringFromPython)
 
    def dispatch(self, msgRaw):
        msg = json.loads(msgRaw)
        self.status += "entering dispatch\n"
        self.status += "dispatch this msg: %s\n" % msg
        self.status += "msg.cmd: %s\n" % msg["cmd"]
        if msg["cmd"] == 'storeSelectedNodes':
            self.status += "storing selected nodes to self.selectedNodes %s\n" % msg["payload"]
            self.selectedNodes = msg["payload"]
        elif msg["cmd"] == 'clearCircles':
            self.circles = []
        else:
          print("unknown cmd: %s" % msg["cmd"])

In [4]:
%%javascript
"use strict";

require.undef('cyjs');

define('cyjs', ["jupyter-js-widgets", "cytoscape"], function(widgets, cytoscape) {
    
    var CyjsView = widgets.DOMWidgetView.extend({

        initialize: function() {
           this.circles = [];
           this.circleCount = 0;
           this.options = {}; 
           this.msg = "empty in javascript";
           this.msgStringFromPython = "";
           this.defaultHeight = "800px";
           this.defaultWidth = "1000px";
           },

        createDiv: function(){
            var outerDiv = $("<div id='cyOuterDiv' style='border:1px solid gray; height: 800px; width: 1000px'></div>");
            var toolbarDiv = $("<div id='cyToolbarDiv' style='height: 30px; width: 1000px'></div>");
            var cyDiv = $("<div id='cyDiv' style='height: 870px; width: 1000px'></div>");
            outerDiv.append(toolbarDiv);
            outerDiv.append(cyDiv);
            var cyWidget = this;
            var fitButton = $("<button>Fit</button>").click(function(){
                console.log("Fit!");
                console.log("fitButton's notion of this:")
                console.log(cyWidget.cy);
                cyWidget.cy.fit(50);
               });
            toolbarDiv.append(fitButton);
            var fitSelectedButton = $("<button>Fit Selected</button>").click(function(){
                var selectedNodes = cyWidget.cy.filter('node:selected');
                if(selectedNodes.length > 0){
                   cyWidget.cy.fit(selectedNodes, 50);
                   }
               });
            toolbarDiv.append(fitSelectedButton);
            var sfnButton = $("<button>SFN</button>").click(function(){
               cyWidget.cy.nodes(':selected').neighborhood().nodes().select()
               });
            toolbarDiv.append(sfnButton);
            var clearButton = $("<button>Clear</button>").click(function(){
               cyWidget.cy.nodes().unselect();
               cyWidget.cy.edges().unselect();
               });
            toolbarDiv.append(clearButton);
            return(outerDiv);
           },
 
        
        createCanvas: function(){
            var cyjsWidget = this;
            console.log("createCanvas notion of this:")
            console.log(cyjsWidget);
            this.cy = cytoscape({
               container: document.getElementById('cyDiv'),
               elements: {
               nodes: [
                 {data: {id: 'a', name: 'Node A', type: 'big' }},
                 {data: {id: 'b', name: 'Node B', type: 'little'}},
                 ],
               edges: [
                {data: {source: 'a', target: 'b'}},
                       {data: {source: 'b', target: 'a'}}
               ]},
          ready: function(){
            console.log("small cyjs network ready");
            console.log("ready's notion of this:")
            console.log(this);
            cyjsWidget.cy = this;
            window.cy = this;  // for easy debugging
            console.log("ready's notion of cyjsWidget:")
            console.log(cyjsWidget);
            console.log("calling this.fit")
            //cyWidget.cy.fit(100);
            cyjsWidget.loadStyle("style.js");
            console.log("--- about to call loadGraph")
            //cyjsWidget.loadGraph("igap4snpGenesetEnrichment.json");
            console.log("    back from loadGraph")
            } // ready
           })},

        loadStyle: function(filename){
           var cyObj = this.cy;
           console.log("cyjsWidget.loadStyle: " + filename)
           console.log("loadStyle's notion of this:");
           console.log(this);
           console.log("loadStyle's notion of cy:");
           console.log(cyObj);
           var url = window.location.origin + "/files/cyjs/" + filename;
           console.log("about to getScript: " + url);
           $.getScript(url)
              .done(function(script, textStatus) {
                 console.log(textStatus);
                 cyObj.style(vizmap);
                 })
             .fail(function( jqxhr, settings, exception ) {
                console.log("getScript error trying to read " + filename);
                console.log("exception: ");
                console.log(exception);
                });
          },
        
        loadGraph: function(filename){
           console.log("entering loadGraph");
           var cyObj = this.cy;
           var url = window.location.origin + "/files/cyjs/" + filename;
           console.log("=== about to getScript on " + url);
           $.getScript(url)
              .done(function(script, textStatus) {
                 console.log("getScript: " + textStatus);
                 console.log("nodes: " + network.elements.nodes.length);
                 if(typeof(network.elements.edges) != "undefined")
                    console.log("edges: " + network.elements.edges.length);
                cyObj.add(network.elements);  // no positions yet
                cyObj.nodes().map(function(node){node.data({degree: node.degree()})});
                }) // .done
            .fail(function(jqxhr, settings, exception) {
               console.log("addNetwork getscript error trying to read " + filename);
               });
           },
        
        render: function() { 
            console.log("entering render")
            this.$el.append(this.createDiv());
            this.listenTo(this.model, 'change:frameWidth', this.frameDimensionsChanged, this);
            this.listenTo(this.model, 'change:frameHeight', this.frameDimensionsChanged, this);
            this.listenTo(this.model, 'change:msgStringFromPython', this.dispatchRequest, this)
            var cyjsWidget = this;
            function myFunc(){
               cyjsWidget.createCanvas()
               }
            setTimeout(myFunc, 500);
            },

        dispatchRequest: function(){
           console.log("dispatchRequest");
           var msgRaw = this.model.get("msgStringFromPython");
           var msg = JSON.parse(msgRaw);
           console.log(msg);
           console.log("========================");
           console.log(this);
           switch(msg.cmd) {
              case 'fit':
                 var margin = msg.payload;
                 console.log("fit with margin: " + margin)
                 this.cy.fit(margin);
                 break;
              case 'getSelectedNodes':
                 var selectedNodes = this.cy.filter("node:selected").map(function(node){ 
                     return node.data().id});
                  console.log("-- found these selected nodes: ");
                  console.log(selectedNodes);
                  var jsonString = JSON.stringify({cmd: "storeSelectedNodes",
                                                status: "reply",
                                                callback: "",
                                                payload: selectedNodes})
                  console.log(" *** jsonString: ")
                  console.log(jsonString);
                  this.model.set("msgStringToPython", jsonString);
                  console.log("    after setting 'msgStringToPython");
                  this.touch();
                  break;
               case 'selectNodes':
                  var nodeIDs = msg.payload;
                  console.log("--- selecting these nodes: " + nodeIDs);
                  if(typeof(nodeIDs) == "string")
                     nodeIDs = [nodeIDs];
                 var filterStrings = [];
                 for(var i=0; i < nodeIDs.length; i++){
                   var s = '[id="' + nodeIDs[i] + '"]';
                   filterStrings.push(s);
                   } // for i
                var nodesToSelect = this.cy.nodes(filterStrings.join());
                nodesToSelect.select()
                break;
              case 'clearSelection':
                 this.cy.nodes().unselect();
                 break;
            default:
               console.log("unrecognized msg.cmd: " + msg.cmd);
             } // switch
           }, 
        
        frameDimensionsChanged: function(){
           console.log("frameDimensionsChanged")
           var newWidth  = this.model.get("frameWidth");
           var newHeight = this.model.get("frameHeight");
           console.log("frame: " + newWidth + " x " + newHeight);
           $("#cyOuterDiv").width(newWidth);
           $("#cyOuterDiv").height(newHeight);
           $("#cyToolbarDiv").width(newWidth);
           $("#cyDiv").width(newWidth);
           $("#cyDiv").height(newHeight - $("#cyToolbarDiv").height());
           }, 
        
        events: {
           //"click #svg": "changeHandler"
           }

    });
    return {
        CyjsView: CyjsView
    };
});



In [5]:
cy = cyjsWidget()
cy



In [5]:
cy.selectNodes("a")

In [6]:
cy.selectNodes("b")

In [5]:
import sys 
for name, module in sorted(sys.modules.items()): 
    if hasattr(module, '__version__'): 
        print(name, module.__version__)


IPython 4.1.2
IPython.core.release 4.1.2
_ctypes 1.1.0
_curses b'2.2'
argparse 1.1
ctypes 1.1.0
ctypes.macholib 1.0
decorator 4.0.9
distutils 3.5.2
ipykernel 4.5.1
ipykernel._version 4.5.1
ipython_genutils 0.1.0
ipython_genutils._version 0.1.0
ipywidgets 6.0.0.beta5
ipywidgets._version 6.0.0.beta5
json 2.0.9
jupyter_client 4.2.2
jupyter_client._version 4.2.2
jupyter_core 4.3.0.dev
jupyter_core.version 4.3.0.dev
logging 0.5.1.2
optparse 1.5.3
parser 0.5
path 0.0.0
pexpect 4.0.1
pickleshare 0.5
pkg_resources._vendor.packaging.__about__ 16.5
pkg_resources._vendor.six 1.10.0
pkg_resources.extern.packaging 16.5
pkg_resources.extern.pyparsing 2.0.6
pkg_resources.extern.six 1.10.0
platform 1.0.7
ptyprocess 0.5
re 2.2.1
six 1.10.0
traitlets 4.3.1
traitlets._version 4.3.1
zlib 1.0
zmq 15.2.0
zmq.sugar 15.2.0
zmq.sugar.version 15.2.0

In [7]:
cy.clearSelection()

In [8]:
cy.selectNodes(["a", "b"])

In [9]:
cy.requestSelectedNodes()

In [10]:
cy.getSelectedNodes()


Out[10]:
['a', 'b']

In [11]:
cy.msgStringToPython


Out[11]:
'{"cmd":"storeSelectedNodes","status":"reply","callback":"","payload":["a","b"]}'

In [12]:
cy.setSize(300, 300);

In [13]:
cy.fit(200)
# print(cy.status)

In [14]:
cy.setSize(1000, 600);

In [15]:
cy.fit(100)

In [16]:
# print(cy.status)

In [ ]: