1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
import base64
from IPython.display import display
from ipywidgets import Image
from ipyevents import Event
from .bridge import LocalBridge
from .remote_scene import SceneManager
from .remote_widget import RemoteWidget
from ..figure import gcf
def base64_to_bytes(str_or_bytes):
data = str_or_bytes.encode('ascii')
return base64.decodebytes(data)
class IPyRemoteWidget(RemoteWidget):
def __init__(self, scene_proxy, bridge, *args, **kw):
super(IPyRemoteWidget, self).__init__(scene_proxy, bridge, *args, **kw)
self.image = Image(format='PNG')
self.event = Event(
source=self.image,
watched_events=[
'dragstart', 'mouseenter', 'mouseleave',
'mousedown', 'mouseup', 'mousemove', 'wheel',
'keyup', 'keydown'
],
prevent_default_action=True
)
self.event.on_dom_event(self.handle_ipyevent)
self._update_image()
def _ipython_display_(self):
display(self.image)
# ###### Public protocol ##############
def show(self):
pass
def show_image(self, data, format='PNG'):
self.image.format = format
self.image.value = data
# ##### VTK Event handling ##########
def on_render(self, data):
self.show_image(base64_to_bytes(data['data']),
format=data.get('format', 'PNG'))
def on_cursor_changed(self, data):
# self.setCursor(cursor)
pass
def handle_ipyevent(self, event):
type = event['type']
shift = event.get('shiftKey', False)
ctrl = event.get('ctrlKey', False)
btn_map = {0: 'left', 1: 'right', 2: 'middle'}
if type == 'mouseenter':
self.on_enter(ctrl, shift)
elif type == 'mouseleave':
self.on_leave(ctrl, shift)
elif type == 'mouseleave':
self.on_leave(ctrl, shift)
elif type == 'mousedown':
repeat = 0
button = btn_map.get(event.get('button'), 'none')
x, y = event.get('relativeX'), event.get('relativeY')
self.on_mouse_press(ctrl, shift, x, y, button, repeat)
elif type == 'mouseup':
x, y = event.get('relativeX'), event.get('relativeY')
self.on_mouse_release(ctrl, shift, x, y)
elif type == 'mousemove':
button = btn_map.get(event.get('button'), 'none')
x, y = event.get('relativeX'), event.get('relativeY')
self.on_mouse_move(ctrl, shift, button, x, y)
elif type == 'wheel':
dx = event.get('deltaX')
dy = event.get('deltaY')
dz = event.get('deltaZ')
delta = dx + dy + dz
self.on_wheel(delta)
elif type == 'keydown':
key = event.get('key')
# Need to map the special keys correctly.
key_sym = key
self.on_key_press(ctrl, shift, key, key_sym)
elif type == 'keyup':
key = event.get('key')
# Need to map the special keys correctly.
key_sym = key
self.on_key_release(ctrl, shift, key)
class WidgetManager(object):
def __init__(self):
self.sm = SceneManager()
# FIXME: we need a way to set the SceneManager.call_later.
# not sure what sort of event loop we can rely on with IPython.
self.bridge = LocalBridge(self.sm)
self.bridge.run()
self.widgets = {}
def scene_to_ipy(self, scene):
figure = gcf()
sm = self.sm
sid = sm.figure_to_id.get(figure)
if sid is None:
sid = sm.register_figure(figure)
if sid in self.widgets:
return self.widgets[sid]
else:
scene = sm.scenes[sid]
w = IPyRemoteWidget(scene, self.bridge)
self.widgets[sid] = w
return w
|