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": " |
|
|
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 |
" |
|
|
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] |
|
|
136 | " del self[key]" | |
|
216 | 137 | ], |
|
217 | 138 | "language": "python", |
|
218 | 139 | "metadata": {}, |
|
219 | 140 | "outputs": [], |
|
220 |
"prompt_number": |
|
|
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": |
|
|
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": |
|
|
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)]) |
|
|
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": |
|
|
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": |
|
|
250 | "prompt_number": 9, | |
|
593 | 251 | "text": [ |
|
594 |
"{3: {1: {}}, 1: {2: {}, 3: {}}, ' |
|
|
252 | "{3: {1: {}}, 1: {2: {}, 3: {}}, 'hello': {}, 2: {1: {}}, 'goodbye': {}}" | |
|
595 | 253 | ] |
|
596 | 254 | } |
|
597 | 255 | ], |
|
598 |
"prompt_number": |
|
|
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": |
|
|
270 | "prompt_number": 10, | |
|
613 | 271 | "text": [ |
|
614 |
"{3: {}, 1: {}, ' |
|
|
272 | "{3: {}, 1: {}, 'hello': {}, 2: {}, 'goodbye': {}}" | |
|
615 | 273 | ] |
|
616 | 274 | } |
|
617 | 275 | ], |
|
618 |
"prompt_number": |
|
|
276 | "prompt_number": 10 | |
|
619 | 277 | }, |
|
620 | 278 | { |
|
621 | 279 | "cell_type": "heading", |
|
622 | 280 | "level": 1, |
|
623 | 281 | "metadata": {}, |
|
624 | 282 | "source": [ |
|
625 |
" |
|
|
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": |
|
|
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": 2 |
|
|
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 |
" |
|
|
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 = |
|
|
868 |
" height = |
|
|
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 |
" |
|
|
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 |
" |
|
|
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 |
" |
|
|
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 = |
|
|
1051 |
" height = |
|
|
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 |
" |
|
|
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 |
" |
|
|
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 0x7f |
|
|
708 | "<IPython.core.display.Javascript at 0x7fdee9b02590>" | |
|
1094 | 709 | ] |
|
1095 | 710 | } |
|
1096 | 711 | ], |
|
1097 |
"prompt_number": 2 |
|
|
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)]) |
|
|
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) |
|
|
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": |
|
|
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": |
|
|
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": |
|
|
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