This is a component for JustPy to place a Cytoscape graph on a page and interact with it.
Base libs:
Place the javascript component in your applications /static/components/
directory (the default /static/
dir is the directory the application is run form).
Place the python module cytoscape.py
somewhere on your python’s PATH
.
Easiest way to install is to clone the git repo to /static/src/
and symlink the two files as required above.
Define a list of nodes and edges, some styling directives, and the layout. Add the component as you would any other JustPy
component.
import justpy as jp
from cytoscape import Cytoscape
elements = [
{'data': { 'id': 'a' }},
{'data': { 'id': 'b' }},
{'data': { 'id': 'c' }},
{'data': { 'id': 'ab', 'source': 'a', 'target': 'b' }},
{'data': { 'id': 'ac', 'source': 'a', 'target': 'c' }},
]
graphstyle = [
{'selector': 'node',
'style': {
'label': 'data(id)'
}},
{'selector': 'edge',
'style': {
'width': 3,
'target-arrow-shape': 'triangle',
}},
]
layout = {'name': 'grid', 'rows': 2}
def cyto_test():
wp = jp.WebPage()
wp.head_html = '''
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script>
'''
cyto = Cytoscape(a=wp,
style = 'background-color: #fefefe; border: 1px solid; width: 400px; height: 400px;',
classes='m-2',
elements = elements,
graphstyle = graphstyle,
layout = layout,
)
return wp
jp.justpy(cyto_test)
Two methods are defined on Cytoscape
class for calling the underlying js
methods. The run_method
function (from JustPy
) and run_method_get_output
for collecting results. N.B. you must bind the WebPage
’s ‘result_ready’ event to cyto.handle_page_event
to receive output.
e.g. adding a button to reset zoom and pan:
async def button_click(self, msg):
await msg.page.cyto.run_method('reset()', msg.websocket)
def cyto_test():
wp = jp.WebPage()
wp.head_html = '''
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script>
'''
wp.cyto = Cytoscape(a=wp,
style = 'background-color: #fefefe; border: 1px solid; width: 400px; height: 400px;',
classes='m-2',
elements = elements,
graphstyle = graphstyle,
layout = layout,
)
button_classes = 'w-32 mr-2 mb-2 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full'
b = jp.Button(text=f'Reset zoom', a=wp, classes=button_classes, click=button_click)
# Need the folowing if using cyto.run_method_get_output:
wp.on('result_ready', wp.cyto.handle_page_event)
return wp
jp.justpy(cyto_test)
Pass in parameters by structuring the argument of the method as a string. e.g.
async def button_click(self, msg):
cy = msg.page.cyto
ws = msg.websocket
await cy.run_method('zoom(2)', ws)
await cy.run_method("layout({'name':'random'}).run()", ws)
x = 100
y = 10
await cy.run_method(f'pan({{x:{x}, y:{y}}})', ws)
Use run_method_get_output
to obtain the output. Stacking function calls and using selectors works. Remember to bind ‘result_ready’ to cyto.handle_page_event
.
e.g.
async def button_click(self, msg):
cy = msg.page.cyto
ws = msg.websocket
a=await cy.run_method_get_output('json()', ws)
a=await cy.run_method_get_output('getElementById("a").position()', ws)
a=await cy.run_method_get_output('$("#a").position()', ws)
To bind to Cytoscape events, first list the event in the attribute allowed_events
then use on()
. e.g.
async def pos_changed(self, msg):
target = msg.target_id
pos = await msg.page.cyto.run_method_get_output(f'getElementById("{target}").position()', msg.websocket)
print(f'Node "{target}" moved to {pos}')
def cyto_test():
wp = jp.WebPage()
wp.head_html = '''
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script>
'''
wp.cyto = Cytoscape(a=wp,
style = 'background-color: #fefefe; border: 1px solid; width: 400px; height: 400px;',
classes='m-2',
elements = elements,
graphstyle = graphstyle,
layout = layout,
allowed_events = ['free'],
)
wp.cyto.on('free', pos_changed)
wp.on('result_ready', wp.cyto.handle_page_event)
return wp
jp.justpy(cyto_test)
:Node “a” moved to {‘x’: 131.17751479289942, ‘y’: 123.07396449704142}
This is a bit hackish but possible.
When the Cytoscape
object is instantiated you can pass in a list of strings in the attribute plugins
. The strings should be javascript code to register and configure plugins (they get passed to eval()
). Function calls and events for the plugins are available on the python cyto object as usual. The js cytograph instance is available in the variable cyto
if you need to bind callbacks.
e.g. after installing ctxmenu and compound=drag-and-drop to the static/
dir,
ctxmenuconfig = """
cyto.cxtmenu({
selector: 'core',
commands: [
{ content: 'Fit',
select: function(){
cyto.fit();
}},
{ content: 'bg',
select: function(){
console.log( 'bg' );
const edata = {
'event_type': 'ctxmenu',
'data': 'bg',
'id': props.jp_props.id,
'page_id': page_id,
'websocket_id': websocket_id
};
send_to_server(edata, 'event');
}}
]});
"""
def echo(*args):
print(args)
def cyto_test():
wp = jp.WebPage()
wp.head_html = '''
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script>
<script src="/static/cytoscape-cxtmenu.js"></script>
<script src="/static/cytoscape-compound-drag-and-drop.js"></script>
'''
wp.cyto = Cytoscape(a=wp,
style = 'background-color: #fefefe; border: 1px solid; width: 400px; height: 400px;',
classes='m-2',
elements = elements,
graphstyle = graphstyle,
layout = layout,
allowed_events = ['ctxmenu','free','tap'],
plugins = [ctxmenuconfig, "cyto.compoundDragAndDrop({})"],
)
wp.cyto.on('ctxmenu', echo)
return wp
jp.justpy(cyto_test)