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