Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets
at 1.2.0 3.9 kB view raw
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)