Пример #1
0
class CommSocket(object):
    """
    A websocket for interactive communication between the plot in
    the browser and the server.

    In addition to the methods required by tornado, it is required to
    have two callback methods:

        - ``send_json(json_content)`` is called by matplotlib when
          it needs to send json to the browser.  `json_content` is
          a JSON tree (Python dictionary), and it is the responsibility
          of this implementation to encode it as a string to send over
          the socket.

        - ``send_binary(blob)`` is called to send binary image data
          to the browser.
    """
    supports_binary = False

    def __init__(self, manager):
        self.manager = manager
        self.uuid = uuid()
        #display(HTML("<div id='%s'></div>"%self.uuid))
        self.comm = Comm('matplotlib', data={'id': self.uuid})

    def open(self):
        # Register the websocket with the FigureManager.
        self.manager.add_web_socket(self)
        self.comm.on_msg(self.on_message)

    def on_close(self):
        # When the socket is closed, deregister the websocket with
        # the FigureManager.

        self.manager.remove_web_socket(self)
        self.comm.close()

    def send_json(self, content):
        self.comm.send({'data': json.dumps(content)})

    def send_binary(self, blob):
        data_uri = "data:image/png;base64,{0}".format(b64encode(blob))
        self.comm.send({'data': data_uri})

    def on_message(self, message):
        # The 'supports_binary' message is relevant to the
        # websocket itself.  The other messages get passed along
        # to matplotlib as-is.

        # Every message has a "type" and a "figure_id".
        message = json.loads(message['content']['data'])
        if message['type'] == 'supports_binary':
            self.supports_binary = message['value']
        else:
            self.manager.handle_json(message)
Пример #2
0
class CommSocket(object):
    """
    A websocket for interactive communication between the plot in
    the browser and the server.

    In addition to the methods required by tornado, it is required to
    have two callback methods:

        - ``send_json(json_content)`` is called by matplotlib when
          it needs to send json to the browser.  `json_content` is
          a JSON tree (Python dictionary), and it is the responsibility
          of this implementation to encode it as a string to send over
          the socket.

        - ``send_binary(blob)`` is called to send binary image data
          to the browser.
    """
    supports_binary = False

    def __init__(self, manager):
        self.manager = manager
        self.uuid = uuid()
        #display(HTML("<div id='%s'></div>"%self.uuid))
        self.comm = Comm('matplotlib', data={'id': self.uuid})

    def open(self):
        # Register the websocket with the FigureManager.
        self.manager.add_web_socket(self)
        self.comm.on_msg(self.on_message)

    def on_close(self):
        # When the socket is closed, deregister the websocket with
        # the FigureManager.

        self.manager.remove_web_socket(self)
        self.comm.close()

    def send_json(self, content):
        self.comm.send({'data': json.dumps(content)})

    def send_binary(self, blob):
        data_uri = "data:image/png;base64,{0}".format(b64encode(blob))
        self.comm.send({'data': data_uri})

    def on_message(self, message):
        # The 'supports_binary' message is relevant to the
        # websocket itself.  The other messages get passed along
        # to matplotlib as-is.

        # Every message has a "type" and a "figure_id".
        message = json.loads(message['content']['data'])
        if message['type'] == 'supports_binary':
            self.supports_binary = message['value']
        else:
            self.manager.handle_json(message)
Пример #3
0
class InteractiveGraphics(object):
    def __init__(self, g, events=None, renderer="sage"):
        self._g = g
        if events is None:
            events = {}
        self._events = events
        self.renderer=renderer

    def figure(self, **kwds):
        if isinstance(self._g, matplotlib.figure.Figure):
            return self._g

        options = dict()
        options.update(self._g.SHOW_OPTIONS)
        options.update(self._g._extra_kwds)
        options.update(kwds)
        options.pop('dpi'); options.pop('transparent'); options.pop('fig_tight')
        fig = self._g.matplotlib(**options)

        return fig

    def save(self, filename, **kwds):
        if isinstance(self._g, matplotlib.figure.Figure):
            self._g.savefig(filename)
        else:
            # When fig_tight=True (the default), the margins are very slightly different.
            # I don't know how to properly account for this yet (or even if it is possible),
            # since it only happens at figsize time -- do "a=plot(sin); a.save??".
            # So for interactive graphics, we just set this to false no matter what.
            kwds['fig_tight'] = False
            self._g.save(filename, **kwds)

    def show(self, **kwds):
        STORED_INTERACTIVE_GRAPHICS.append(self);
        if self.renderer=="sage":
            return self.show_sage(**kwds)
        elif self.renderer=="matplotlib":
            return self.show_matplotlib(**kwds)

    def show_matplotlib(self, **kwds):
        self.fig = self.figure(**kwds)
        CommFigure(self.fig)
        for k,v in self._events.items():
            self.fig.canvas.mpl_connect(k,v)

    def show_sage(self, **kwds):
        fig = self.figure(**kwds)
        from matplotlib.backends.backend_agg import FigureCanvasAgg
        canvas = FigureCanvasAgg(fig)
        fig.set_canvas(canvas)
        fig.tight_layout()  # critical, since sage does this -- if not, coords all wrong
        ax = fig.axes[0]
        # upper left data coordinates
        xmin, ymax = ax.transData.inverted().transform( fig.transFigure.transform((0,1)) )
        # lower right data coordinates
        xmax, ymin = ax.transData.inverted().transform( fig.transFigure.transform((1,0)) )

        def to_data_coords(p):
            # 0<=x,y<=1
            return ((xmax-xmin)*p[0] + xmin, (ymax-ymin)*(1-p[1]) + ymin)
        def on_msg(msg):
            data = msg['content']['data']
            x,y = data['x'], data['y']
            eventType = data['eventType']
            if eventType in self._events:
                self._events[eventType](to_data_coords([x,y]))
        file_id = uuid()
        if kwds.get('svg',False):
            filename = '%s.svg'%file_id
            del kwds['svg']
        else:
            filename = '%s.png'%file_id

        fig.savefig(filename)

        from comm import SageCellComm as Comm
        self.comm = Comm(data={"filename": filename}, target_name='graphicswidget')
        import sys
        sys._sage_.sent_files[filename] = os.path.getmtime(filename)

        self.comm.on_msg(on_msg)
Пример #4
0
class ThreeJS(object):
    def __init__(self, renderer=None, width=None, height=None,
                 frame=True, camera_distance=10.0, background=None, foreground=None, **ignored):
        """
        INPUT:

        - renderer -- None (automatic), 'canvas2d', or 'webgl'
        - width    -- None (automatic) or an integer
        - height   -- None (automatic) or an integer
        - frame    -- bool (default: True); draw a frame that includes every object.
        - camera_distance -- float (default: 10); default camera distance.
        - background -- None (transparent); otherwise a color such as 'black' or 'white'
        - foreground -- None (automatic = black if transparent; otherwise opposite of background);
           or a color; this is used for drawing the frame and axes labels.
        """
        self.id = uuid()
        self.comm = Comm(data={'renderer':renderer,
                                     'width':noneint(width),
                                     'height':noneint(height),
                                     'camera_distance':float(camera_distance),
                                     'background':background,
                                     'foreground':foreground
                                     }, target_name='threejs')
        self.comm.on_msg(self.on_msg)
        self._graphics = []

    def on_msg(self, msg):
        data = msg['content']['data']
        x,y = data['x'], data['y']
        print (x,y)
    def send(self, msg_type, data):
        d = {'msg_type': msg_type}
        d.update(data)
        self.comm.send(d)

    def lights(self, lights):
        self.send('lights', {'lights': [l.scenetree_json() for l in lights]})

    def add(self, graphics3d, **kwds):
        kwds = graphics3d._process_viewing_options(kwds)
        self._frame = kwds.get('frame',False)
        self._graphics.append(graphics3d)
        obj = graphics3d_to_jsonable(graphics3d)
        self.send('add', {'obj': obj,
                        'wireframe':jsonable(kwds.get('wireframe'))})
        self.set_frame(draw = self._frame)  # update the frame
    def render_scene(self, force=True):
        self.send('render', {'force':force})

    def add_text(self, pos, text, fontsize=18, fontface='Arial', sprite_alignment='topLeft'):
        self.send('add_text',
                   obj={'pos':[float(pos[0]), float(pos[1]), float(pos[2])],
                        'text':str(text),
                        'fontsize':int(fontsize),'fontface':str(fontface),
                        'sprite_alignment':str(sprite_alignment)})

    def animate(self, fps=None, stop=None, mouseover=True):
        self.send('animate', {'fps':noneint(fps), 'stop':stop, 'mouseover':mouseover})

    def set_frame(self, xmin=None, xmax=None, ymin=None, ymax=None, zmin=None, zmax=None, color=None, draw=True):
        if not self._graphics:
            xmin, xmax, ymin, ymax, zmin, zmax = -1,1,-1,1,-1,1
        else:
            b = self._graphics[0].bounding_box()
            xmin, xmax, ymin, ymax, zmin, zmax = b[0][0], b[1][0], b[0][1], b[1][1], b[0][2], b[1][2]
            for g in self._graphics[1:]:
                b = g.bounding_box()
                xmin, xmax, ymin, ymax, zmin, zmax = (
                      min(xmin,b[0][0]), max(b[1][0],xmax),
                      min(b[0][1],ymin), max(b[1][1],ymax),
                      min(b[0][2],zmin), max(b[1][2],zmax))

        self.send('set_frame', {
                      'xmin':float(xmin), 'xmax':float(xmax),
                      'ymin':float(ymin), 'ymax':float(ymax),
                      'zmin':float(zmin), 'zmax':float(zmax), 'color':color, 'draw':draw})
Пример #5
0
class ThreeJS(object):
    def __init__(self,
                 renderer=None,
                 width=None,
                 height=None,
                 frame=True,
                 camera_distance=10.0,
                 background=None,
                 foreground=None,
                 **ignored):
        """
        INPUT:

        - renderer -- None (automatic), 'canvas2d', or 'webgl'
        - width    -- None (automatic) or an integer
        - height   -- None (automatic) or an integer
        - frame    -- bool (default: True); draw a frame that includes every object.
        - camera_distance -- float (default: 10); default camera distance.
        - background -- None (transparent); otherwise a color such as 'black' or 'white'
        - foreground -- None (automatic = black if transparent; otherwise opposite of background);
           or a color; this is used for drawing the frame and axes labels.
        """
        self.id = uuid()
        self.comm = Comm(data={
            'renderer': renderer,
            'width': noneint(width),
            'height': noneint(height),
            'camera_distance': float(camera_distance),
            'background': background,
            'foreground': foreground
        },
                         target_name='threejs')
        self.comm.on_msg(self.on_msg)
        self._graphics = []

    def on_msg(self, msg):
        data = msg['content']['data']
        x, y = data['x'], data['y']
        print(x, y)

    def send(self, msg_type, data):
        d = {'msg_type': msg_type}
        d.update(data)
        self.comm.send(d)

    def lights(self, lights):
        self.send('lights', {'lights': [l.scenetree_json() for l in lights]})

    def add(self, graphics3d, **kwds):
        kwds = graphics3d._process_viewing_options(kwds)
        self._frame = kwds.get('frame', False)
        self._graphics.append(graphics3d)
        obj = graphics3d_to_jsonable(graphics3d)
        self.send('add', {
            'obj': obj,
            'wireframe': jsonable(kwds.get('wireframe'))
        })
        self.set_frame(draw=self._frame)  # update the frame

    def render_scene(self, force=True):
        self.send('render', {'force': force})

    def add_text(self,
                 pos,
                 text,
                 fontsize=18,
                 fontface='Arial',
                 sprite_alignment='topLeft'):
        self.send('add_text',
                  obj={
                      'pos': [float(pos[0]),
                              float(pos[1]),
                              float(pos[2])],
                      'text': str(text),
                      'fontsize': int(fontsize),
                      'fontface': str(fontface),
                      'sprite_alignment': str(sprite_alignment)
                  })

    def animate(self, fps=None, stop=None, mouseover=True):
        self.send('animate', {
            'fps': noneint(fps),
            'stop': stop,
            'mouseover': mouseover
        })

    def set_frame(self,
                  xmin=None,
                  xmax=None,
                  ymin=None,
                  ymax=None,
                  zmin=None,
                  zmax=None,
                  color=None,
                  draw=True):
        if not self._graphics:
            xmin, xmax, ymin, ymax, zmin, zmax = -1, 1, -1, 1, -1, 1
        else:
            b = self._graphics[0].bounding_box()
            xmin, xmax, ymin, ymax, zmin, zmax = b[0][0], b[1][0], b[0][1], b[
                1][1], b[0][2], b[1][2]
            for g in self._graphics[1:]:
                b = g.bounding_box()
                xmin, xmax, ymin, ymax, zmin, zmax = (min(xmin, b[0][0]),
                                                      max(b[1][0], xmax),
                                                      min(b[0][1], ymin),
                                                      max(b[1][1], ymax),
                                                      min(b[0][2], zmin),
                                                      max(b[1][2], zmax))

        self.send(
            'set_frame', {
                'xmin': float(xmin),
                'xmax': float(xmax),
                'ymin': float(ymin),
                'ymax': float(ymax),
                'zmin': float(zmin),
                'zmax': float(zmax),
                'color': color,
                'draw': draw
            })
Пример #6
0
class InteractiveGraphics(object):
    def __init__(self, g, events=None, renderer="sage"):
        self._g = g
        if events is None:
            events = {}
        self._events = events
        self.renderer = renderer

    def figure(self, **kwds):
        if isinstance(self._g, matplotlib.figure.Figure):
            return self._g

        options = dict()
        options.update(self._g.SHOW_OPTIONS)
        options.update(self._g._extra_kwds)
        options.update(kwds)
        options.pop('dpi')
        options.pop('transparent')
        options.pop('fig_tight')
        fig = self._g.matplotlib(**options)

        return fig

    def save(self, filename, **kwds):
        if isinstance(self._g, matplotlib.figure.Figure):
            self._g.savefig(filename)
        else:
            # When fig_tight=True (the default), the margins are very slightly different.
            # I don't know how to properly account for this yet (or even if it is possible),
            # since it only happens at figsize time -- do "a=plot(sin); a.save??".
            # So for interactive graphics, we just set this to false no matter what.
            kwds['fig_tight'] = False
            self._g.save(filename, **kwds)

    def show(self, **kwds):
        STORED_INTERACTIVE_GRAPHICS.append(self)
        if self.renderer == "sage":
            return self.show_sage(**kwds)
        elif self.renderer == "matplotlib":
            return self.show_matplotlib(**kwds)

    def show_matplotlib(self, **kwds):
        self.fig = self.figure(**kwds)
        CommFigure(self.fig)
        for k, v in self._events.items():
            self.fig.canvas.mpl_connect(k, v)

    def show_sage(self, **kwds):
        fig = self.figure(**kwds)
        from matplotlib.backends.backend_agg import FigureCanvasAgg
        canvas = FigureCanvasAgg(fig)
        fig.set_canvas(canvas)
        fig.tight_layout(
        )  # critical, since sage does this -- if not, coords all wrong
        ax = fig.axes[0]
        # upper left data coordinates
        xmin, ymax = ax.transData.inverted().transform(
            fig.transFigure.transform((0, 1)))
        # lower right data coordinates
        xmax, ymin = ax.transData.inverted().transform(
            fig.transFigure.transform((1, 0)))

        def to_data_coords(p):
            # 0<=x,y<=1
            return ((xmax - xmin) * p[0] + xmin,
                    (ymax - ymin) * (1 - p[1]) + ymin)

        def on_msg(msg):
            data = msg['content']['data']
            x, y = data['x'], data['y']
            eventType = data['eventType']
            if eventType in self._events:
                self._events[eventType](to_data_coords([x, y]))

        file_id = uuid()
        if kwds.get('svg', False):
            filename = '%s.svg' % file_id
            del kwds['svg']
        else:
            filename = '%s.png' % file_id

        fig.savefig(filename)

        from comm import SageCellComm as Comm
        self.comm = Comm(data={"filename": filename},
                         target_name='graphicswidget')
        import sys
        sys._sage_.sent_files[filename] = os.path.getmtime(filename)

        self.comm.on_msg(on_msg)