anywidget-graph 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anywidget_graph-0.1.0/.gitignore +33 -0
- anywidget_graph-0.1.0/.python-version +1 -0
- anywidget_graph-0.1.0/PKG-INFO +258 -0
- anywidget_graph-0.1.0/README.md +225 -0
- anywidget_graph-0.1.0/examples/demo.py +58 -0
- anywidget_graph-0.1.0/pyproject.toml +87 -0
- anywidget_graph-0.1.0/src/anywidget_graph/__init__.py +6 -0
- anywidget_graph-0.1.0/src/anywidget_graph/py.typed +0 -0
- anywidget_graph-0.1.0/src/anywidget_graph/widget.py +293 -0
- anywidget_graph-0.1.0/tests/__init__.py +0 -0
- anywidget_graph-0.1.0/tests/test_graph.py +55 -0
- anywidget_graph-0.1.0/uv.lock +981 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
develop-eggs/
|
|
8
|
+
dist/
|
|
9
|
+
downloads/
|
|
10
|
+
eggs/
|
|
11
|
+
.eggs/
|
|
12
|
+
lib/
|
|
13
|
+
lib64/
|
|
14
|
+
parts/
|
|
15
|
+
sdist/
|
|
16
|
+
var/
|
|
17
|
+
wheels/
|
|
18
|
+
*.cl*/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
ENV/
|
|
25
|
+
.env
|
|
26
|
+
*.log
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.ruff_cache/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.ty/
|
|
33
|
+
node_modules/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anywidget-graph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive graph visualization for Python notebooks using anywidget
|
|
5
|
+
Project-URL: Homepage, https://grafeo.dev/
|
|
6
|
+
Project-URL: Repository, https://github.com/GrafeoDB/anywidget-graph
|
|
7
|
+
Author-email: "S.T. Grond" <widget@grafeo.dev>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Keywords: anywidget,cypher,graph,jupyter,marimo,neo4j,visualization
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: Jupyter
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: anywidget>=0.9.21
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: marimo>=0.19.7; extra == 'dev'
|
|
24
|
+
Requires-Dist: prek>=0.3.1; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.14.14; extra == 'dev'
|
|
27
|
+
Requires-Dist: ty>=0.0.14; extra == 'dev'
|
|
28
|
+
Provides-Extra: networkx
|
|
29
|
+
Requires-Dist: networkx>=3.0; extra == 'networkx'
|
|
30
|
+
Provides-Extra: pandas
|
|
31
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# anywidget-graph
|
|
35
|
+
|
|
36
|
+
Interactive graph visualization for Python notebooks.
|
|
37
|
+
|
|
38
|
+
Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Universal** — One widget, every notebook environment
|
|
43
|
+
- **Backend-agnostic** — Grafeo, Neo4j, NetworkX, pandas, or raw dicts
|
|
44
|
+
- **Interactive** — Pan, zoom, click, expand neighbors, select paths
|
|
45
|
+
- **Customizable** — Colors, sizes, shapes, layouts
|
|
46
|
+
- **Performant** — Virtualized rendering for large graphs
|
|
47
|
+
- **Exportable** — PNG, SVG, JSON
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv add anywidget-graph
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from anywidget_graph import Graph
|
|
59
|
+
|
|
60
|
+
graph = Graph.from_dict({
|
|
61
|
+
"nodes": [
|
|
62
|
+
{"id": "alice", "label": "Alice", "group": "person"},
|
|
63
|
+
{"id": "bob", "label": "Bob", "group": "person"},
|
|
64
|
+
{"id": "paper", "label": "Graph Theory", "group": "document"},
|
|
65
|
+
],
|
|
66
|
+
"edges": [
|
|
67
|
+
{"source": "alice", "target": "bob", "label": "knows"},
|
|
68
|
+
{"source": "alice", "target": "paper", "label": "authored"},
|
|
69
|
+
]
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
graph
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Data Sources
|
|
76
|
+
|
|
77
|
+
### Dictionary
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from anywidget_graph import Graph
|
|
81
|
+
|
|
82
|
+
graph = Graph.from_dict({
|
|
83
|
+
"nodes": [{"id": "a"}, {"id": "b"}],
|
|
84
|
+
"edges": [{"source": "a", "target": "b"}]
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Grafeo
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from grafeo import GrafeoDB
|
|
92
|
+
from anywidget_graph import Graph
|
|
93
|
+
|
|
94
|
+
db = GrafeoDB()
|
|
95
|
+
db.execute("INSERT (:Person {name: 'Alice'})-[:KNOWS]->(:Person {name: 'Bob'})")
|
|
96
|
+
|
|
97
|
+
result = db.execute("MATCH (a)-[r]->(b) RETURN a, r, b")
|
|
98
|
+
graph = Graph.from_grafeo(result)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Neo4j
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from neo4j import GraphDatabase
|
|
105
|
+
from anywidget_graph import Graph
|
|
106
|
+
|
|
107
|
+
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
|
|
108
|
+
|
|
109
|
+
with driver.session() as session:
|
|
110
|
+
result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 100")
|
|
111
|
+
graph = Graph.from_neo4j(result)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### NetworkX
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import networkx as nx
|
|
118
|
+
from anywidget_graph import Graph
|
|
119
|
+
|
|
120
|
+
G = nx.karate_club_graph()
|
|
121
|
+
graph = Graph.from_networkx(G)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### pandas
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import pandas as pd
|
|
128
|
+
from anywidget_graph import Graph
|
|
129
|
+
|
|
130
|
+
edges = pd.DataFrame({
|
|
131
|
+
"source": ["alice", "alice", "bob"],
|
|
132
|
+
"target": ["bob", "carol", "carol"],
|
|
133
|
+
"weight": [1.0, 0.5, 0.8]
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
graph = Graph.from_pandas(edges)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Interactivity
|
|
140
|
+
|
|
141
|
+
### Events
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
graph = Graph.from_dict(data)
|
|
145
|
+
|
|
146
|
+
@graph.on_node_click
|
|
147
|
+
def handle_node(node_id, node_data):
|
|
148
|
+
print(f"Clicked: {node_id}")
|
|
149
|
+
|
|
150
|
+
@graph.on_edge_click
|
|
151
|
+
def handle_edge(edge_id, edge_data):
|
|
152
|
+
print(f"Edge: {edge_data['label']}")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Selection
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
graph.selected_nodes # Get current selection
|
|
159
|
+
graph.select(["alice"]) # Select nodes
|
|
160
|
+
graph.clear_selection() # Clear
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Expansion
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
graph.expand("alice") # Show neighbors
|
|
167
|
+
graph.collapse("alice") # Hide neighbors
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Styling
|
|
171
|
+
|
|
172
|
+
### By Group
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
graph = Graph.from_dict(
|
|
176
|
+
data,
|
|
177
|
+
node_styles={
|
|
178
|
+
"person": {"color": "#4CAF50", "size": 30},
|
|
179
|
+
"document": {"color": "#2196F3", "shape": "square"},
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### By Property
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
graph = Graph.from_dict(
|
|
188
|
+
data,
|
|
189
|
+
node_color="group", # Color by field
|
|
190
|
+
node_size=lambda n: n["score"] * 10, # Size by function
|
|
191
|
+
edge_width="weight", # Width by field
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Layouts
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
Graph.from_dict(data, layout="force") # Default
|
|
199
|
+
Graph.from_dict(data, layout="hierarchical")
|
|
200
|
+
Graph.from_dict(data, layout="circular")
|
|
201
|
+
Graph.from_dict(data, layout="grid")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Options
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
graph = Graph.from_dict(
|
|
208
|
+
data,
|
|
209
|
+
width=800,
|
|
210
|
+
height=600,
|
|
211
|
+
directed=True,
|
|
212
|
+
labels=True,
|
|
213
|
+
edge_labels=False,
|
|
214
|
+
physics=True,
|
|
215
|
+
zoom=(0.1, 4),
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Large Graphs
|
|
220
|
+
|
|
221
|
+
For 1000+ nodes:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
graph = Graph.from_dict(
|
|
225
|
+
data,
|
|
226
|
+
virtualize=True,
|
|
227
|
+
cluster=True,
|
|
228
|
+
)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Export
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
graph.to_png("graph.png")
|
|
235
|
+
graph.to_svg("graph.svg")
|
|
236
|
+
graph.to_json("graph.json")
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Environment Support
|
|
240
|
+
|
|
241
|
+
| Environment | Supported |
|
|
242
|
+
|-------------|-----------|
|
|
243
|
+
| Marimo | ✅ |
|
|
244
|
+
| JupyterLab | ✅ |
|
|
245
|
+
| Jupyter Notebook | ✅ |
|
|
246
|
+
| VS Code | ✅ |
|
|
247
|
+
| Google Colab | ✅ |
|
|
248
|
+
| Databricks | ✅ |
|
|
249
|
+
|
|
250
|
+
## Related
|
|
251
|
+
|
|
252
|
+
- [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
|
|
253
|
+
- [Grafeo](https://github.com/GrafeoDB/grafeo) — Embeddable graph database
|
|
254
|
+
- [grafeo-wasm](https://github.com/GrafeoDB/grafeo-wasm) — Grafeo in the browser
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
Apache-2.0
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# anywidget-graph
|
|
2
|
+
|
|
3
|
+
Interactive graph visualization for Python notebooks.
|
|
4
|
+
|
|
5
|
+
Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Universal** — One widget, every notebook environment
|
|
10
|
+
- **Backend-agnostic** — Grafeo, Neo4j, NetworkX, pandas, or raw dicts
|
|
11
|
+
- **Interactive** — Pan, zoom, click, expand neighbors, select paths
|
|
12
|
+
- **Customizable** — Colors, sizes, shapes, layouts
|
|
13
|
+
- **Performant** — Virtualized rendering for large graphs
|
|
14
|
+
- **Exportable** — PNG, SVG, JSON
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv add anywidget-graph
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from anywidget_graph import Graph
|
|
26
|
+
|
|
27
|
+
graph = Graph.from_dict({
|
|
28
|
+
"nodes": [
|
|
29
|
+
{"id": "alice", "label": "Alice", "group": "person"},
|
|
30
|
+
{"id": "bob", "label": "Bob", "group": "person"},
|
|
31
|
+
{"id": "paper", "label": "Graph Theory", "group": "document"},
|
|
32
|
+
],
|
|
33
|
+
"edges": [
|
|
34
|
+
{"source": "alice", "target": "bob", "label": "knows"},
|
|
35
|
+
{"source": "alice", "target": "paper", "label": "authored"},
|
|
36
|
+
]
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
graph
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Data Sources
|
|
43
|
+
|
|
44
|
+
### Dictionary
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from anywidget_graph import Graph
|
|
48
|
+
|
|
49
|
+
graph = Graph.from_dict({
|
|
50
|
+
"nodes": [{"id": "a"}, {"id": "b"}],
|
|
51
|
+
"edges": [{"source": "a", "target": "b"}]
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Grafeo
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from grafeo import GrafeoDB
|
|
59
|
+
from anywidget_graph import Graph
|
|
60
|
+
|
|
61
|
+
db = GrafeoDB()
|
|
62
|
+
db.execute("INSERT (:Person {name: 'Alice'})-[:KNOWS]->(:Person {name: 'Bob'})")
|
|
63
|
+
|
|
64
|
+
result = db.execute("MATCH (a)-[r]->(b) RETURN a, r, b")
|
|
65
|
+
graph = Graph.from_grafeo(result)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Neo4j
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from neo4j import GraphDatabase
|
|
72
|
+
from anywidget_graph import Graph
|
|
73
|
+
|
|
74
|
+
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
|
|
75
|
+
|
|
76
|
+
with driver.session() as session:
|
|
77
|
+
result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 100")
|
|
78
|
+
graph = Graph.from_neo4j(result)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### NetworkX
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import networkx as nx
|
|
85
|
+
from anywidget_graph import Graph
|
|
86
|
+
|
|
87
|
+
G = nx.karate_club_graph()
|
|
88
|
+
graph = Graph.from_networkx(G)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### pandas
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import pandas as pd
|
|
95
|
+
from anywidget_graph import Graph
|
|
96
|
+
|
|
97
|
+
edges = pd.DataFrame({
|
|
98
|
+
"source": ["alice", "alice", "bob"],
|
|
99
|
+
"target": ["bob", "carol", "carol"],
|
|
100
|
+
"weight": [1.0, 0.5, 0.8]
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
graph = Graph.from_pandas(edges)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Interactivity
|
|
107
|
+
|
|
108
|
+
### Events
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
graph = Graph.from_dict(data)
|
|
112
|
+
|
|
113
|
+
@graph.on_node_click
|
|
114
|
+
def handle_node(node_id, node_data):
|
|
115
|
+
print(f"Clicked: {node_id}")
|
|
116
|
+
|
|
117
|
+
@graph.on_edge_click
|
|
118
|
+
def handle_edge(edge_id, edge_data):
|
|
119
|
+
print(f"Edge: {edge_data['label']}")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Selection
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
graph.selected_nodes # Get current selection
|
|
126
|
+
graph.select(["alice"]) # Select nodes
|
|
127
|
+
graph.clear_selection() # Clear
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Expansion
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
graph.expand("alice") # Show neighbors
|
|
134
|
+
graph.collapse("alice") # Hide neighbors
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Styling
|
|
138
|
+
|
|
139
|
+
### By Group
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
graph = Graph.from_dict(
|
|
143
|
+
data,
|
|
144
|
+
node_styles={
|
|
145
|
+
"person": {"color": "#4CAF50", "size": 30},
|
|
146
|
+
"document": {"color": "#2196F3", "shape": "square"},
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### By Property
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
graph = Graph.from_dict(
|
|
155
|
+
data,
|
|
156
|
+
node_color="group", # Color by field
|
|
157
|
+
node_size=lambda n: n["score"] * 10, # Size by function
|
|
158
|
+
edge_width="weight", # Width by field
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Layouts
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
Graph.from_dict(data, layout="force") # Default
|
|
166
|
+
Graph.from_dict(data, layout="hierarchical")
|
|
167
|
+
Graph.from_dict(data, layout="circular")
|
|
168
|
+
Graph.from_dict(data, layout="grid")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Options
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
graph = Graph.from_dict(
|
|
175
|
+
data,
|
|
176
|
+
width=800,
|
|
177
|
+
height=600,
|
|
178
|
+
directed=True,
|
|
179
|
+
labels=True,
|
|
180
|
+
edge_labels=False,
|
|
181
|
+
physics=True,
|
|
182
|
+
zoom=(0.1, 4),
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Large Graphs
|
|
187
|
+
|
|
188
|
+
For 1000+ nodes:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
graph = Graph.from_dict(
|
|
192
|
+
data,
|
|
193
|
+
virtualize=True,
|
|
194
|
+
cluster=True,
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Export
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
graph.to_png("graph.png")
|
|
202
|
+
graph.to_svg("graph.svg")
|
|
203
|
+
graph.to_json("graph.json")
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Environment Support
|
|
207
|
+
|
|
208
|
+
| Environment | Supported |
|
|
209
|
+
|-------------|-----------|
|
|
210
|
+
| Marimo | ✅ |
|
|
211
|
+
| JupyterLab | ✅ |
|
|
212
|
+
| Jupyter Notebook | ✅ |
|
|
213
|
+
| VS Code | ✅ |
|
|
214
|
+
| Google Colab | ✅ |
|
|
215
|
+
| Databricks | ✅ |
|
|
216
|
+
|
|
217
|
+
## Related
|
|
218
|
+
|
|
219
|
+
- [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
|
|
220
|
+
- [Grafeo](https://github.com/GrafeoDB/grafeo) — Embeddable graph database
|
|
221
|
+
- [grafeo-wasm](https://github.com/GrafeoDB/grafeo-wasm) — Grafeo in the browser
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
Apache-2.0
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Marimo demo for anywidget-graph."""
|
|
2
|
+
|
|
3
|
+
import marimo
|
|
4
|
+
|
|
5
|
+
__generated_with = "0.19.5"
|
|
6
|
+
app = marimo.App(width="medium")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.cell
|
|
10
|
+
def _():
|
|
11
|
+
import marimo as mo
|
|
12
|
+
|
|
13
|
+
return (mo,)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.cell
|
|
17
|
+
def _():
|
|
18
|
+
from anywidget_graph import Graph
|
|
19
|
+
|
|
20
|
+
return (Graph,)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.cell
|
|
24
|
+
def _(Graph):
|
|
25
|
+
# Create a simple graph from a dictionary
|
|
26
|
+
graph = Graph.from_dict(
|
|
27
|
+
{
|
|
28
|
+
"nodes": [
|
|
29
|
+
{"id": "alice", "label": "Alice", "color": "#4CAF50"},
|
|
30
|
+
{"id": "bob", "label": "Bob", "color": "#2196F3"},
|
|
31
|
+
{"id": "carol", "label": "Carol", "color": "#FF9800"},
|
|
32
|
+
{"id": "paper", "label": "Graph Theory", "color": "#9C27B0"},
|
|
33
|
+
],
|
|
34
|
+
"edges": [
|
|
35
|
+
{"source": "alice", "target": "bob", "label": "knows"},
|
|
36
|
+
{"source": "alice", "target": "carol", "label": "knows"},
|
|
37
|
+
{"source": "bob", "target": "carol", "label": "knows"},
|
|
38
|
+
{"source": "alice", "target": "paper", "label": "authored"},
|
|
39
|
+
{"source": "bob", "target": "paper", "label": "reviewed"},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
width=800,
|
|
43
|
+
height=500,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
graph
|
|
47
|
+
return (graph,)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.cell
|
|
51
|
+
def _(graph, mo):
|
|
52
|
+
# Display selected node info
|
|
53
|
+
mo.md(f"**Selected node:** {graph.selected_node}")
|
|
54
|
+
return ()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
app.run()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "anywidget-graph"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Interactive graph visualization for Python notebooks using anywidget"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "Apache-2.0" }
|
|
7
|
+
requires-python = ">=3.12"
|
|
8
|
+
authors = [{ name = "S.T. Grond", email = "widget@grafeo.dev" }]
|
|
9
|
+
keywords = ["anywidget", "graph", "visualization", "jupyter", "marimo", "cypher", "neo4j"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Framework :: Jupyter",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"License :: OSI Approved :: Apache Software License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Programming Language :: Python :: 3.14",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
dependencies = [
|
|
24
|
+
"anywidget>=0.9.21",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"prek>=0.3.1",
|
|
30
|
+
"pytest>=9.0.2",
|
|
31
|
+
"ruff>=0.14.14",
|
|
32
|
+
"ty>=0.0.14",
|
|
33
|
+
"marimo>=0.19.7",
|
|
34
|
+
]
|
|
35
|
+
networkx = ["networkx>=3.0"]
|
|
36
|
+
pandas = ["pandas>=2.0"]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://grafeo.dev/"
|
|
40
|
+
Repository = "https://github.com/GrafeoDB/anywidget-graph"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["hatchling"]
|
|
44
|
+
build-backend = "hatchling.build"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/anywidget_graph"]
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
line-length = 120
|
|
51
|
+
target-version = "py312"
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint]
|
|
54
|
+
select = [
|
|
55
|
+
"E",
|
|
56
|
+
"F",
|
|
57
|
+
"I",
|
|
58
|
+
"UP",
|
|
59
|
+
"FA",
|
|
60
|
+
"Q",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint.flake8-quotes]
|
|
64
|
+
inline-quotes = "double"
|
|
65
|
+
docstring-quotes = "double"
|
|
66
|
+
|
|
67
|
+
[tool.ty.environment]
|
|
68
|
+
python-version = "3.12"
|
|
69
|
+
|
|
70
|
+
[tool.pytest.ini_options]
|
|
71
|
+
testpaths = ["tests"]
|
|
72
|
+
pythonpath = ["src"]
|
|
73
|
+
addopts = "-v"
|
|
74
|
+
|
|
75
|
+
[tool.prek]
|
|
76
|
+
repos = [
|
|
77
|
+
{ repo = "ruff", hooks = [
|
|
78
|
+
{ id = "ruff-check", args = ["--fix", "--exit-non-zero-on-fix"] },
|
|
79
|
+
{ id = "ruff-format" },
|
|
80
|
+
]},
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
[tool.uv]
|
|
84
|
+
dev-dependencies = [
|
|
85
|
+
"pytest>=9.0.2",
|
|
86
|
+
"ruff>=0.14.14",
|
|
87
|
+
]
|
|
File without changes
|