##// END OF EJS Templates
Removed initialjson trait from D3 example...
Jonathan Frederic -
Show More
This diff has been collapsed as it changes many lines, (579 lines changed) Show them Hide them
@@ -1,1233 +1,826 b''
1 1 {
2 2 "metadata": {
3 3 "name": ""
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "code",
12 12 "collapsed": false,
13 13 "input": [
14 "from __future__ import print_function # py 2.7 compat"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [],
19 "prompt_number": 1
20 },
21 {
22 "cell_type": "heading",
23 "level": 1,
24 "metadata": {},
25 "source": [
26 "Validate NetworkX version"
27 ]
28 },
29 {
30 "cell_type": "code",
31 "collapsed": false,
32 "input": [
33 "import networkx as nx\n",
34 "version = float('.'.join(nx.__version__.split('.')[0:2]))\n",
35 "if version < 1.8:\n",
36 " raise Exception('This notebook requires networkx version 1.8 or later. Version %s is installed on this machine.' % nx.__version__)"
37 ],
38 "language": "python",
39 "metadata": {},
40 "outputs": [],
41 "prompt_number": 2
42 },
43 {
44 "cell_type": "heading",
45 "level": 1,
46 "metadata": {},
47 "source": [
48 "Simple Output Test"
49 ]
50 },
51 {
52 "cell_type": "code",
53 "collapsed": false,
54 "input": [
55 "from networkx.readwrite import json_graph\n",
56 "import json\n",
14 "from __future__ import print_function # py 2.7 compat\n",
57 15 "\n",
58 "def to_d3_json(graph):\n",
59 " data = json_graph.node_link_data(graph)\n",
60 " return json.dumps(data)"
16 "import networkx as nx"
61 17 ],
62 18 "language": "python",
63 19 "metadata": {},
64 20 "outputs": [],
65 "prompt_number": 3
66 },
67 {
68 "cell_type": "code",
69 "collapsed": false,
70 "input": [
71 "G = nx.Graph([(1,2)])\n",
72 "to_d3_json(G)"
73 ],
74 "language": "python",
75 "metadata": {},
76 "outputs": [
77 {
78 "metadata": {},
79 "output_type": "pyout",
80 "prompt_number": 4,
81 "text": [
82 "'{\"multigraph\": false, \"directed\": false, \"links\": [{\"target\": 1, \"source\": 0}], \"graph\": [], \"nodes\": [{\"id\": 1}, {\"id\": 2}]}'"
83 ]
84 }
85 ],
86 21 "prompt_number": 4
87 22 },
88 23 {
89 "cell_type": "code",
90 "collapsed": false,
91 "input": [
92 "G.add_node('test')\n",
93 "to_d3_json(G)"
94 ],
95 "language": "python",
24 "cell_type": "markdown",
96 25 "metadata": {},
97 "outputs": [
98 {
99 "metadata": {},
100 "output_type": "pyout",
101 "prompt_number": 5,
102 "text": [
103 "'{\"multigraph\": false, \"directed\": false, \"links\": [{\"target\": 1, \"source\": 0}], \"graph\": [], \"nodes\": [{\"id\": 1}, {\"id\": 2}, {\"id\": \"test\"}]}'"
104 ]
105 }
106 ],
107 "prompt_number": 5
26 "source": [
27 "This notebook demonstrates how NetworkX and D3 can be married using custom widget code."
28 ]
108 29 },
109 30 {
110 31 "cell_type": "heading",
111 32 "level": 1,
112 33 "metadata": {},
113 34 "source": [
114 "Listen To Graph Changes"
35 "Hooking NetworkX Graphs"
115 36 ]
116 37 },
117 38 {
118 39 "cell_type": "markdown",
119 40 "metadata": {},
120 41 "source": [
121 "Create a simple eventfull dictionary."
42 "NetworkX graphs do not have events that can be listened to. In order to watch the NetworkX graph object for changes a custom eventful graph object must be created. The custom eventful graph object will inherit from the base graph object and use special eventful dictionaries instead of standard Python dict instances. Because NetworkX nests dictionaries inside dictionaries, it's important that the eventful dictionary is capable of recognizing when a dictionary value is set to another dictionary instance. When this happens, the eventful dictionary needs to also make the new dictionary an eventful dictionary. This allows the eventful dictionary to listen to changes made to dictionaries within dictionaries."
122 43 ]
123 44 },
124 45 {
125 46 "cell_type": "code",
126 47 "collapsed": false,
127 48 "input": [
128 49 "class EventfulDict(dict):\n",
129 50 " \n",
130 51 " def __init__(self, *args, **kwargs):\n",
131 52 " self._add_callbacks = []\n",
132 53 " self._del_callbacks = []\n",
133 54 " self._set_callbacks = []\n",
134 55 " dict.__init__(self, *args, **kwargs)\n",
135 56 " \n",
136 57 " def on_add(self, callback, remove=False):\n",
137 58 " self._register_callback(self._add_callbacks, callback, remove)\n",
138 59 " def on_del(self, callback, remove=False):\n",
139 60 " self._register_callback(self._del_callbacks, callback, remove)\n",
140 61 " def on_set(self, callback, remove=False):\n",
141 62 " self._register_callback(self._set_callbacks, callback, remove)\n",
142 63 " def _register_callback(self, callback_list, callback, remove=False):\n",
143 64 " if callable(callback):\n",
144 65 " if remove and callback in callback_list:\n",
145 66 " callback_list.remove(callback)\n",
146 67 " elif not remove and not callback in callback_list:\n",
147 68 " callback_list.append(callback)\n",
148 69 " else:\n",
149 70 " raise Exception('Callback must be callable.')\n",
150 71 "\n",
151 72 " def _handle_add(self, key, value):\n",
152 73 " self._try_callbacks(self._add_callbacks, key, value)\n",
153 74 " def _handle_del(self, key):\n",
154 75 " self._try_callbacks(self._del_callbacks, key)\n",
155 76 " def _handle_set(self, key, value):\n",
156 77 " self._try_callbacks(self._set_callbacks, key, value)\n",
157 78 " def _try_callbacks(self, callback_list, *pargs, **kwargs):\n",
158 79 " for callback in callback_list:\n",
159 80 " callback(*pargs, **kwargs)\n",
160 81 " \n",
161 82 " def __setitem__(self, key, value):\n",
162 83 " return_val = None\n",
163 84 " exists = False\n",
164 85 " if key in self:\n",
165 86 " exists = True\n",
166 87 " \n",
167 88 " # If the user sets the property to a new dict, make the dict\n",
168 89 " # eventful and listen to the changes of it ONLY if it is not\n",
169 90 " # already eventful. Any modification to this new dict will\n",
170 91 " # fire a set event of the parent dict.\n",
171 92 " if isinstance(value, dict) and not isinstance(value, EventfulDict):\n",
172 93 " new_dict = EventfulDict(value)\n",
173 94 " \n",
174 95 " def handle_change(*pargs, **kwargs):\n",
175 96 " self._try_callbacks(self._set_callbacks, key, dict.__getitem__(self, key))\n",
176 97 " \n",
177 98 " new_dict.on_add(handle_change)\n",
178 99 " new_dict.on_del(handle_change)\n",
179 100 " new_dict.on_set(handle_change)\n",
180 101 " return_val = dict.__setitem__(self, key, new_dict)\n",
181 102 " else:\n",
182 103 " return_val = dict.__setitem__(self, key, value)\n",
183 104 " \n",
184 105 " if exists:\n",
185 106 " self._handle_set(key, value)\n",
186 107 " else:\n",
187 108 " self._handle_add(key, value)\n",
188 109 " return return_val\n",
189 110 " \n",
190 111 "\n",
191 112 " def __delitem__(self, key):\n",
192 113 " return_val = dict.__delitem__(self, key)\n",
193 114 " self._handle_del(key)\n",
194 115 " return return_val\n",
195 116 "\n",
196 117 " \n",
197 118 " def pop(self, key):\n",
198 119 " return_val = dict.pop(self, key)\n",
199 120 " if key in self:\n",
200 121 " self._handle_del(key)\n",
201 122 " return return_val\n",
202 123 "\n",
203 124 " def popitem(self):\n",
204 125 " popped = dict.popitem(self)\n",
205 126 " if popped is not None and popped[0] is not None:\n",
206 127 " self._handle_del(popped[0])\n",
207 128 " return popped\n",
208 129 "\n",
209 130 " def update(self, other_dict):\n",
210 131 " for (key, value) in other_dict.items():\n",
211 132 " self[key] = value\n",
212 133 " \n",
213 134 " def clear(self):\n",
214 135 " for key in list(self.keys()):\n",
215 " del self[key]\n"
136 " del self[key]"
216 137 ],
217 138 "language": "python",
218 139 "metadata": {},
219 140 "outputs": [],
220 "prompt_number": 6
221 },
222 {
223 "cell_type": "markdown",
224 "metadata": {},
225 "source": [
226 "Test the eventful dictionary."
227 ]
228 },
229 {
230 "cell_type": "code",
231 "collapsed": false,
232 "input": [
233 "a = EventfulDict()"
234 ],
235 "language": "python",
236 "metadata": {},
237 "outputs": [],
238 "prompt_number": 7
239 },
240 {
241 "cell_type": "code",
242 "collapsed": false,
243 "input": [
244 "def echo_dict_events(eventful_dict, prefix=''):\n",
245 " def key_add(key, value):\n",
246 " print(prefix + 'add (%s, %s)' % (key, str(value)))\n",
247 " def key_set(key, value):\n",
248 " print(prefix + 'set (%s, %s)' % (key, str(value)))\n",
249 " def key_del(key):\n",
250 " print(prefix + 'del %s' % key)\n",
251 " eventful_dict.on_add(key_add)\n",
252 " eventful_dict.on_set(key_set)\n",
253 " eventful_dict.on_del(key_del)\n",
254 " \n",
255 "echo_dict_events(a)"
256 ],
257 "language": "python",
258 "metadata": {},
259 "outputs": [],
260 "prompt_number": 8
261 },
262 {
263 "cell_type": "code",
264 "collapsed": false,
265 "input": [
266 "a['a'] = 'hello'"
267 ],
268 "language": "python",
269 "metadata": {},
270 "outputs": [
271 {
272 "output_type": "stream",
273 "stream": "stdout",
274 "text": [
275 "add (a, hello)\n"
276 ]
277 }
278 ],
279 "prompt_number": 9
280 },
281 {
282 "cell_type": "code",
283 "collapsed": false,
284 "input": [
285 "a['a'] = 'goodbye'"
286 ],
287 "language": "python",
288 "metadata": {},
289 "outputs": [
290 {
291 "output_type": "stream",
292 "stream": "stdout",
293 "text": [
294 "set (a, goodbye)\n"
295 ]
296 }
297 ],
298 "prompt_number": 10
299 },
300 {
301 "cell_type": "code",
302 "collapsed": false,
303 "input": [
304 "b = {'c': 'yay', 'd': 'no'}\n",
305 "a.update(b)"
306 ],
307 "language": "python",
308 "metadata": {},
309 "outputs": [
310 {
311 "output_type": "stream",
312 "stream": "stdout",
313 "text": [
314 "add (c, yay)\n",
315 "add (d, no)\n"
316 ]
317 }
318 ],
319 "prompt_number": 11
320 },
321 {
322 "cell_type": "code",
323 "collapsed": false,
324 "input": [
325 "a"
326 ],
327 "language": "python",
328 "metadata": {},
329 "outputs": [
330 {
331 "metadata": {},
332 "output_type": "pyout",
333 "prompt_number": 12,
334 "text": [
335 "{'a': 'goodbye', 'c': 'yay', 'd': 'no'}"
336 ]
337 }
338 ],
339 "prompt_number": 12
340 },
341 {
342 "cell_type": "code",
343 "collapsed": false,
344 "input": [
345 "a.pop('a')"
346 ],
347 "language": "python",
348 "metadata": {},
349 "outputs": [
350 {
351 "metadata": {},
352 "output_type": "pyout",
353 "prompt_number": 13,
354 "text": [
355 "'goodbye'"
356 ]
357 }
358 ],
359 "prompt_number": 13
360 },
361 {
362 "cell_type": "code",
363 "collapsed": false,
364 "input": [
365 "a.popitem()"
366 ],
367 "language": "python",
368 "metadata": {},
369 "outputs": [
370 {
371 "output_type": "stream",
372 "stream": "stdout",
373 "text": [
374 "del c\n"
375 ]
376 },
377 {
378 "metadata": {},
379 "output_type": "pyout",
380 "prompt_number": 14,
381 "text": [
382 "('c', 'yay')"
383 ]
384 }
385 ],
386 "prompt_number": 14
387 },
388 {
389 "cell_type": "code",
390 "collapsed": false,
391 "input": [
392 "a['e'] = {}"
393 ],
394 "language": "python",
395 "metadata": {},
396 "outputs": [
397 {
398 "output_type": "stream",
399 "stream": "stdout",
400 "text": [
401 "add (e, {})\n"
402 ]
403 }
404 ],
405 "prompt_number": 15
406 },
407 {
408 "cell_type": "code",
409 "collapsed": false,
410 "input": [
411 "a['e']['a'] = 0"
412 ],
413 "language": "python",
414 "metadata": {},
415 "outputs": [
416 {
417 "output_type": "stream",
418 "stream": "stdout",
419 "text": [
420 "set (e, {'a': 0})\n"
421 ]
422 }
423 ],
424 "prompt_number": 16
425 },
426 {
427 "cell_type": "code",
428 "collapsed": false,
429 "input": [
430 "a['e']['b'] = 1"
431 ],
432 "language": "python",
433 "metadata": {},
434 "outputs": [
435 {
436 "output_type": "stream",
437 "stream": "stdout",
438 "text": [
439 "set (e, {'a': 0, 'b': 1})\n"
440 ]
441 }
442 ],
443 "prompt_number": 17
444 },
445 {
446 "cell_type": "code",
447 "collapsed": false,
448 "input": [
449 "a.clear()"
450 ],
451 "language": "python",
452 "metadata": {},
453 "outputs": [
454 {
455 "output_type": "stream",
456 "stream": "stdout",
457 "text": [
458 "del e\n",
459 "del d\n"
460 ]
461 }
462 ],
463 "prompt_number": 18
141 "prompt_number": 5
464 142 },
465 143 {
466 144 "cell_type": "markdown",
467 145 "metadata": {},
468 146 "source": [
469 147 "Override the NetworkX Graph object to make an eventful graph."
470 148 ]
471 149 },
472 150 {
473 151 "cell_type": "code",
474 152 "collapsed": false,
475 153 "input": [
476 154 "class EventfulGraph(nx.Graph):\n",
477 155 " \n",
478 156 " def __init__(self, *pargs, **kwargs):\n",
479 157 " \"\"\"Initialize a graph with edges, name, graph attributes.\"\"\"\n",
480 158 " super(EventfulGraph, self).__init__(*pargs, **kwargs)\n",
481 159 " \n",
482 160 " self.graph = EventfulDict(self.graph)\n",
483 161 " self.node = EventfulDict(self.node)\n",
484 162 " self.adj = EventfulDict(self.adj)\n",
485 163 " "
486 164 ],
487 165 "language": "python",
488 166 "metadata": {},
489 167 "outputs": [],
490 "prompt_number": 19
168 "prompt_number": 6
169 },
170 {
171 "cell_type": "markdown",
172 "metadata": {},
173 "source": [
174 "To make sure that the eventful graph works, create a new graph and log the dictionary events raised."
175 ]
491 176 },
492 177 {
493 178 "cell_type": "code",
494 179 "collapsed": false,
495 180 "input": [
181 "def echo_dict_events(eventful_dict, prefix=''):\n",
182 " def key_add(key, value):\n",
183 " print(prefix + 'add (%s, %s)' % (key, str(value)))\n",
184 " def key_set(key, value):\n",
185 " print(prefix + 'set (%s, %s)' % (key, str(value)))\n",
186 " def key_del(key):\n",
187 " print(prefix + 'del %s' % key)\n",
188 " eventful_dict.on_add(key_add)\n",
189 " eventful_dict.on_set(key_set)\n",
190 " eventful_dict.on_del(key_del)\n",
191 " \n",
496 192 "def echo_graph_events(eventful_graph):\n",
497 193 " for key in ['graph', 'node', 'adj']:\n",
498 194 " echo_dict_events(getattr(eventful_graph, key), prefix=key+' ')"
499 195 ],
500 196 "language": "python",
501 197 "metadata": {},
502 198 "outputs": [],
503 "prompt_number": 20
199 "prompt_number": 7
504 200 },
505 201 {
506 202 "cell_type": "code",
507 203 "collapsed": false,
508 204 "input": [
509 205 "G = EventfulGraph()\n",
510 "echo_graph_events(G)"
511 ],
512 "language": "python",
513 "metadata": {},
514 "outputs": [],
515 "prompt_number": 21
516 },
517 {
518 "cell_type": "code",
519 "collapsed": false,
520 "input": [
521 "to_d3_json(G)"
522 ],
523 "language": "python",
524 "metadata": {},
525 "outputs": [
526 {
527 "metadata": {},
528 "output_type": "pyout",
529 "prompt_number": 22,
530 "text": [
531 "'{\"multigraph\": false, \"directed\": false, \"links\": [], \"graph\": [], \"nodes\": []}'"
532 ]
533 }
534 ],
535 "prompt_number": 22
536 },
537 {
538 "cell_type": "code",
539 "collapsed": false,
540 "input": [
206 "echo_graph_events(G)\n",
207 "\n",
541 208 "G.add_node('hello')\n",
542 209 "G.add_node('goodbye')\n",
543 "G.add_edges_from([(1,2),(1,3)])\n",
544 "to_d3_json(G)"
210 "G.add_edges_from([(1,2),(1,3)])"
545 211 ],
546 212 "language": "python",
547 213 "metadata": {},
548 214 "outputs": [
549 215 {
550 216 "output_type": "stream",
551 217 "stream": "stdout",
552 218 "text": [
553 219 "adj add (hello, {})\n",
554 220 "node add (hello, {})\n",
555 221 "adj add (goodbye, {})\n",
556 222 "node add (goodbye, {})\n",
557 223 "adj add (1, {})\n",
558 224 "node add (1, {})\n",
559 225 "adj add (2, {})\n",
560 226 "node add (2, {})\n",
561 227 "adj set (1, {2: {}})\n",
562 228 "adj set (2, {1: {}})\n",
563 229 "adj add (3, {})\n",
564 230 "node add (3, {})\n",
565 231 "adj set (1, {2: {}, 3: {}})\n",
566 232 "adj set (3, {1: {}})\n"
567 233 ]
568 },
569 {
570 "metadata": {},
571 "output_type": "pyout",
572 "prompt_number": 23,
573 "text": [
574 "'{\"multigraph\": false, \"directed\": false, \"links\": [{\"target\": 1, \"source\": 0}, {\"target\": 3, \"source\": 1}], \"graph\": [], \"nodes\": [{\"id\": 3}, {\"id\": 1}, {\"id\": \"goodbye\"}, {\"id\": 2}, {\"id\": \"hello\"}]}'"
575 ]
576 234 }
577 235 ],
578 "prompt_number": 23
236 "prompt_number": 8
579 237 },
580 238 {
581 239 "cell_type": "code",
582 240 "collapsed": false,
583 241 "input": [
584 242 "G.adj"
585 243 ],
586 244 "language": "python",
587 245 "metadata": {},
588 246 "outputs": [
589 247 {
590 248 "metadata": {},
591 249 "output_type": "pyout",
592 "prompt_number": 24,
250 "prompt_number": 9,
593 251 "text": [
594 "{3: {1: {}}, 1: {2: {}, 3: {}}, 'goodbye': {}, 2: {1: {}}, 'hello': {}}"
252 "{3: {1: {}}, 1: {2: {}, 3: {}}, 'hello': {}, 2: {1: {}}, 'goodbye': {}}"
595 253 ]
596 254 }
597 255 ],
598 "prompt_number": 24
256 "prompt_number": 9
599 257 },
600 258 {
601 259 "cell_type": "code",
602 260 "collapsed": false,
603 261 "input": [
604 262 "G.node"
605 263 ],
606 264 "language": "python",
607 265 "metadata": {},
608 266 "outputs": [
609 267 {
610 268 "metadata": {},
611 269 "output_type": "pyout",
612 "prompt_number": 25,
270 "prompt_number": 10,
613 271 "text": [
614 "{3: {}, 1: {}, 'goodbye': {}, 2: {}, 'hello': {}}"
272 "{3: {}, 1: {}, 'hello': {}, 2: {}, 'goodbye': {}}"
615 273 ]
616 274 }
617 275 ],
618 "prompt_number": 25
276 "prompt_number": 10
619 277 },
620 278 {
621 279 "cell_type": "heading",
622 280 "level": 1,
623 281 "metadata": {},
624 282 "source": [
625 "Custom Widget"
283 "D3 Widget"
626 284 ]
627 285 },
628 286 {
629 287 "cell_type": "code",
630 288 "collapsed": false,
631 289 "input": [
632 "%%html\n",
633 "<div id=\"d3loadindicator\" style=\"background: red; color: white;\"><center>Loading D3...<center></div>\n",
634 "<script>\n",
635 " $.getScript('http://d3js.org/d3.v3.min.js', function(){\n",
636 " $('#d3loadindicator')\n",
637 " .css('background', 'green')\n",
638 " .html('<center>D3 Loaded Successfully</center>');\n",
639 " });\n",
640 "</script>"
641 ],
642 "language": "python",
643 "metadata": {},
644 "outputs": [
645 {
646 "html": [
647 "<div id=\"d3loadindicator\" style=\"background: red; color: white;\"><center>Loading D3...<center></div>\n",
648 "<script>\n",
649 " $.getScript('http://d3js.org/d3.v3.min.js', function(){\n",
650 " $('#d3loadindicator')\n",
651 " .css('background', 'green')\n",
652 " .html('<center>D3 Loaded Successfully</center>');\n",
653 " });\n",
654 "</script>"
655 ],
656 "metadata": {},
657 "output_type": "display_data",
658 "text": [
659 "<IPython.core.display.HTML at 0x7fa801877d90>"
660 ]
661 }
662 ],
663 "prompt_number": 26
664 },
665 {
666 "cell_type": "code",
667 "collapsed": false,
668 "input": [
669 290 "from IPython.html import widgets # Widget definitions\n",
670 291 "from IPython.display import display # Used to display widgets in the notebook"
671 292 ],
672 293 "language": "python",
673 294 "metadata": {},
674 295 "outputs": [],
675 "prompt_number": 27
296 "prompt_number": 11
676 297 },
677 298 {
678 299 "cell_type": "code",
679 300 "collapsed": false,
680 301 "input": [
681 302 "# Import the base Widget class and the traitlets Unicode class.\n",
682 303 "from IPython.html.widgets import Widget\n",
683 304 "from IPython.utils.traitlets import Unicode\n",
684 305 "\n",
685 306 "# Define our ForceDirectedGraphWidget and its target model and default view.\n",
686 307 "class ForceDirectedGraphWidget(Widget):\n",
687 308 " target_name = Unicode('ForceDirectedGraphModel')\n",
688 309 " default_view_name = Unicode('D3ForceDirectedGraphView')\n",
689 310 " \n",
690 " _keys = ['initial_json']\n",
691 " initial_json = Unicode()\n",
692 " \n",
693 311 " def __init__(self, eventful_graph, *pargs, **kwargs):\n",
694 312 " Widget.__init__(self, *pargs, **kwargs)\n",
695 313 " \n",
696 314 " self._eventful_graph = eventful_graph\n",
697 315 " self._send_dict_changes(eventful_graph.graph, 'graph')\n",
698 316 " self._send_dict_changes(eventful_graph.node, 'node')\n",
699 317 " self._send_dict_changes(eventful_graph.adj, 'adj')\n",
700 318 " \n",
701 319 " \n",
702 320 " def _repr_widget_(self, *pargs, **kwargs):\n",
703 " self.initial_json = to_d3_json(self._eventful_graph)\n",
321 " \n",
322 " # Show the widget, then send the current state\n",
704 323 " Widget._repr_widget_(self, *pargs, **kwargs)\n",
324 " for (key, value) in self._eventful_graph.graph.items():\n",
325 " self.send({'dict': 'graph', 'action': 'add', 'key': key, 'value': value})\n",
326 " for (key, value) in self._eventful_graph.node.items():\n",
327 " self.send({'dict': 'node', 'action': 'add', 'key': key, 'value': value})\n",
328 " for (key, value) in self._eventful_graph.adj.items():\n",
329 " self.send({'dict': 'adj', 'action': 'add', 'key': key, 'value': value})\n",
705 330 " \n",
706 331 " \n",
707 332 " def _send_dict_changes(self, eventful_dict, dict_name):\n",
708 333 " def key_add(key, value):\n",
709 334 " self.send({'dict': dict_name, 'action': 'add', 'key': key, 'value': value})\n",
710 335 " def key_set(key, value):\n",
711 336 " self.send({'dict': dict_name, 'action': 'set', 'key': key, 'value': value})\n",
712 337 " def key_del(key):\n",
713 338 " self.send({'dict': dict_name, 'action': 'del', 'key': key})\n",
714 339 " eventful_dict.on_add(key_add)\n",
715 340 " eventful_dict.on_set(key_set)\n",
716 341 " eventful_dict.on_del(key_del)\n",
717 342 " "
718 343 ],
719 344 "language": "python",
720 345 "metadata": {},
721 346 "outputs": [],
722 "prompt_number": 28
347 "prompt_number": 20
723 348 },
724 349 {
725 350 "cell_type": "code",
726 351 "collapsed": false,
727 352 "input": [
728 353 "%%javascript\n",
729 354 "\n",
730 "require([\"notebook/js/widget\"], function(){\n",
355 "require([\"http://d3js.org/d3.v3.min.js\", \"notebook/js/widget\"], function(){\n",
731 356 " \n",
732 357 " // Define the ForceDirectedGraphModel and register it with the widget manager.\n",
733 358 " var ForceDirectedGraphModel = IPython.WidgetModel.extend({});\n",
734 359 " IPython.widget_manager.register_widget_model('ForceDirectedGraphModel', ForceDirectedGraphModel);\n",
735 360 " \n",
736 361 " // Define the D3ForceDirectedGraphView\n",
737 362 " var D3ForceDirectedGraphView = IPython.WidgetView.extend({\n",
738 363 " \n",
739 364 " render: function(){\n",
740 365 " this.guid = 'd3force' + IPython.utils.uuid();\n",
741 366 " this.setElement($('<div />', {id: this.guid}));\n",
742 367 " this.model.on_msg($.proxy(this.handle_msg, this));\n",
368 " this.has_drawn = false;\n",
743 369 " },\n",
744 370 " \n",
745 371 " add_node: function(id){\n",
746 372 " var index = this.find_node(id);\n",
747 373 " if (index == -1) {\n",
748 374 " var node = {id: id};\n",
749 375 " this.nodes.push(node);\n",
750 376 " return node;\n",
751 377 " } else {\n",
752 378 " return this.nodes[index];\n",
753 379 " }\n",
754 380 " },\n",
755 381 " \n",
756 382 " remove_node: function(id){\n",
757 383 " var found_index = this.find_node(id);\n",
758 384 " if (found_index>=0) {\n",
759 385 " this.nodes.splice(found_index, 1);\n",
760 386 " }\n",
761 387 " \n",
762 388 " clear_links(id);\n",
763 389 " },\n",
764 390 " \n",
765 391 " find_node: function(id){\n",
766 392 " var found_index = -1;\n",
767 393 " for (var index in this.nodes) {\n",
768 394 " if (this.nodes[index].id == id) {\n",
769 395 " found_index = index;\n",
770 396 " break;\n",
771 397 " }\n",
772 398 " }\n",
773 399 " return found_index;\n",
774 400 " },\n",
775 401 " \n",
776 402 " clear_links: function(id){\n",
777 403 " \n",
778 404 " // Remove existing links\n",
779 405 " var found_indexs = [];\n",
780 406 " for (var index in this.links) {\n",
781 407 " if (this.links[index].source.id == id) {\n",
782 408 " found_indexs.push(index);\n",
783 409 " }\n",
784 410 " }\n",
785 411 " \n",
786 412 " for (var index in found_indexs) {\n",
787 413 " this.links.splice(found_indexs[index], 1);\n",
788 414 " }\n",
789 415 " },\n",
790 416 " \n",
791 417 " handle_msg: function(content){\n",
792 418 " var dict = content.dict;\n",
793 419 " var action = content.action;\n",
794 420 " var key = content.key;\n",
795 421 " console.log(dict, action, key);\n",
796 422 " \n",
797 423 " if (dict=='node') {\n",
798 424 " \n",
799 425 " // Only support node ADD and DEL actions for now...\n",
800 426 " if (action=='add') {\n",
801 427 " this.add_node(key)\n",
802 428 " } else if (action=='del') {\n",
803 429 " this.remove_node(key);\n",
804 430 " }\n",
805 431 " \n",
806 432 " } else if (dict=='adj') {\n",
807 433 " this.clear_links(key);\n",
808 434 " \n",
809 435 " // Add all links\n",
810 436 " if (action != 'del') {\n",
811 437 " var value = content.value;\n",
812 438 " for (var link_to in value) {\n",
813 439 " var source_node = this.add_node(key);\n",
814 440 " var target_node = this.add_node(link_to);\n",
815 441 " this.links.push({source: source_node, target: target_node});\n",
816 442 " }\n",
817 443 " }\n",
818 444 " }\n",
819 445 " this.start();\n",
820 446 " },\n",
821 447 " \n",
822 448 " start: function() {\n",
823 449 " var node = this.svg.selectAll(\".node\"),\n",
824 450 " link = this.svg.selectAll(\".link\");\n",
825 451 " \n",
826 452 " var link = link.data(this.force.links(), function(d) { return d.source.id + \"-\" + d.target.id; });\n",
827 453 " link.enter()\n",
828 454 " .insert(\"line\", \".node\")\n",
829 455 " .attr(\"class\", \"link\")\n",
830 456 " .style(\"stroke-width\", '1.5px')\n",
831 457 " .style('stroke', '#999');\n",
832 458 " link.exit().remove();\n",
833 459 " \n",
834 460 " var node = node.data(this.force.nodes(), function(d) { return d.id;});\n",
835 461 " var that = this;\n",
836 462 " node.enter()\n",
837 463 " .append(\"circle\")\n",
838 464 " .attr(\"class\", function(d) { return \"node \" + d.id; })\n",
839 465 " .attr(\"r\", 8)\n",
840 466 " .style(\"fill\", function(d) { return that.color(d.group); })\n",
841 467 " .style(\"stroke\", \"#fff\")\n",
842 468 " .style(\"stroke-width\", \"1.5px\")\n",
843 469 " .call(this.force.drag);\n",
844 470 " node.exit().remove();\n",
845 471 " \n",
846 472 " this.force.start();\n",
847 473 " },\n",
848 474 " \n",
849 475 " tick: function() {\n",
850 476 " var node = this.svg.selectAll(\".node\"),\n",
851 477 " link = this.svg.selectAll(\".link\");\n",
852 478 " \n",
853 479 " link.attr(\"x1\", function(d) { return d.source.x; })\n",
854 480 " .attr(\"y1\", function(d) { return d.source.y; })\n",
855 481 " .attr(\"x2\", function(d) { return d.target.x; })\n",
856 482 " .attr(\"y2\", function(d) { return d.target.y; });\n",
857 483 " \n",
858 484 " node.attr(\"cx\", function(d) { return d.x; })\n",
859 485 " .attr(\"cy\", function(d) { return d.y; });\n",
860 486 " },\n",
861 487 " \n",
862 488 " update: function(){\n",
863 " var initial_json = this.model.get('initial_json');\n",
864 " if (this.initial_json != initial_json) {\n",
865 " this.initial_json = initial_json;\n",
489 " if (! this.has_drawn) {\n",
490 " this.has_drawn = true;\n",
866 491 " \n",
867 " var width = 860,\n",
868 " height = 400;\n",
492 " var width = 400,\n",
493 " height = 300;\n",
869 494 " \n",
870 495 " this.color = d3.scale.category20();\n",
871 496 " \n",
872 " var graph = JSON.parse(initial_json);\n",
873 497 " this.nodes = [];\n",
874 " $.extend(this.nodes, graph.nodes);\n",
875 498 " this.links = [];\n",
876 " $.extend(this.links, graph.links);\n",
877 499 " \n",
878 " var force = d3.layout.force()\n",
500 " this.force = d3.layout.force()\n",
879 501 " .nodes(this.nodes)\n",
880 502 " .links(this.links)\n",
881 503 " .charge(-120)\n",
882 504 " .linkDistance(30)\n",
883 505 " .size([width, height])\n",
884 506 " .on(\"tick\", $.proxy(this.tick, this));\n",
885 " this.force = force;\n",
886 507 " \n",
887 " var svg = d3.select(\"#\" + this.guid).append(\"svg\")\n",
508 " this.svg = d3.select(\"#\" + this.guid).append(\"svg\")\n",
888 509 " .attr(\"width\", width)\n",
889 510 " .attr(\"height\", height);\n",
890 " this.svg = svg;\n",
891 511 " \n",
892 512 " var that = this;\n",
893 513 " setTimeout(function() {\n",
894 514 " that.start();\n",
895 515 " }, 0);\n",
896 516 " }\n",
897 517 " \n",
898 518 " return IPython.WidgetView.prototype.update.call(this);\n",
899 519 " },\n",
900 520 " \n",
901 521 " });\n",
902 522 " \n",
903 523 " // Register the D3ForceDirectedGraphView with the widget manager.\n",
904 524 " IPython.widget_manager.register_widget_view('D3ForceDirectedGraphView', D3ForceDirectedGraphView);\n",
905 525 "});"
906 526 ],
907 527 "language": "python",
908 528 "metadata": {},
909 529 "outputs": [
910 530 {
911 531 "javascript": [
912 532 "\n",
913 "require([\"notebook/js/widget\"], function(){\n",
533 "require([\"http://d3js.org/d3.v3.min.js\", \"notebook/js/widget\"], function(){\n",
914 534 " \n",
915 535 " // Define the ForceDirectedGraphModel and register it with the widget manager.\n",
916 536 " var ForceDirectedGraphModel = IPython.WidgetModel.extend({});\n",
917 537 " IPython.widget_manager.register_widget_model('ForceDirectedGraphModel', ForceDirectedGraphModel);\n",
918 538 " \n",
919 539 " // Define the D3ForceDirectedGraphView\n",
920 540 " var D3ForceDirectedGraphView = IPython.WidgetView.extend({\n",
921 541 " \n",
922 542 " render: function(){\n",
923 543 " this.guid = 'd3force' + IPython.utils.uuid();\n",
924 544 " this.setElement($('<div />', {id: this.guid}));\n",
925 545 " this.model.on_msg($.proxy(this.handle_msg, this));\n",
546 " this.has_drawn = false;\n",
926 547 " },\n",
927 548 " \n",
928 549 " add_node: function(id){\n",
929 550 " var index = this.find_node(id);\n",
930 551 " if (index == -1) {\n",
931 552 " var node = {id: id};\n",
932 553 " this.nodes.push(node);\n",
933 554 " return node;\n",
934 555 " } else {\n",
935 556 " return this.nodes[index];\n",
936 557 " }\n",
937 558 " },\n",
938 559 " \n",
939 560 " remove_node: function(id){\n",
940 561 " var found_index = this.find_node(id);\n",
941 562 " if (found_index>=0) {\n",
942 563 " this.nodes.splice(found_index, 1);\n",
943 564 " }\n",
944 565 " \n",
945 566 " clear_links(id);\n",
946 567 " },\n",
947 568 " \n",
948 569 " find_node: function(id){\n",
949 570 " var found_index = -1;\n",
950 571 " for (var index in this.nodes) {\n",
951 572 " if (this.nodes[index].id == id) {\n",
952 573 " found_index = index;\n",
953 574 " break;\n",
954 575 " }\n",
955 576 " }\n",
956 577 " return found_index;\n",
957 578 " },\n",
958 579 " \n",
959 580 " clear_links: function(id){\n",
960 581 " \n",
961 582 " // Remove existing links\n",
962 583 " var found_indexs = [];\n",
963 584 " for (var index in this.links) {\n",
964 585 " if (this.links[index].source.id == id) {\n",
965 586 " found_indexs.push(index);\n",
966 587 " }\n",
967 588 " }\n",
968 589 " \n",
969 590 " for (var index in found_indexs) {\n",
970 591 " this.links.splice(found_indexs[index], 1);\n",
971 592 " }\n",
972 593 " },\n",
973 594 " \n",
974 595 " handle_msg: function(content){\n",
975 596 " var dict = content.dict;\n",
976 597 " var action = content.action;\n",
977 598 " var key = content.key;\n",
978 599 " console.log(dict, action, key);\n",
979 600 " \n",
980 601 " if (dict=='node') {\n",
981 602 " \n",
982 603 " // Only support node ADD and DEL actions for now...\n",
983 604 " if (action=='add') {\n",
984 605 " this.add_node(key)\n",
985 606 " } else if (action=='del') {\n",
986 607 " this.remove_node(key);\n",
987 608 " }\n",
988 609 " \n",
989 610 " } else if (dict=='adj') {\n",
990 611 " this.clear_links(key);\n",
991 612 " \n",
992 613 " // Add all links\n",
993 614 " if (action != 'del') {\n",
994 615 " var value = content.value;\n",
995 616 " for (var link_to in value) {\n",
996 617 " var source_node = this.add_node(key);\n",
997 618 " var target_node = this.add_node(link_to);\n",
998 619 " this.links.push({source: source_node, target: target_node});\n",
999 620 " }\n",
1000 621 " }\n",
1001 622 " }\n",
1002 623 " this.start();\n",
1003 624 " },\n",
1004 625 " \n",
1005 626 " start: function() {\n",
1006 627 " var node = this.svg.selectAll(\".node\"),\n",
1007 628 " link = this.svg.selectAll(\".link\");\n",
1008 629 " \n",
1009 630 " var link = link.data(this.force.links(), function(d) { return d.source.id + \"-\" + d.target.id; });\n",
1010 631 " link.enter()\n",
1011 632 " .insert(\"line\", \".node\")\n",
1012 633 " .attr(\"class\", \"link\")\n",
1013 634 " .style(\"stroke-width\", '1.5px')\n",
1014 635 " .style('stroke', '#999');\n",
1015 636 " link.exit().remove();\n",
1016 637 " \n",
1017 638 " var node = node.data(this.force.nodes(), function(d) { return d.id;});\n",
1018 639 " var that = this;\n",
1019 640 " node.enter()\n",
1020 641 " .append(\"circle\")\n",
1021 642 " .attr(\"class\", function(d) { return \"node \" + d.id; })\n",
1022 643 " .attr(\"r\", 8)\n",
1023 644 " .style(\"fill\", function(d) { return that.color(d.group); })\n",
1024 645 " .style(\"stroke\", \"#fff\")\n",
1025 646 " .style(\"stroke-width\", \"1.5px\")\n",
1026 647 " .call(this.force.drag);\n",
1027 648 " node.exit().remove();\n",
1028 649 " \n",
1029 650 " this.force.start();\n",
1030 651 " },\n",
1031 652 " \n",
1032 653 " tick: function() {\n",
1033 654 " var node = this.svg.selectAll(\".node\"),\n",
1034 655 " link = this.svg.selectAll(\".link\");\n",
1035 656 " \n",
1036 657 " link.attr(\"x1\", function(d) { return d.source.x; })\n",
1037 658 " .attr(\"y1\", function(d) { return d.source.y; })\n",
1038 659 " .attr(\"x2\", function(d) { return d.target.x; })\n",
1039 660 " .attr(\"y2\", function(d) { return d.target.y; });\n",
1040 661 " \n",
1041 662 " node.attr(\"cx\", function(d) { return d.x; })\n",
1042 663 " .attr(\"cy\", function(d) { return d.y; });\n",
1043 664 " },\n",
1044 665 " \n",
1045 666 " update: function(){\n",
1046 " var initial_json = this.model.get('initial_json');\n",
1047 " if (this.initial_json != initial_json) {\n",
1048 " this.initial_json = initial_json;\n",
667 " if (! this.has_drawn) {\n",
668 " this.has_drawn = true;\n",
1049 669 " \n",
1050 " var width = 860,\n",
1051 " height = 400;\n",
670 " var width = 400,\n",
671 " height = 300;\n",
1052 672 " \n",
1053 673 " this.color = d3.scale.category20();\n",
1054 674 " \n",
1055 " var graph = JSON.parse(initial_json);\n",
1056 675 " this.nodes = [];\n",
1057 " $.extend(this.nodes, graph.nodes);\n",
1058 676 " this.links = [];\n",
1059 " $.extend(this.links, graph.links);\n",
1060 677 " \n",
1061 " var force = d3.layout.force()\n",
678 " this.force = d3.layout.force()\n",
1062 679 " .nodes(this.nodes)\n",
1063 680 " .links(this.links)\n",
1064 681 " .charge(-120)\n",
1065 682 " .linkDistance(30)\n",
1066 683 " .size([width, height])\n",
1067 684 " .on(\"tick\", $.proxy(this.tick, this));\n",
1068 " this.force = force;\n",
1069 685 " \n",
1070 " var svg = d3.select(\"#\" + this.guid).append(\"svg\")\n",
686 " this.svg = d3.select(\"#\" + this.guid).append(\"svg\")\n",
1071 687 " .attr(\"width\", width)\n",
1072 688 " .attr(\"height\", height);\n",
1073 " this.svg = svg;\n",
1074 689 " \n",
1075 690 " var that = this;\n",
1076 691 " setTimeout(function() {\n",
1077 692 " that.start();\n",
1078 693 " }, 0);\n",
1079 694 " }\n",
1080 695 " \n",
1081 696 " return IPython.WidgetView.prototype.update.call(this);\n",
1082 697 " },\n",
1083 698 " \n",
1084 699 " });\n",
1085 700 " \n",
1086 701 " // Register the D3ForceDirectedGraphView with the widget manager.\n",
1087 702 " IPython.widget_manager.register_widget_view('D3ForceDirectedGraphView', D3ForceDirectedGraphView);\n",
1088 703 "});"
1089 704 ],
1090 705 "metadata": {},
1091 706 "output_type": "display_data",
1092 707 "text": [
1093 "<IPython.core.display.Javascript at 0x7fa809a360d0>"
708 "<IPython.core.display.Javascript at 0x7fdee9b02590>"
1094 709 ]
1095 710 }
1096 711 ],
1097 "prompt_number": 29
712 "prompt_number": 28
713 },
714 {
715 "cell_type": "heading",
716 "level": 1,
717 "metadata": {},
718 "source": [
719 "Test"
720 ]
1098 721 },
1099 722 {
1100 723 "cell_type": "code",
1101 724 "collapsed": false,
1102 725 "input": [
1103 726 "G = EventfulGraph()\n",
1104 727 "G.add_node('hello')\n",
1105 728 "G.add_node('goodbye')\n",
1106 "G.add_edges_from([(1,2),(1,3)])\n",
1107 "to_d3_json(G)"
729 "G.add_edges_from([(1,2),(1,3)])"
1108 730 ],
1109 731 "language": "python",
1110 732 "metadata": {},
1111 "outputs": [
1112 {
1113 "metadata": {},
1114 "output_type": "pyout",
1115 "prompt_number": 36,
1116 "text": [
1117 "'{\"multigraph\": false, \"directed\": false, \"links\": [{\"target\": 1, \"source\": 0}, {\"target\": 3, \"source\": 1}], \"graph\": [], \"nodes\": [{\"id\": 3}, {\"id\": 1}, {\"id\": \"goodbye\"}, {\"id\": 2}, {\"id\": \"hello\"}]}'"
1118 ]
1119 }
1120 ],
1121 "prompt_number": 36
733 "outputs": [],
734 "prompt_number": 29
1122 735 },
1123 736 {
1124 737 "cell_type": "code",
1125 738 "collapsed": false,
1126 739 "input": [
1127 "floating_container = widgets.ContainerWidget()\n",
1128 "floating_container.set_css({\n",
1129 " 'position': 'relative',\n",
1130 " 'left': '0px',\n",
1131 " 'top': '0px',\n",
1132 " 'z-index': '999',\n",
1133 " 'background': '#FFF',\n",
1134 " 'opacity': '0.8'\n",
1135 "})\n",
740 "floating_container = widgets.ContainerWidget(default_view_name='ModalView')\n",
1136 741 "\n",
1137 742 "d3 = ForceDirectedGraphWidget(G, parent=floating_container)\n",
1138 "display(floating_container)\n",
1139 "\n",
1140 "detach_button = widgets.ButtonWidget(description=\"Detach\")\n",
1141 "def handle_detach(sender):\n",
1142 " if sender.description == \"Detach\":\n",
1143 " sender.description = \"Attach\"\n",
1144 " floating_container.set_css('position', 'absolute')\n",
1145 " else:\n",
1146 " sender.description = \"Detach\"\n",
1147 " floating_container.set_css('position', 'relative')\n",
1148 "detach_button.on_click(handle_detach)\n",
1149 "display(detach_button)"
743 "display(floating_container)"
1150 744 ],
1151 745 "language": "python",
1152 746 "metadata": {},
1153 "outputs": [],
1154 "prompt_number": 37
747 "outputs": []
1155 748 },
1156 749 {
1157 750 "cell_type": "code",
1158 751 "collapsed": false,
1159 752 "input": [
1160 753 "G.add_node('beep')\n",
1161 754 "G.add_node('beep2')"
1162 755 ],
1163 756 "language": "python",
1164 757 "metadata": {},
1165 758 "outputs": [],
1166 "prompt_number": 38
759 "prompt_number": 25
1167 760 },
1168 761 {
1169 762 "cell_type": "code",
1170 763 "collapsed": false,
1171 764 "input": [
1172 765 "G.remove_node('beep')"
1173 766 ],
1174 767 "language": "python",
1175 768 "metadata": {},
1176 769 "outputs": [],
1177 "prompt_number": 39
770 "prompt_number": 26
1178 771 },
1179 772 {
1180 773 "cell_type": "code",
1181 774 "collapsed": false,
1182 775 "input": [
1183 776 "\n",
1184 777 "G.add_edges_from([(4,5),(4,6)])"
1185 778 ],
1186 779 "language": "python",
1187 780 "metadata": {},
1188 781 "outputs": [],
1189 "prompt_number": 40
782 "prompt_number": 27
1190 783 },
1191 784 {
1192 785 "cell_type": "code",
1193 786 "collapsed": false,
1194 787 "input": [
1195 788 "import time\n",
1196 789 "for i in range(30):\n",
1197 790 " G.add_edges_from([(7+i, 8+i), (7+i, 9+i)])\n",
1198 791 " time.sleep(0.1)"
1199 792 ],
1200 793 "language": "python",
1201 794 "metadata": {},
1202 795 "outputs": [],
1203 796 "prompt_number": 41
1204 797 },
1205 798 {
1206 799 "cell_type": "code",
1207 800 "collapsed": false,
1208 801 "input": [
1209 802 "\n",
1210 803 "for i in range(30):\n",
1211 804 " G.add_edge(7, i+8)\n",
1212 805 " time.sleep(0.25)"
1213 806 ],
1214 807 "language": "python",
1215 808 "metadata": {},
1216 809 "outputs": [],
1217 810 "prompt_number": 42
1218 811 },
1219 812 {
1220 813 "cell_type": "code",
1221 814 "collapsed": false,
1222 815 "input": [
1223 816 "G.clear()"
1224 817 ],
1225 818 "language": "python",
1226 819 "metadata": {},
1227 820 "outputs": []
1228 821 }
1229 822 ],
1230 823 "metadata": {}
1231 824 }
1232 825 ]
1233 826 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now