In [1]:
import ipywidgets as widgets
import json
import time
import os
from IPython.display import display, HTML
from traitlets import Int, Unicode, observe

two javascript libraries which may not always be available. detect that if so.


In [2]:
import requests
assert(requests.get('http://cytoscape.github.io/cytoscape.js/api/cytoscape.js-latest/cytoscape.js').status_code == 200)
assert(requests.get('http://igv.org/web/release/1.0.6/igv-1.0.6.js').status_code == 200)

In [ ]:
display(HTML('<link rel="stylesheet" type="text/css" href="//igv.org/web/release/1.0.6/igv-1.0.6.css">'))

In [4]:
class TabsWidget(widgets.DOMWidget):
    
    _view_name = Unicode('TabsView').tag(sync=True)
    _view_module = Unicode('tabsDemo').tag(sync=True)
    frameHeight = Int(300).tag(sync=True)
    _chromLocString = "chr1:0-0";
    _rawMessage = "";
    _incomingMessage = {};
    msgFromKernel = Unicode("{}").tag(sync=True)
    msgFromBrowser = Unicode("{}").tag(sync=True)
    status = "initial status message\n"

       # ensure that any ensuing message is seen as novel in the browser
       # the message (a json-ified string) is a mutable traitlet.  the browser only sees 
       # the message if it changes
    def _sendResetMessageToBrowser(self):  
       self.msgFromKernel = json.dumps({"cmd": "cleanSlate", "status": "nop", "callback": "", "payload": ""});

    def setHeight(self, height):
       print("setHeight(%d) "% height)
       self.frameHeight = height
        
    def getChromLocString(self):
        return self._chromLocString
    
    def ping(self):
        self._sendResetMessageToBrowser()
        self.msgFromKernel = json.dumps({"cmd": "ping", "status": "request", "callback": "", "payload": ""});

    def deleteGraph(self):
      self._sendResetMessageToBrowser()
      self.msgFromKernel = json.dumps({"cmd": "deleteGraph", "status": "request", "callback": "", "payload": ""});

    def addGraph(self, gjson):
      self._sendResetMessageToBrowser();
      self.msgFromKernel = json.dumps({"cmd": "addGraph", "status": "request",
                                       "callback": "",    "payload": gjson});
    def fitSelected(self, margin=50):
      self._sendResetMessageToBrowser();
      self.status += "entering fitSelected (%d)\n" % margin
      self.msgFromKernel = json.dumps({"cmd": "fitSelected", "status": "request", "callback": "", "payload": margin});
        
    def fit(self, margin=50):
      self._sendResetMessageToBrowser();
      self.status += "entering fit (%d)\n" % margin
      self.msgFromKernel = json.dumps({"cmd": "fit", "status": "request", "callback": "", "payload": margin});


    def availableLayouts(self):
      self._sendResetMessageToBrowser();

      return(["grid", "null", "random", "cose", "circle", "concentric", "breadthfirst"]);

    def layout(self, name):
      self._sendResetMessageToBrowser();
      self.msgFromKernel = json.dumps({"cmd": "layout", "status": "request", "callback": "", "payload": name});

    def tfGridLayout(self):
      self._sendResetMessageToBrowser();
      self.msgFromKernel = json.dumps({"cmd": "tfGridLayout", "status": "request", "callback": "", "payload": ""});

    def selectedNodeGridLayout(self, name):
      self._sendResetMessageToBrowser();
      self.msgFromKernel = json.dumps({"cmd": "selectedNodeGridLayout", "status": "request", "callback": "", "payload": ""});


    def loadNetworkStyleFile(self, filename):
      if(not os.path.isfile(filename)):
        print("file '%s' not found" % filename)
        return;
      self._sendResetMessageToBrowser();
      self.msgFromKernel = json.dumps({"cmd": "loadNetworkStyleFile", "status": "request", "callback": "", 
                                       "payload": filename});
        

    @observe('msgFromBrowser')
    def msg_arrived(self, change):
       #self.status += "msgFromBrowser has arrived: %f\n" % time.time()
       self._rawMessage = change['new']
       #self.status += "rawMessage: %s\n" % self._rawMessage
       self._incomingMessage = json.loads(self._rawMessage)
       cmd = self._incomingMessage["cmd"]
       #self.status += "cmd: %s\n"  % cmd
       if(cmd == "updateChromLocString"):
          self._chromLocString = self._incomingMessage["payload"]
       else:
         self.status += "unrecognized cmd: '%s'" % cmd

display(HTML(data="""

"""))


In [5]:
%%javascript
"use strict"
require.config({

    paths: {'jquery'    :   'http://code.jquery.com/jquery-1.12.4.min',
            'jquery-ui' :   'http://code.jquery.com/ui/1.12.1/jquery-ui.min',
            'cytoscape' :   'http://cytoscape.github.io/cytoscape.js/api/cytoscape.js-latest/cytoscape',
            'bootstrap' :   'http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min',
            'igv'       :   'http://igv.org/web/release/1.0.6/igv-1.0.6',
            'three'     :   'https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three',
            'app3d'     :   'http://localhost:9998/files/three/js/app3d'
            },
    shim: {'bootstrap': {'deps'   : ['jquery']},
           'igv':       {'deps'   : ['jquery', 'jquery-ui', 'bootstrap']},
           'three':     {'exports': 'THREE'}
           }
    });

require.undef('tabsDemo')

define('tabsDemo', ["jupyter-js-widgets", "jquery", "jquery-ui", "cytoscape", "igv", 'three', 'app3d'], 
       function(widgets, $, ui, cytoscape, igv, THREE, app3d) {
    
    var TabsView = widgets.DOMWidgetView.extend({

        initialize: function() {
           window.tabsWidget = this;
           this.options = {}
           this.chromLocString = "";
           console.log("constructing TabsView");
           this.frameHeight = "800px";
           window.cyInitialized = false;
           },

        showDivSize: function(divName){
           var width = Math.round($("#" + divName).width())
           var height = Math.round($("#" + divName).height())
           console.log(divName + ": " + width + " x " + height);
           },
        
        resizeHandler: function(){
           //window.tabsWidget.showDivSize("masterTabsDiv");
           $("#cyOuterDiv").height($("#masterTabsDiv").height() * 0.95);
           $("#cyDiv").height($("#cyOuterDiv").height() - 25);
           $("#threeDiv").height($("#masterTabsDiv").height() * 0.90);
           //window.tabsWidget.showDivSize("cyOuterDiv");
           //window.tabsWidget.showDivSize("cyMenubarDiv");
           //window.tabsWidget.showDivSize("cyDiv");
           //window.tabsWidget.showDivSize("igvDiv");
           },
        
        createMasterTabsDiv: function(){
           var masterTabsDiv = $("<div id='masterTabsDiv' style='border:1px solid gray; height: 1200px; width: 97%'></div>");
           var list = $("<ul/>");
           list.append("<li><a href='#tab-2'>igv</a></li>");
           list.append("<li><a href='#tab-1'>cytoscape</a></li>");
           list.append("<li><a href='#tab-3'>three</a></li>");
           masterTabsDiv.append(list);

           var tab2 = $("<div id='tab-1'></div>");
            
           tab2.append("<div id='cyOuterDiv' style='border:1px solid red; margin: auto; width: 98% height: 95%;'>" +
                       "<div id='cyMenubarDiv' style='background-color: lightgray; height: 25px'>" + 
                       "<button id='cyFitButton'>Fit</button>" +
                       "<button id='cyFitSelectedButton'>Fit Selected</button>" +
                       "<button id='cySFNButton'>SFN</button>" +
                       "<button id='cyHideUnselectedButton'>Hide Unselected</button>" +
                       "<button id='cyShowAllButton'>Show All</button>" +
                       "</div>" +
                       "<div id='cyDiv' style='background-color: lightgray; border:1px solid green; margin: auto; width: 100%; height: 98%'></div> </div>");

           var tab1 = $("<div id='tab-2'></div>");
           tab1.append("<div id='igvDiv' style='border:1px solid green; height:1000px;'></div>");
            
           var tab3 = $("<div id='tab-3'>contents 3</div>");
           tab3.append("<div id='threeDiv' style='border:1px solid magenta; margin: auto; width: 98% height: 500px;'></div>");
            
           masterTabsDiv.append(tab1);
           masterTabsDiv.append(tab2);
           masterTabsDiv.append(tab3); 
           return(masterTabsDiv);
           },
         
        getCyOptions: function(){
           console.log("entering getCyOptions");
           debugger;
           var value = {container: $("#cyDiv"), 
                                      elements: {nodes: [{data: {id:'a'}}],
                                                 edges: [{data:{source:'a', target:'a'}}]},
                                      style: cytoscape.stylesheet()
                                        .selector('node').style({'background-color': '#d22',
                                                                 'label': 'data(id)',
                                                                 'text-valign': 'center',
                                                                 'text-halign': 'center',
                                                                 'border-width': 1})
                                         .selector('edge').style({'line-color': 'black',
                                                                  'target-arrow-shape': 'triangle',
                                                                  'target-arrow-color': 'black',
                                                                  'curve-style': 'bezier'})
                       };
            console.log("about to leave getCyOptions")
            return(value);
            }, // getCyOptions
        
        
        getIgvOptions: function(){
           var igvOptionsLocal = {locus: "7:101,165,560-101,165,630", // 5:88,621,548-88,999,827",
             reference: {id: "geneSymbols_hg38",
                 fastaURL: "http://pshannon.systemsbiology.net/genomes/human_g1k_v37_decoy.fasta",
                 cytobandURL: "http://pshannon.systemsbiology.net/annotations/b37_cytoband.txt"
                 },
                tracks: [
                  {name: 'Gencode v24',
                   url: "http://pshannon.systemsbiology.net/hg38/gencode.v24.annotation.sorted.gtf.gz",
                  indexURL: "http://pshannon.systemsbiology.net/hg38/gencode.v24.annotation.sorted.gtf.gz.tbi",
                  format: 'gtf',
                  visibilityWindow: 2000000,
                  displayMode: 'EXPANDED'
                  },
                 {name: 'geneSymbols_hg38',
                  url: 'http://pshannon.systemsbiology.net/hg38/geneSymbolSearch.bed',
                  indexed: false,
                  searchable: true,
                  //visibilityWindow: 5000000,
                  displayMode: 'COLLAPSED',
                  color: "#448844"
                  },
                 {name: "igap gwas",
                  url: 'http://pshannon.systemsbiology.net/hg38/variants/igap.bed',
                  indexed: false,
                  searchable: true,
                  //visibilityWindow: 5000000,
                  displayMode: 'EXPANDED',
                  color: "#884444"
                  }
                  ]
              }; // igvOptionsLocal
             return(igvOptionsLocal);
           }, // getIgvOptions
 
        initializeNewThreeTab: function(){
           window.app3d = app3d;
           app3d.init("threeDiv");
           var data = [{"chr":6,"pos":147596,"an":1,"af":0.9812,"EC1":0.0244},
                   {"chr":6,"pos":148039,"an":1,"af":0.7887,"EC1":-0.1367}];
           var data3 = [{"gene":"ADNP","x":-146.0568,"y":-0.7765,"z":1.345},
        {"gene":"ADNP2","x":-111.9405,"y":0.7206,"z":1.9047},
        {"gene":"ALX1","x":51.0562,"y":-9.3092,"z":-15.5825},
        {"gene":"ALX3","x":-38.909,"y":-2.9967,"z":1.2376},
        {"gene":"ALX4","x":-17.7929,"y":-2.9019,"z":-6.0279},
        {"gene":"ARGFX","x":87.2175,"y":7.2355,"z":-7.2452},
        {"gene":"ARX","x":-91.302,"y":5.8996,"z":0.9547},
        {"gene":"CRX","x":24.0127,"y":-1.8842,"z":1.1204},
        {"gene":"DPRX","x":96.4716,"y":-11.5378,"z":13.6378},
        {"gene":"DRGX","x":-30.3606,"y":20.0352,"z":0.1661},
        {"gene":"DUXA","x":124.1735,"y":1.6056,"z":1.2608},
        {"gene":"ELF3","x":6.4602,"y":2.3951,"z":2.9274},
        {"gene":"ESX1","x":125.4266,"y":2.1301,"z":2.1979},
        {"gene":"GSC","x":101.5726,"y":-6.5466,"z":-5.9331},
        {"gene":"GSC2","x":113.1012,"y":4.1633,"z":4.064},
        {"gene":"HESX1","x":-21.7646,"y":-0.5798,"z":4.4957},
        {"gene":"HOMEZ","x":-90.9856,"y":-2.7474,"z":1.2747},
        {"gene":"HOPX","x":-155.0937,"y":4.4173,"z":-0.9517},
        {"gene":"ISX","x":121.934,"y":2.3002,"z":0.6731},
        {"gene":"LEUTX","x":125.4103,"y":1.7299,"z":2.0273},
        {"gene":"MEF2C","x":-168.1081,"y":10.322,"z":2.2409},
        {"gene":"MIXL1","x":29.8232,"y":5.892,"z":5.236},
        {"gene":"NOBOX","x":114.7024,"y":4.3831,"z":2.4121},
        {"gene":"OTP","x":73.4485,"y":22.8675,"z":1.6014},
        {"gene":"OTX1","x":-61.1505,"y":-1.0486,"z":-1.7832},
        {"gene":"OTX2","x":91.1536,"y":-16.412,"z":5.2518},
        {"gene":"PHOX2A","x":85.9395,"y":0.0448,"z":-10.3375},
        {"gene":"PHOX2B","x":123.2999,"y":1.5495,"z":4.2616},
        {"gene":"PITX1","x":86.3081,"y":-14.9184,"z":-4.9912},
        {"gene":"PITX2","x":113.9095,"y":3.6351,"z":-1.4579},
        {"gene":"PITX3","x":98.6208,"y":3.3063,"z":4.4485},
        {"gene":"PROP1","x":54.8345,"y":11.5046,"z":-22.5947},
        {"gene":"PRRX1","x":-97.6001,"y":-7.997,"z":-0.1716},
        {"gene":"PRRX2","x":-2.1381,"y":-7.3584,"z":-5.464},
        {"gene":"RAX","x":98.7371,"y":-21.1732,"z":20.4459},
        {"gene":"RAX2","x":32.2993,"y":-5.3922,"z":-13.5179},
        {"gene":"RHOXF1","x":-17.3206,"y":2.2535,"z":-0.6599},
        {"gene":"RHOXF2","x":98.8198,"y":-1.538,"z":0.2752},
        {"gene":"RHOXF2B","x":117.5179,"y":6.9199,"z":4.6329},
        {"gene":"SEBOX","x":87.627,"y":3.701,"z":-6.6413},
        {"gene":"SHOX","x":79.1686,"y":-6.9527,"z":-11.0102},
        {"gene":"SHOX2","x":60.181,"y":7.1496,"z":16.6654},
        {"gene":"TPRX1","x":84.3702,"y":-3.7428,"z":-17.4296},
        {"gene":"TSHZ1","x":-129.1469,"y":-0.1712,"z":-0.473},
        {"gene":"TSHZ2","x":-101.2699,"y":7.1558,"z":1.7648},
        {"gene":"TSHZ3","x":-105.0179,"y":4.4191,"z":0.3811},
        {"gene":"UNCX","x":111.1621,"y":-6.2214,"z":7.1391},
        {"gene":"VSX1","x":-64.5455,"y":-4.3236,"z":1.3823},
        {"gene":"VSX2","x":36.9045,"y":17.9651,"z":13.514},
        {"gene":"ZBTB16","x":-125.9315,"y":-0.172,"z":0.1997},
        {"gene":"ZEB1","x":-144.9223,"y":-2.2388,"z":0.2275},
        {"gene":"ZEB2","x":-166.8426,"y":-5.2802,"z":3.2249},
        {"gene":"ZFHX2","x":-113.4972,"y":-0.5561,"z":-3.1312},
        {"gene":"ZFHX3","x":-115.247,"y":-3.7799,"z":-1.7704},
        {"gene":"ZFHX4","x":-120.3524,"y":-4.7425,"z":0.162},
        {"gene":"ZHX1","x":-140.2198,"y":-3.0202,"z":0.5338},
        {"gene":"ZHX2","x":-124.4253,"y":-5.8642,"z":1.7423},
        {"gene":"ZHX3","x":-153.7224,"y":-3.5177,"z":0.1432}];
           app3d.drawScatterPlot(data3);
           app3d.resize();
           app3d.animate()
           },

        render: function() {
            var multiWidget = this;
            $(window).resize(multiWidget.resizeHandler);
            console.log("entering render");
            this.listenTo(this.model, 'change:msgFromKernel', this.dispatchRequest, this);
            this.masterTabsDiv = this.createMasterTabsDiv();
            this.$el.append(this.masterTabsDiv);
            this.listenTo(this.model, 'change:frameHeight', this.frameDimensionsChanged, this);
            setTimeout(function(){
               window.browser = igv.createBrowser($("#igvDiv"), multiWidget.getIgvOptions());
               //window.browser.on('locuschange', function(referenceFrame, chromLocString){multiWidget.chromLocString=chromLocString});
               window.browser.on('locuschange', function(referenceFrame, chromLocString){
                   multiWidget.updateChromLocStringToKernel(multiWidget, chromLocString)});
               setTimeout(function() {
                   console.log("about to call tabs()");
                    $("#masterTabsDiv").tabs({
                       activate: function(){
                         console.log("tabs Activate");
                         console.log("cyInitialized: " + window.cyInitialized);
                         if(!window.cyInitialized){
                            cy.fit(200);   // condition this on first time display only
                            window.cyInitialized = true;
                            }
                         cy.resize();
                       }});
                   $("[href='#tab-2']").trigger("click");
                   }, 8000);
               }, 3000);
            setTimeout(function(){
               console.log("starting cy creation setTimeout")
               console.log("   cyDiv defined? " + $("#cyDiv"));
               var options = multiWidget.getCyOptions();
               console.log("   after  getCyOptions")
               console.log(JSON.stringify(options));
               window.cy = cytoscape(multiWidget.getCyOptions());
               console.log("window.cy created")
               $("#cyDiv").height(800);
               $("#cyFitButton").click(function(){cy.fit(50)});
               $("#cyFitSelectedButton").click(function(){cy.fit(cy.nodes(":selected"), 50)});
               $("#cySFNButton").click(function(){cy.nodes(':selected').neighborhood().nodes().select()});
               $("#cyHideUnselectedButton").click(function(){cy.nodes(":unselected").hide()});
               $("#cyShowAllButton").click(function(){cy.nodes().show(); cy.edges().show()});
               cy.fit(100);
               multiWidget.initializeNewThreeTab();
               multiWidget.resizeHandler();
               }, 1000);
             }, // render

        dispatchRequest: function(){
           console.log("dispatchRequest");
           var msgRaw = this.model.get("msgFromKernel");
           var msg = JSON.parse(msgRaw);
           console.log(msg);
           console.log("========================");
           console.log(this);
           switch(msg.cmd) {
              case 'ping':
                console.log("ping! from the kernel");
                break;
              case 'deleteGraph':
                cy.edges().remove();
                cy.nodes().remove();
                break;
              case 'layout':
                 var layoutName = msg.payload;
                 cy.layout({"name": layoutName})
                 break;
              case 'loadNetworkStyleFile':
                var styleFile = msg.payload;
                this.loadNetworkStyleFile(styleFile);
                break;
              case 'tfGridLayout':
                cy.nodes().filter("[type='TF']").layout({name: "grid"})
                break;
              case 'selectedNodeGridLayout':
                cy.nodes(":selected").layout({name: "grid"})
                break;
              case 'addGraph':
                console.log("--- addGraph")
                var jsonGraph = msg.payload;
                console.log(jsonGraph)
                cy.json(jsonGraph);
                cy.fit(100);
                $("[href='#tab-1']").trigger("click");
                break;
              case 'fit':
                 var margin = msg.payload;
                 console.log("fit with margin: " + margin)
                 cy.fit(margin);
                 break;
               case 'fitSelected':
                 var margin = msg.payload;
                 console.log("fit with margin: " + margin)
                 cy.fit(cy.nodes(":selected"), margin);
                 break;


            default:
               console.log("unrecognized msg.cmd: " + msg.cmd);
             } // switch
           }, 

       updateChromLocStringToKernel: function(multiWidget, newString){
           console.log("xxx " + newString);
           var jsonString = JSON.stringify({cmd: "updateChromLocString",
                                            status: "request",
                                            callback: "",
                                            payload: newString});
            multiWidget.model.set("msgFromBrowser", jsonString);
            multiWidget.touch()
            },
                                                
        loadNetworkStyleFile: function(filename){
           console.log("--- entering loadNetworkStyleFile")
           var cyObj = window.cy
           var str = window.location.href;
           var url = str.substr(0, str.lastIndexOf("/")) + "/" + filename;
           url = url.replace("/notebooks/", "/files/");
           console.log("--- loadNetworkStyleFile: " +  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);
                });
          },


        frameDimensionsChanged: function(){
           console.log("frameDimensionsChanged");
           var oldHeight = $("#masterTabsDiv").height()
           var oldWidth  = $("#masterTabsDiv").width()
           var newHeight = this.model.get("frameHeight");
           var msg = "<center>tabs demo, height: " + oldHeight + " -> " + newHeight + "</center>";
           $("#masterTabsDiv").height(newHeight);
           }, 
    });
    return {
        TabsView: TabsView
    };
});



In [6]:
app = TabsWidget()
display(app)



In [7]:
app.deleteGraph()

In [8]:
app.setHeight(1800)


setHeight(1800) 

In [9]:
print(app.getChromLocString())


chr1:0-0

In [10]:
import zmq

In [11]:
socketContext = zmq.Context()
socket = socketContext.socket(zmq.REQ)
socket.connect("tcp://localhost:%s" % '5557')
socket


Out[11]:
<zmq.sugar.socket.Socket at 0x106aa8288>

In [ ]:
msg = {"cmd": "ping", "status": "request", "callback": "", "payload": ""}
msg_json = json.dumps(msg)
print(msg_json)
socket.send_string(json.dumps(msg))
print(json.loads(socket.recv_string()))


{"payload": "", "status": "request", "callback": "", "cmd": "ping"}
{'payload': ['pong'], 'status': ['result'], 'callback': [''], 'cmd': {}}

msg = json.dumps({"cmd": "getTestNetwork", "status": "request", "callback": "", "payload": "VGF"}) socket.send_string(msg) gjson = json.loads(socket.recv_string())["payload"] gjson

app.deleteGraph() app.addGraph(gjson)


In [ ]:
region = app.getChromLocString()
targetGene = "VGF"
msg = json.dumps({"cmd": "createGeneModel", "status": "request", "callback": "", 
                  "payload": {"targetGene": targetGene, "footprintRegion": region}})
socket.send_string(msg)
gjson = json.loads(socket.recv_string())["payload"][0] 
graphData = json.loads(gjson)
app.deleteGraph()
app.addGraph(graphData)
app.loadNetworkStyleFile("style-purityControlsNodeSize.js")

In [ ]:
graphData = json.loads(gjson)
app.deleteGraph()
app.addGraph(graphData)
app.loadNetworkStyleFile("style-purityControlsNodeSize.js")

In [ ]:


In [ ]:
app.fit()

In [ ]:
app.fitSelected()

In [ ]:
app.selectedNodeGridLayout("foo")

In [ ]:
app.availableLayouts()

In [ ]:
app.layout("grid")
app.layout("cose")

In [ ]: