Source code for algorithmx.server.CanvasServer

from typing import Dict, Optional, Callable, Any
from threading import Thread
import json
import os.path as ospath

from .FileServer import (
    FileServer,
    relative_file_handler,
    absolute_file_handler,
)
from .WebsocketServer import WebsocketServer
from . import Canvas, create_canvas, DispatchEvent, ReceiveEvent


[docs]class CanvasServer: """ A local HTTP server using WebSockets to transmit data. """ def __init__(self, file: Optional[str], host: str, port: int): self._canvases: Dict[str, Canvas] = {} file_handler = None if file is None: file_handler = absolute_file_handler( ospath.abspath(ospath.dirname(__file__)), "algorithmx.html" ) else: file_handler = relative_file_handler(file) self.file_server = FileServer(file_handler, host, port) self.websocket_server = WebsocketServer(host, port + 1) def receive_raw(message: str): event = json.loads(message) canvas_name = event["canvas"] if "canvas" in event else "0" if canvas_name in self._canvases: self._canvases[canvas_name].receive(event) self.websocket_server.onreceive(receive_raw)
[docs] def start(self): """ Starts the server on the current threat, blocking all further execution until the server shuts down. """ websocket_thread = Thread( target=lambda: self.websocket_server.start(), daemon=True ) websocket_thread.start() try: self.file_server.start() except (KeyboardInterrupt, SystemExit): pass
[docs] def shutdown(self): """ Shuts down the server. This must be called on a different thread to the one used to start the server. """ self.file_server.shutdown()
[docs] def canvas(self, name: Optional[str] = None) -> Canvas: """Returns an :class:`~api.Canvas` interface, which will dispatch and receive events through a WebSocket connected to the server. :name: (Optional) The name of the canvas. By default, each server will only render one canvas, and so this argument has no affect. However, if you wish to design a custom UI with more than one canvas per page, you can use this to differentiate between them. """ full_name = name or "0" if full_name in self._canvases: return self._canvases[full_name] def dispatch(event: DispatchEvent): json_event = { **event, **({"canvas": name} if name is not None else {}), } self.websocket_server.dispatch(json.dumps(json_event)) canvas = create_canvas() canvas.ondispatch(dispatch) self._canvases[full_name] = canvas return canvas