cyjs as a jupyter widget


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



In [471]:
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 [476]:
%%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 [477]:
cy = cyjsWidget()
cy

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

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

In [480]:
cy.clearSelection()

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

In [482]:
cy.requestSelectedNodes()

In [483]:
cy.getSelectedNodes()


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

In [484]:
cy.msgStringToPython


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

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

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

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

In [488]:
cy.fit(100)

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

In [ ]: