File: _ipynb_static.py

package info (click to toggle)
python-vispy 0.6.6-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 21,344 kB
  • sloc: python: 57,412; javascript: 6,810; makefile: 63; sh: 5
file content (208 lines) | stat: -rw-r--r-- 6,842 bytes parent folder | download | duplicates (2)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# -*- coding: utf-8 -*-
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.

"""
vispy backend for the IPython notebook (static approach).

We aim to have:
* ipynb_static - export visualization to a static notebook
* ipynb_vnc - vnc-approach: render in Python, send result to JS as png
* ipynb_webgl - send gl commands to JS and execute in webgl context

"""

from __future__ import division

from ..base import BaseApplicationBackend, BaseCanvasBackend
from .. import Application, Canvas
from ...util import logger

# Imports for screenshot
from ...gloo.util import _screenshot
from ...io import _make_png
from base64 import b64encode

# -------------------------------------------------------------------- init ---

capability = dict(  # things that can be set by the backend
    title=True,  # But it only applies to the dummy window :P
    size=True,  # We cannot possibly say we dont, because Canvas always sets it
    position=True,  # Dito
    show=True,  # Note: we don't alow this, but all scripts call show ...
    vsync=False,
    resizable=True,  # Yes, you can set to not be resizable (it always is)
    decorate=False,
    fullscreen=False,
    context=True,
    multi_window=True,
    scroll=True,
    parent=False,
    always_on_top=False,
)


def _set_config(c):
    _app.backend_module._set_config(c)


# Create our "backend" backend; The toolkit that is going to provide a
# canvas (e.g. OpenGL context) so we can render images.
# Note that if IPython has already loaded a GUI backend, vispy is
# probably going to use that as well, because it prefers loaded backends.
try:
    # Explicitly use default (avoid using test-app)
    _app = Application('default')
except Exception:
    _msg = 'ipynb_static backend relies on a core backend'
    available, testable, why_not, which = False, False, _msg, None
else:
    # Try importing IPython
    try:
        from IPython.display import display_png
    except Exception as exp:
        available, testable, why_not, which = False, False, str(exp), None
    else:
        available, testable, why_not = True, False, None
        which = _app.backend_module.which

    # Use that backend's shared context
    KEYMAP = _app.backend_module.KEYMAP


# ------------------------------------------------------------- application ---

# todo: maybe trigger something in JS on any of these methods?
class ApplicationBackend(BaseApplicationBackend):

    def __init__(self):
        BaseApplicationBackend.__init__(self)
        self._backend2 = _app._backend

    def _vispy_get_backend_name(self):
        realname = self._backend2._vispy_get_backend_name()
        return 'ipynb_static (via %s)' % realname

    def _vispy_process_events(self):
        return self._backend2._vispy_process_events()

    def _vispy_run(self):
        pass  # We run in IPython, so we don't run!
        #return self._backend2._vispy_run()

    def _vispy_quit(self):
        return self._backend2._vispy_quit()

    def _vispy_get_native_app(self):
        return self._backend2._vispy_get_native_app()


# ------------------------------------------------------------------ canvas ---

class CanvasBackend(BaseCanvasBackend):

    # args are for BaseCanvasBackend, kwargs are for us.
    def __init__(self, *args, **kwargs):
        BaseCanvasBackend.__init__(self, *args)
        self._initialized = False

        # Test kwargs
#         if kwargs['position']:
#             raise RuntimeError('ipynb_static Canvas is not positionable')
        if not kwargs['decorate']:
            raise RuntimeError('ipynb_static Canvas is not decoratable')
        if kwargs['vsync']:
            raise RuntimeError('ipynb_static Canvas does not support vsync')
        if kwargs['fullscreen']:
            raise RuntimeError('ipynb_static Canvas does not support '
                               'fullscreen')

        # Create real canvas. It is a backend to this backend
        kwargs.pop('vispy_canvas', None)
        kwargs['autoswap'] = False
        canvas = Canvas(app=_app, **kwargs)  # Pass kwargs to underlying canvas
        self._backend2 = canvas.native

        # Connect to events of canvas to keep up to date with size and draw
        canvas.events.draw.connect(self._on_draw)
        canvas.events.resize.connect(self._on_resize)
        
        # Show the widget
        canvas.show()
        # todo: hide that canvas

        # Raw PNG that will be displayed on canvas.show()
        self._im = ""

    def _vispy_warmup(self):
        return self._backend2._vispy_warmup()

    def _vispy_set_current(self):
        return self._backend2._vispy_set_current()

    def _vispy_swap_buffers(self):
        return self._backend2._vispy_swap_buffers()

    def _vispy_set_title(self, title):
        return self._backend2._vispy_set_title(title)
        #logger.warn('IPython notebook canvas has not title.')

    def _vispy_set_size(self, w, h):
        return self._backend2._vispy_set_size(w, h)

    def _vispy_set_position(self, x, y):
        logger.warn('IPython notebook canvas cannot be repositioned.')

    def _vispy_set_visible(self, visible):
        #self._backend2._vispy_set_visible(visible)
        if not visible:
            logger.warn('IPython notebook canvas cannot be hidden.')
        else:
            self._vispy_update()
            self._vispy_canvas.app.process_events()
            self._vispy_close()
            display_png(self._im, raw=True)

    def _vispy_update(self):
        return self._backend2._vispy_update()

    def _vispy_close(self):
        return self._backend2._vispy_close()
        # todo: can we close on IPython side?

    def _vispy_get_position(self):
        return 0, 0

    def _vispy_get_size(self):
        return self._backend2._vispy_get_size()

    def _on_resize(self, event=None):
        # Event handler that is called by the underlying canvas
        if self._vispy_canvas is None:
            return
        size = self._backend2._vispy_get_size()
        self._vispy_canvas.events.resize(size=size)

    def _on_draw(self, event=None):
        # Event handler that is called by the underlying canvas
        if self._vispy_canvas is None:
            return
        # Handle initialization
        if not self._initialized:
            self._initialized = True
            self._vispy_canvas.events.initialize()
            self._on_resize()
        # Normal behavior
        self._vispy_canvas.set_current()
        self._vispy_canvas.events.draw(region=None)

        # Generate base64 encoded PNG string
        self._gen_png()

    def _gen_png(self):
        # Take the screenshot
        screenshot = _screenshot()
        # Convert to PNG
        png = _make_png(screenshot)
        # Encode base64
        self._im = b64encode(png)