1from ipywidgets import widgets
2from traitlets import Unicode, Bool
3
4
5# Register front end javascript
6from IPython import display
7from . import drawing_javascript
8display.display(display.Javascript(drawing_javascript.javascript))
9del drawing_javascript
10
11
12class DrawingWidget(widgets.DOMWidget):
13 _view_name = Unicode('DrawingView').tag(sync=True)
14 _view_module = Unicode('drawingview').tag(sync=True)
15 _view_module_version = Unicode('0.1.0').tag(sync=True)
16 _image = Unicode().tag(sync=True)
17 _mousemove_blocked = Bool(False).tag(sync=True)
18 throttle = Bool(True).tag(sync=True)
19 disable = Bool(False).tag(sync=True)
20
21 def __init__(self, drawing, throttle=True, disable=False):
22 '''
23 DrawingWidget is an interactive Jupyter notebook widget. It works
24 similarly to displaying a Drawing as a cell output but DrawingWidget
25 can register callbacks for user mouse events. Within a callback modify
26 the drawing then call .refresh() to update the output in real time.
27
28 Arguments:
29 drawing: The initial Drawing to display. Call .refresh() after
30 modifying or just assign a new Drawing.
31 throttle: If True, limit the rate of mousemove events. For drawings
32 with many elements, this will significantly reduce lag.
33 disable: While True, mouse events will be disabled.
34 '''
35 super().__init__()
36 self.throttle = throttle
37 self.disable = disable
38 self.drawing = drawing
39 self.mousedown_callbacks = []
40 self.mousemove_callbacks = []
41 self.mouseup_callbacks = []
42
43 self.on_msg(self._receive_msg)
44
45 @property
46 def drawing(self):
47 return self._drawing
48
49 @drawing.setter
50 def drawing(self, drawing):
51 self._drawing = drawing
52 self.refresh()
53
54 def refresh(self):
55 '''
56 Redraw the displayed output with the current value of self.drawing.
57 '''
58 self._image = self.drawing.asSvg()
59
60 def _receive_msg(self, _, content, buffers):
61 if not isinstance(content, dict):
62 return
63 callbacks = {
64 'mousedown': self.mousedown_callbacks,
65 'mousemove': self.mousemove_callbacks,
66 'mouseup': self.mouseup_callbacks,
67 }.get(content.get('name'), ())
68 try:
69 if callbacks:
70 self._call_handlers(callbacks, content.get('x'),
71 content.get('y'), content)
72 finally:
73 self._mousemove_blocked = False
74
75
76 def mousedown(self, handler, remove=False):
77 '''
78 Register (or unregister) a handler for the mousedown event.
79
80 Arguments:
81 remove: If True, unregister, otherwise register.
82 '''
83 self.on_msg
84 self._register_handler(
85 self.mousedown_callbacks, handler, remove=remove)
86
87 def mousemove(self, handler, remove=False):
88 '''
89 Register (or unregister) a handler for the mousemove event.
90
91 Arguments:
92 remove: If True, unregister, otherwise register.
93 '''
94 self._register_handler(
95 self.mousemove_callbacks, handler, remove=remove)
96
97 def mouseup(self, handler, remove=False):
98 '''
99 Register (or unregister) a handler for the mouseup event.
100
101 Arguments:
102 remove: If True, unregister, otherwise register.
103 '''
104 self._register_handler(
105 self.mouseup_callbacks, handler, remove=remove)
106
107 def _register_handler(self, callback_list, handler, remove=False):
108 if remove:
109 callback_list.remove(handler)
110 else:
111 callback_list.append(handler)
112
113 def _call_handlers(self, callback_list, *args, **kwargs):
114 for callback in callback_list:
115 callback(self, *args, **kwargs)