jupyrdf / ipyelk Goto Github PK
View Code? Open in Web Editor NEWJupyter Widgets for interactive graphs powered by the Eclipse Layout Kernel (ELK)
License: BSD 3-Clause "New" or "Revised" License
Jupyter Widgets for interactive graphs powered by the Eclipse Layout Kernel (ELK)
License: BSD 3-Clause "New" or "Revised" License
For interactive applications when using the contrib.elements
there needs to be a mechanism to connect the Element back to some other data structures.
Adding a metadata
dictionary field onto the BaseElement
class for users to attach data useful outside the layouter logic.
We might only need One Elk and One Sprotty Server, as it appears they can both handle multiplexing requests.
It would still be good to be able to cull them when there are no more views on the page, probably.
0.3.x
branch0.3.1
This project is (or will soon be) complex enough to show non-trivial demos of:
doit
graphThese would be cute to test, as we could take the generated SVG artifacts and put them in (eventual) documentation, but these seem like useful tools, especially for canonical outputs of common developer tools.
Probably not a lot of work to do?
Appears to be an issue if the hierarchy changes sprotty will throw a "duplicate id" error when recursively adding children.
Potential fixes:
Without adding RDFlib as a hard dependency, let's offer a variant which uses the existing networkx machinery to create nice ELK JSON with ports.
Restructure the layout options dictionaries to avoid using the ElkTypes directly as keys. Instead keep the dictionaries json serializable.
#24 (comment)
It's time for another ipyelk release!
Mazama Zetta
Add unit tests into CI process.
Let's have some proper docs that somebody else builds/versions/maintains.
this can play out over several PRs, not bound to lab version (but should likely land for final lab 1 release)
ipyelk
)conf.py
README
, CHANGELOG
, CONTRIBUTING
, etc.rst
files, etc.With master
, it looks like adding a "properties": {"cssClasses": "custom-class-from-data-foo"}}
to an edge does not result in it getting added to the DOM.
We probably just need to initialize all of the properties
from the data, rather than None
, and special-case cssClasses
.
This seems worthwhile to test.
XELK
transformer is a treeCurrently the logic to handle the collapsing is in the XELK
transformer. But there are a few benefits from moving to the browser:
From a code perspective, it looks fine, if somewhat hard to grasp... however, there's almost no way I'd be able to look at the docs and actually make one of these custom thingers work. I think refining a few, targeted examples, for e.g. the edge collapsing, would be helpful... linking to the source is fine.
Originally posted by @nrbgt in #24 (comment)
Connect the completion of sprotty actions and commands back to the python kernel.
Helpful for debugging ipyelk in non-development environments.
Example attempt below:
+const Theelk = new ELK.default({ + workerFactory: () => { + ELK_DEBUG && console.warn('ELK Worker created'); + return new (Worker as any)(); + } +} as any); + export class ELKModel extends DOMWidgetModel { static model_name = 'ELKModel'; @@ -91,24 +98,25 @@ export class ELKModel extends DOMWidgetModel { } protected cullElk() { - const elk: any = this._elk; - if (elk != null) { - ELK_DEBUG && console.warn('ELK worker culling for', this.cid); - elk.worker?.terminate(); - } else { - ELK_DEBUG && console.warn('ELK was already culled for', this.cid); - } - this._elk = null; + // const elk: any = this._elk; + // if (elk != null) { + // ELK_DEBUG && console.warn('ELK worker culling for', this.cid); + // elk.worker?.terminate(); + // } else { + // ELK_DEBUG && console.warn('ELK was already culled for', this.cid); + // } + // this._elk = null; } protected ensureElk() { if (this._elk == null) { - this._elk = new ELK.default({ - workerFactory: () => { - ELK_DEBUG && console.warn('ELK Worker created'); - return new (Worker as any)(); - } - } as any); + this._elk = Theelk; + // this._elk = new ELK.default({ + // workerFactory: () => { + // ELK_DEBUG && console.warn('ELK Worker created'); + // return new (Worker as any)(); + // }
Let's get started with GitHub actions.
We likely need an offline pipeline (appears to have been started) for scraping the elk options and capturing them in a format we can easily consume from python and ts.
This could be modeled, offline, as a notebook which graduated to a series of doit tasks:
Assets:
Last-Modified
times appear to be meaningful, which will let us do some things to track provenance
Investigate how to implement container nodes and their implication with building the elk json.
Add support for:
Allow jupyterlab widgets to overlay as control surfaces on top of certain elements. The use case it to provide a richer and reactive UI as the user navigates a particular diagram.
Easier to reason about logic and errors with custom extensions.
This issue is for long-term collection of screenshots!
Enhance networkx transformer for finer grained control for attaching ELK layout options.
See https://www.eclipse.org/elk/reference/options.html for some possible option choices.
Adding Mypy tooling would add addition rigor to the library with better type understanding.
Making the examples notebooks work with importnb
, restructuring the discrete functional examples into humane names, e.g. an_ipyelk_example_with_bells(and_whistles=True)
Many of these ideas have been developed over on @deathbeds/wxyz
, where all the examples are importable, with a "kitchen sink" example that tests out having everything on the page at once, which can find DOM conflicts and performance issues.
Concretely, the "simple" example could become a function, e.g.
def make_a_simple_elk_example(elk_json=None) -> Tuple[W.Box, ElkDiagram]:
diagram = ElkDiagram()
elk_json = elk_json or ...
...
return box, diagram
if __name__ == "__main__":
box, diagram = make_a_simple_elk_example(**kwargs)
IPython.display.display(box)
then the SVG exporter could:
with importnb.Notebook():
from 00_Introduction import make_a_simple_elk_example
def make_a_simple_example_with_svg_export(box_diagram=None, **kwargs) -> Tuple[W.Box, ElkDiagram, ElkExporter]:
box, diagram = box_diagram or make_a_simple_elk_example(**kwargs)
exporter = ElkExporter(diagram=simple)
...
return box, diagram, exporter
if __name__ == "__main__":
box, diagram, exporter = make_a_simple_example_with_svg_export()
IPython.display.display(box)
Box
subclasses
dlink
ingIt's time for another ipyelk release!
Rangifer tarandus
Developing off master. Running doit lab
after a git clean -xdf
results in everything building with no errors and lab starting. However, inside the example notebooks jupyterlab cannot find the diagram widget with the error:
Module @jupyrdf/jupyter-elk, semver range 1.0.1 is not registered as a widget module
It looks like the ipyelk
extension isn't registered/being discovered. !jupyter labextension list
only lists bqplot and the widget-manager.
It would be useful to have a mime type render for simple diagrams. The main use case is for having diagrams rendered in the built docs.
Let's get a JupyterLab 1.0-compatible package up before starting on Lab 2.x compatibility.
Let's get a nice binder working!
Make a Box
subclass which uses the position of Elk nodes to place its children
.
For some kinds of interaction, it would be interesting to put interactive elements directly on an elk diagram.
With a real plotting library (as a demo dependency) on the widget bus, e.g. bqplot
, we could show things like signals being transformed based on inputs, e.g. a slider:
|-----------------| |-----------------|
| | | -- |
| X ----O---- 0.3 []--[] / \ |
| | |---- --------|
|-----------------| |-----------------|
The python side of the house would probably just require a child_nodes = T.Dict()
that mapped each child (or the index in children
, lighter-weight) to the id
of a node (or a label or port, i guess).
The typescript side will be... weird. We have this over on wxyz, which was (grossly, much any
) ported from ipylayouts. We can repurpose a lot of this, and replace a good deal of the expensive DOM measurement code with _mark_layouts
and the root <g transform="scale(z) translate(x, y)">
. Unlike that, this would allow for elements that are off-canvas, so the box DOM might be something like:
<div class="p-Widget p-Panel jupyter-widgets widget-container widget-box widget-vbox styled-widget-140473608868624 jp-ElkBoxView" style="overflow: hidden;">
<div class="p-Widget jp-ElkView"></div>
<div class="p-Widget jp-FloatSlider"
style="position: absolute; left: 50px; top: 50px; zoom: 0.5"; width: 50px; height: 20px;"></div>
</div>
We would likely not publish the details back to the kernel, unless asked, as this would be doing LOTS of calculations during a pan/zoom interaction... indeed, we may want to hide the elements altogether until the DOM "cools off".
ElkTransformer
to_dict
and from_dict
. Issue with a port dict that has an ElkLabel
instance instead of a dict that looks like an ElkLabel
get_properties
get_layout
and get_css
should work. Remove them?We should validate the documented functionality with robot framework:
It would be lovely to be able to export properly-styled SVG of the current viewport, suitable for printing.
As this would be expensive, it would likely be a message-based thing, rather than a mode trait.
It would be required to inject a fair amount of CSS to make it look right.
Some added, super-amazing features:
xlink:href
to make things clickableIssue with the schema generated for labels. Elklabels are allowed to have Elklabeks with extended properties.
Not a "real" 1.0.0 (will be basically the same as 0.3.0 #69) , the major version bump will help us distinguish in the packaging approach w/r/t jupyterlab 3, and let us do backports/releases more simply.
1.0.1
Need a new version which contains all the code, and potentially a test step...
personally would like an option for this to be vertical....
Originally posted by @nrbgt in #15 (comment)
The usability can be improved with a clearer api surface area.
contrib.elements
to ipyelk.elements
BaseElement
Mark
sCompound
class to MarkFactory
Symbol
and Shape
schema and api.ConnectorDef
aka ElementSymbol
offset fieldsExample diff for cleaner style tags
diff --git a/src/exporter.ts b/src/exporter.ts index 7ceb3e1..c435492 100644 --- a/src/exporter.ts +++ b/src/exporter.ts @@ -151,6 +151,14 @@ export class ELKExporterModel extends WidgetModel { this._update_timeout = setTimeout(() => this._on_layout_updated(), 1000); } + makeStyleTag(style: string) { + return !style.trim().length ? "" : `<style type="text/css"> + <![CDATA[ + ${style} + ]]> + </style>`; + } + async _on_layout_updated() { if (!this.enabled) { return; @@ -166,17 +174,13 @@ export class ELKExporterModel extends WidgetModel { const strip_ids = this.get('strip_ids'); const add_xml_header = this.get('add_xml_header'); const raw_app_css = this.app_raw_css; - const rawStyle = ` - ${STANDALONE_CSS} - ${raw_app_css.join('\n')} - ${this.get('extra_css') || ''} - `; - const style = ` - <style type="text/css"> - <![CDATA[ - ${rawStyle} - ]]> - </style>`; + const rawStyle = [ + STANDALONE_CSS, + raw_app_css.join('\n'), + this.get('extra_css') || '', + ] + + const style = rawStyle.map(this.makeStyleTag).join("\n"); const g: SVGGElement = svg.querySelector('g'); const transform = g.attributes['transform'].value; let scaleFactor = 1.0; @@ -189,13 +193,17 @@ export class ELKExporterModel extends WidgetModel { let withCSS = outerHTML .replace( /<svg([^>]+)>/, - `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width / scaleFactor + - padding} ${height / scaleFactor + padding}" $1> + `<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + viewBox="0 0 ${width / scaleFactor + padding} ${height / scaleFactor + padding}" + $1>
Sometimes creating a new sprotty that is initially hidden will result in the diagram not rendering.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.