stream#

Module: stream.client#

ArrayCircularQueue([max_size, dimension, ...])

This implements a MultiDimensional Queue which works with Arrays and RawArrays.

FuryStreamClient(showm[, max_window_size, ...])

This obj is responsible to create a StreamClient.

FuryStreamInteraction(showm[, ...])

This obj.

IntervalTimer(seconds, callback, *args, **kwargs)

A object that creates a timer that calls a function periodically.

IntervalTimerThreading(seconds, callback, ...)

Implements a object with the same behavior of setInterval from Js

RawArrayImageBufferManager([...])

This implements an ImageBufferManager using RawArrays.

SharedMemCircularQueue([max_size, ...])

This implements a MultiDimensional Queue which works with SharedMemory.

SharedMemImageBufferManager([...])

This implements an ImageBufferManager using the SharedMemory approach.

partial

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

callback_stream_client(stream_client)

This callback is used to update the image inside of the ImageManager instance

interaction_callback(circular_queue, showm, ...)

This callback is used to invoke vtk interaction events reading those events from the provided circular_queue instance

Module: stream.constants#

namedtuple(typename, field_names, *[, ...])

Returns a new subclass of tuple with named fields.

Module: stream.server#

web_server([image_buffer_names, ...])

This will create a streaming webserver running on the given port and host using SharedMemory.

web_server_raw_array([image_buffers, ...])

This will create a streaming webserver running on the given port and host using RawArrays.

Module: stream.server.async_app#

MultipartWriter([subtype, boundary])

Multipart body writer.

WSCloseCode(value)

An enumeration.

partial

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

get_app([rtc_server, folder, ...])

index(request, **kwargs)

javascript(request, **kwargs)

mjpeg_handler(request)

This async function it's responsible to create the MJPEG streaming.

offer(request, **kwargs)

on_shutdown(app)

set_mouse(data, circular_queue)

set_mouse_click(data, circular_queue)

3 | LeftButtonPressEvent 4 | LeftButtonReleaseEvent 5 | MiddleButtonPressEvent 6 | MiddleButtonReleaseEvent 7 | RightButtonPressEvent 8 | RightButtonReleaseEvent

set_weel(data, circular_queue)

websocket_handler(request, **kwargs)

Module: stream.server.main#

ArrayCircularQueue([max_size, dimension, ...])

This implements a MultiDimensional Queue which works with Arrays and RawArrays.

RTCServer(image_buffer_manager)

This Obj it's responsible to create the VideoStream for the WebRTCServer

RawArrayImageBufferManager([...])

This implements an ImageBufferManager using RawArrays.

SharedMemCircularQueue([max_size, ...])

This implements a MultiDimensional Queue which works with SharedMemory.

SharedMemImageBufferManager([...])

This implements an ImageBufferManager using the SharedMemory approach.

VideoStreamTrack

alias of object

get_app([rtc_server, folder, ...])

remove_shm_from_resource_tracker()

Monkey-patch multiprocessing.resource_tracker so SharedMemory won't be tracked

web_server([image_buffer_names, ...])

This will create a streaming webserver running on the given port and host using SharedMemory.

web_server_raw_array([image_buffers, ...])

This will create a streaming webserver running on the given port and host using RawArrays.

Module: stream.tools#

ABC()

Helper class that provides a standard way to create an ABC using inheritance.

ArrayCircularQueue([max_size, dimension, ...])

This implements a MultiDimensional Queue which works with Arrays and RawArrays.

GenericCircularQueue([max_size, dimension, ...])

This implements a generic circular queue which works with shared memory resources.

GenericImageBufferManager([max_window_size, ...])

This implements a abstract (generic) ImageBufferManager with the n-buffer techinique.

GenericMultiDimensionalBuffer([max_size, ...])

This implements a abstract (generic) multidimensional buffer.

IntervalTimer(seconds, callback, *args, **kwargs)

A object that creates a timer that calls a function periodically.

IntervalTimerThreading(seconds, callback, ...)

Implements a object with the same behavior of setInterval from Js

RawArrayImageBufferManager([...])

This implements an ImageBufferManager using RawArrays.

RawArrayMultiDimensionalBuffer(max_size[, ...])

This implements a multidimensional buffer with RawArray.

SharedMemCircularQueue([max_size, ...])

This implements a MultiDimensional Queue which works with SharedMemory.

SharedMemImageBufferManager([...])

This implements an ImageBufferManager using the SharedMemory approach.

SharedMemMultiDimensionalBuffer(max_size[, ...])

This implements a generic multidimensional buffer with SharedMemory.

Timer(interval, function[, args, kwargs])

Call a function after a specified number of seconds:

abstractmethod(funcobj)

A decorator indicating abstract methods.

remove_shm_from_resource_tracker()

Monkey-patch multiprocessing.resource_tracker so SharedMemory won't be tracked

Module: stream.widget#

FuryStreamClient(showm[, max_window_size, ...])

This obj is responsible to create a StreamClient.

FuryStreamInteraction(showm[, ...])

This obj.

IFrame(src, width, height[, extras])

Generic class to embed an iframe in an IPython notebook

Widget(showm[, ms_stream, ms_interaction, ...])

Thi Obj it's able execute the fury streaming system using the SharedMemory object from Python multiprocessing.

check_port_is_available(host, port)

Check if a given port it's available

display(*objs[, include, exclude, metadata, ...])

Display a Python object in all frontends.

ArrayCircularQueue#

class fury.stream.client.ArrayCircularQueue(max_size=10, dimension=6, head_tail_buffer=None, buffer=None)[source]#

Bases: GenericCircularQueue

This implements a MultiDimensional Queue which works with Arrays and RawArrays.

__init__(max_size=10, dimension=6, head_tail_buffer=None, buffer=None)[source]#

Stream system uses that to implement user interactions

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • head_tail_buffer (buffer, optional) – If buffer is not passed to __init__ then this obj will create a new RawArray to store head and tail position.

  • buffer (buffer, optional) – If buffer is not passed to __init__ then the multidimensional buffer obj will create a new RawArray to store the data

cleanup()[source]#
create_mem_resource()[source]#
dequeue()[source]#
enqueue(data)[source]#
load_mem_resource()[source]#

FuryStreamClient#

class fury.stream.client.FuryStreamClient(showm, max_window_size=None, use_raw_array=True, whithout_iren_start=False, num_buffers=2)[source]#

Bases: object

This obj is responsible to create a StreamClient.

__init__(showm, max_window_size=None, use_raw_array=True, whithout_iren_start=False, num_buffers=2)[source]#

A StreamClient extracts a framebuffer from the OpenGL context and writes into a shared memory resource.

Parameters:
  • showm (ShowManager) –

  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • use_raw_array (bool, optional) – If False then FuryStreamClient will use SharedMemory instead of RawArrays. Notice that Python >=3.8 it’s a requirement to use SharedMemory)

  • whithout_iren_start (bool, optional) – Sometimes you can’t initiate the vtkInteractor instance.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

cleanup()[source]#

Release the shared memory resources if necessary.

start(ms=0, use_asyncio=False)[source]#

Start the stream client.

Parameters:
  • ms (float, optional) – positive number. This update the image buffer using a interval of ms milliseconds. If ms is 0 then the stream client will update the buffer after every Render event.

  • use_asyncio (bool, optional) – If False then the stream client will update the image using a threading technique.

stop()[source]#

Stop the stream client.

FuryStreamInteraction#

class fury.stream.client.FuryStreamInteraction(showm, max_queue_size=50, use_raw_array=True, whithout_iren_start=False)[source]#

Bases: object

This obj. is responsible to manage the user interaction

__init__(showm, max_queue_size=50, use_raw_array=True, whithout_iren_start=False)[source]#
Parameters:
  • showm (ShowmManager) –

  • max_queue_size (int, optional) – maximum number of events to be stored.

  • use_raw_array (bool, optional) – If False then a CircularQueue will be created using SharedMemory instead of RawArrays. Notice that Python >=3.8 it’s requirement to use SharedMemory.

  • whithout_iren_start (bool, optional) – Set that to True if you can’t initiate the vtkInteractor instance.

cleanup()[source]#

Release the shared memory resources if necessary.

start(ms=3, use_asyncio=False)[source]#

Start the stream interaction client.

Parameters:
  • ms (float, optional) – positive number greather than zero.

  • use_asyncio (bool, optional) – If False then the interaction will be performed in a separate thread.

stop()[source]#

Stop the stream interaction client.

IntervalTimer#

class fury.stream.client.IntervalTimer(seconds, callback, *args, **kwargs)[source]#

Bases: object

A object that creates a timer that calls a function periodically.

__init__(seconds, callback, *args, **kwargs)[source]#
Parameters:
  • seconds (float) – A postive float number. Represents the total amount of seconds between each call

  • callback (function) – The function to be called

  • *args (args) – args to be passed to callback

  • **kwargs (kwargs) – kwargs to be passed to callback

start()[source]#

Start the timer

stop()[source]#

Stop the timer

IntervalTimerThreading#

class fury.stream.client.IntervalTimerThreading(seconds, callback, *args, **kwargs)[source]#

Bases: object

Implements a object with the same behavior of setInterval from Js

__init__(seconds, callback, *args, **kwargs)[source]#
Parameters:
  • seconds (float) – A postive float number. Represents the total amount of seconds between each call

  • callback (function) – The function to be called

  • *args (args) – args to be passed to callback

  • **kwargs (kwargs) – kwargs to be passed to callback

Examples

def callback(arr):
    arr += [len(arr)]
arr = []
interval_timer = tools.IntervalTimer(1, callback, arr)
interval_timer.start()
time.sleep(5)
interval_timer.stop()
# len(arr) == 5

References

[1] https://stackoverflow.com/questions/3393612/run-certain-code-every-n-seconds

start()[source]#

Start the timer

stop()[source]#

Stop the timer

RawArrayImageBufferManager#

class fury.stream.client.RawArrayImageBufferManager(max_window_size=(100, 100), num_buffers=2, image_buffers=None, info_buffer=None)[source]#

Bases: GenericImageBufferManager

This implements an ImageBufferManager using RawArrays.

__init__(max_window_size=(100, 100), num_buffers=2, image_buffers=None, info_buffer=None)[source]#
Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • info_buffer (buffer, optional) – A buffer with the information about the current frame to be streamed and the respective sizes

  • image_buffers (list of buffers, optional) – A list of buffers with each one containing a frame.

cleanup()[source]#
create_mem_resource()[source]#
load_mem_resource()[source]#

SharedMemCircularQueue#

class fury.stream.client.SharedMemCircularQueue(max_size=10, dimension=6, head_tail_buffer_name=None, buffer_name=None)[source]#

Bases: GenericCircularQueue

This implements a MultiDimensional Queue which works with SharedMemory.

__init__(max_size=10, dimension=6, head_tail_buffer_name=None, buffer_name=None)[source]#

Stream system uses that to implemenet user interactions

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • head_tail_buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory with the head and tail informations

  • buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory to create the MultiDimensionalBuffer

cleanup()[source]#
create_mem_resource()[source]#
dequeue()[source]#
enqueue(data)[source]#
is_unlocked()[source]#
load_mem_resource()[source]#
lock()[source]#
unlock()[source]#

SharedMemImageBufferManager#

class fury.stream.client.SharedMemImageBufferManager(max_window_size=(100, 100), num_buffers=2, image_buffer_names=None, info_buffer_name=None)[source]#

Bases: GenericImageBufferManager

This implements an ImageBufferManager using the SharedMemory approach.

__init__(max_window_size=(100, 100), num_buffers=2, image_buffer_names=None, info_buffer_name=None)[source]#

Note

Python >=3.8 is a requirement to use this object.

Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • info_buffer_name (str) – The name of a buffer with the information about the current frame to be streamed and the respective sizes

  • image_buffer_names (list of str, optional) – a list of buffer names. Each buffer contains a frame

cleanup()[source]#

Release the resources used by the Shared Memory Manager

create_mem_resource()[source]#
load_mem_resource()[source]#

partial#

class fury.stream.client.partial[source]#

Bases: object

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

__init__()#
args#

tuple of arguments to future partial calls

func#

function object to use in future partial calls

keywords#

dictionary of keyword arguments to future partial calls

callback_stream_client#

fury.stream.client.callback_stream_client(stream_client)[source]#

This callback is used to update the image inside of the ImageManager instance

Parameters:

stream_client (StreamClient) –

interaction_callback#

fury.stream.client.interaction_callback(circular_queue, showm, iren, render_after)[source]#

This callback is used to invoke vtk interaction events reading those events from the provided circular_queue instance

Parameters:
  • circular_queue (CircularQueue) –

  • showm (ShowmManager) –

  • iren (vtkInteractor) –

  • render_after (bool, optional) – If the render method should be called after an dequeue

namedtuple#

fury.stream.constants.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)[source]#

Returns a new subclass of tuple with named fields.

>>> Point = namedtuple('Point', ['x', 'y'])
>>> Point.__doc__                   # docstring for the new class
'Point(x, y)'
>>> p = Point(11, y=22)             # instantiate with positional args or keywords
>>> p[0] + p[1]                     # indexable like a plain tuple
33
>>> x, y = p                        # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y                       # fields also accessible by name
33
>>> d = p._asdict()                 # convert to a dictionary
>>> d['x']
11
>>> Point(**d)                      # convert from a dictionary
Point(x=11, y=22)
>>> p._replace(x=100)               # _replace() is like str.replace() but targets named fields
Point(x=100, y=22)

web_server#

fury.stream.server.web_server(image_buffer_names=None, info_buffer_name=None, queue_head_tail_buffer_name=None, queue_buffer_name=None, port=8000, host='localhost', provides_mjpeg=True, provides_webrtc=True, avoid_unlink_shared_mem=True, ms_jpeg=16, run_app=True)[source]#

This will create a streaming webserver running on the given port and host using SharedMemory.

Parameters:
  • image_buffers_name (list of str) – A list of buffers with each one containing a frame.

  • info_buffer_name (str) – A buffer with the information about the current frame to be streamed and the respective sizes

  • queue_head_tail_buffer_name (str, optional) – If buffer is passed than this Obj will read a a already created RawArray.

  • buffer_name (str, optional) – If queue_buffer is passed than this Obj will read a a already created RawArray containing the user interactions events stored in the queue_buffer.

  • port (int, optional) – Port to be used by the aiohttp server

  • host (str, optional, default localhost) – host to be used by the aiohttp server

  • provides_mjpeg (bool, default True) – If a MJPEG streaming should be available. If True you can consume that through host:port/video/mjpeg or if you want to interact you can consume that through your browser http://host:port?encoding=mjpeg

  • provides_webrtc (bool, default True) – If a WebRTC streaming should be available. http://host:port

  • avoid_unlink_shared_mem (bool, default False) – If True, then this will apply a monkey-patch solution to a python>=3.8 core bug

  • ms_jpeg (float, optional) – This it’s used only if the MJPEG will be used. The ms_jpeg represents the amount of miliseconds between to consecutive calls of the jpeg enconding.

  • run_app (bool, default True) – This will run the aiohttp application. The False condition is used just to be able to test the server.

web_server_raw_array#

fury.stream.server.web_server_raw_array(image_buffers=None, info_buffer=None, queue_head_tail_buffer=None, queue_buffer=None, port=8000, host='localhost', provides_mjpeg=True, provides_webrtc=True, ms_jpeg=16, run_app=True)[source]#

This will create a streaming webserver running on the given port and host using RawArrays.

Parameters:
  • image_buffers (list of buffers) – A list of buffers with each one containing a frame.

  • info_buffer (buffer) – A buffer with the information about the current frame to be streamed and the respective sizes

  • queue_head_tail_buffer (buffer) – If buffer is passed than this Obj will read a a already created RawArray.

  • queue_buffer (buffer) – If queue_buffer is passed than this Obj will read a a already created RawArray containing the user interactions events stored in the queue_buffer.

  • port (int, optional) – Port to be used by the aiohttp server

  • host (str, optional, default localhost) – host to be used by the aiohttp server

  • provides_mjpeg (bool, default True) – If a MJPEG streaming should be available. If True you can consume that through host:port/video/mjpeg or if you want to interact you can consume that through your browser http://host:port?encoding=mjpeg

  • provides_webrtc (bool, default True) – If a WebRTC streaming should be available. http://host:port

  • ms_jpeg (float, optional) – This it’s used only if the MJPEG will be used. The ms_jpeg represents the amount of miliseconds between to consecutive calls of the jpeg enconding.

  • run_app (bool, default True) – This will run the aiohttp application. The False condition is used just to be able to test the server.

MultipartWriter#

class fury.stream.server.async_app.MultipartWriter(subtype: str = 'mixed', boundary: str | None = None)[source]#

Bases: Payload

Multipart body writer.

__init__(subtype: str = 'mixed', boundary: str | None = None) None[source]#
append(obj: Any, headers: MultiMapping[str] | None = None) Payload[source]#
append_form(obj: Sequence[Tuple[str, str]] | Mapping[str, str], headers: MultiMapping[str] | None = None) Payload[source]#

Helper to append form urlencoded part.

append_json(obj: Any, headers: MultiMapping[str] | None = None) Payload[source]#

Helper to append JSON part.

append_payload(payload: Payload) Payload[source]#

Adds a new body part to multipart writer.

property boundary: str#
property size: int | None#

Size of the payload.

async write(writer: Any, close_boundary: bool = True) None[source]#

Write body.

WSCloseCode#

class fury.stream.server.async_app.WSCloseCode(value)[source]#

Bases: IntEnum

An enumeration.

__init__()#
ABNORMAL_CLOSURE = 1006#
BAD_GATEWAY = 1014#
GOING_AWAY = 1001#
INTERNAL_ERROR = 1011#
INVALID_TEXT = 1007#
MANDATORY_EXTENSION = 1010#
MESSAGE_TOO_BIG = 1009#
OK = 1000#
POLICY_VIOLATION = 1008#
PROTOCOL_ERROR = 1002#
SERVICE_RESTART = 1012#
TRY_AGAIN_LATER = 1013#
UNSUPPORTED_DATA = 1003#

partial#

class fury.stream.server.async_app.partial[source]#

Bases: object

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

__init__()#
args#

tuple of arguments to future partial calls

func#

function object to use in future partial calls

keywords#

dictionary of keyword arguments to future partial calls

get_app#

fury.stream.server.async_app.get_app(rtc_server=None, folder=None, circular_queue=None, image_buffer_manager=None, provides_mjpeg=False, broadcast=True)[source]#

index#

async fury.stream.server.async_app.index(request, **kwargs)[source]#

javascript#

async fury.stream.server.async_app.javascript(request, **kwargs)[source]#

mjpeg_handler#

async fury.stream.server.async_app.mjpeg_handler(request)[source]#

This async function it’s responsible to create the MJPEG streaming.

Notes:#

endpoint : /video/mjpeg

offer#

async fury.stream.server.async_app.offer(request, **kwargs)[source]#

on_shutdown#

async fury.stream.server.async_app.on_shutdown(app)[source]#

set_mouse#

fury.stream.server.async_app.set_mouse(data, circular_queue)[source]#

set_mouse_click#

fury.stream.server.async_app.set_mouse_click(data, circular_queue)[source]#

3 | LeftButtonPressEvent 4 | LeftButtonReleaseEvent 5 | MiddleButtonPressEvent 6 | MiddleButtonReleaseEvent 7 | RightButtonPressEvent 8 | RightButtonReleaseEvent

set_weel#

fury.stream.server.async_app.set_weel(data, circular_queue)[source]#

websocket_handler#

async fury.stream.server.async_app.websocket_handler(request, **kwargs)[source]#

ArrayCircularQueue#

class fury.stream.server.main.ArrayCircularQueue(max_size=10, dimension=6, head_tail_buffer=None, buffer=None)[source]#

Bases: GenericCircularQueue

This implements a MultiDimensional Queue which works with Arrays and RawArrays.

__init__(max_size=10, dimension=6, head_tail_buffer=None, buffer=None)[source]#

Stream system uses that to implement user interactions

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • head_tail_buffer (buffer, optional) – If buffer is not passed to __init__ then this obj will create a new RawArray to store head and tail position.

  • buffer (buffer, optional) – If buffer is not passed to __init__ then the multidimensional buffer obj will create a new RawArray to store the data

cleanup()[source]#
create_mem_resource()[source]#
dequeue()[source]#
enqueue(data)[source]#
load_mem_resource()[source]#

RTCServer#

class fury.stream.server.main.RTCServer(image_buffer_manager)[source]#

Bases: object

This Obj it’s responsible to create the VideoStream for the WebRTCServer

__init__(image_buffer_manager)[source]#
Parameters:

image_buffer_manager (ImageBufferManager) –

async recv()[source]#

Return a VideoFrame to be used in the WebRTC Server

The frame will be created using the image stored in the shared memory

Returns:

frame

Return type:

VideoFrame

release()[source]#

Release the RTCServer

RawArrayImageBufferManager#

class fury.stream.server.main.RawArrayImageBufferManager(max_window_size=(100, 100), num_buffers=2, image_buffers=None, info_buffer=None)[source]#

Bases: GenericImageBufferManager

This implements an ImageBufferManager using RawArrays.

__init__(max_window_size=(100, 100), num_buffers=2, image_buffers=None, info_buffer=None)[source]#
Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • info_buffer (buffer, optional) – A buffer with the information about the current frame to be streamed and the respective sizes

  • image_buffers (list of buffers, optional) – A list of buffers with each one containing a frame.

cleanup()[source]#
create_mem_resource()[source]#
load_mem_resource()[source]#

SharedMemCircularQueue#

class fury.stream.server.main.SharedMemCircularQueue(max_size=10, dimension=6, head_tail_buffer_name=None, buffer_name=None)[source]#

Bases: GenericCircularQueue

This implements a MultiDimensional Queue which works with SharedMemory.

__init__(max_size=10, dimension=6, head_tail_buffer_name=None, buffer_name=None)[source]#

Stream system uses that to implemenet user interactions

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • head_tail_buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory with the head and tail informations

  • buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory to create the MultiDimensionalBuffer

cleanup()[source]#
create_mem_resource()[source]#
dequeue()[source]#
enqueue(data)[source]#
is_unlocked()[source]#
load_mem_resource()[source]#
lock()[source]#
unlock()[source]#

SharedMemImageBufferManager#

class fury.stream.server.main.SharedMemImageBufferManager(max_window_size=(100, 100), num_buffers=2, image_buffer_names=None, info_buffer_name=None)[source]#

Bases: GenericImageBufferManager

This implements an ImageBufferManager using the SharedMemory approach.

__init__(max_window_size=(100, 100), num_buffers=2, image_buffer_names=None, info_buffer_name=None)[source]#

Note

Python >=3.8 is a requirement to use this object.

Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • info_buffer_name (str) – The name of a buffer with the information about the current frame to be streamed and the respective sizes

  • image_buffer_names (list of str, optional) – a list of buffer names. Each buffer contains a frame

cleanup()[source]#

Release the resources used by the Shared Memory Manager

create_mem_resource()[source]#
load_mem_resource()[source]#

VideoStreamTrack#

fury.stream.server.main.VideoStreamTrack#

alias of object

get_app#

fury.stream.server.main.get_app(rtc_server=None, folder=None, circular_queue=None, image_buffer_manager=None, provides_mjpeg=False, broadcast=True)[source]#

remove_shm_from_resource_tracker#

fury.stream.server.main.remove_shm_from_resource_tracker()[source]#

Monkey-patch multiprocessing.resource_tracker so SharedMemory won’t be tracked

Notes

More details at: https://bugs.python.org/issue38119

web_server#

fury.stream.server.main.web_server(image_buffer_names=None, info_buffer_name=None, queue_head_tail_buffer_name=None, queue_buffer_name=None, port=8000, host='localhost', provides_mjpeg=True, provides_webrtc=True, avoid_unlink_shared_mem=True, ms_jpeg=16, run_app=True)[source]#

This will create a streaming webserver running on the given port and host using SharedMemory.

Parameters:
  • image_buffers_name (list of str) – A list of buffers with each one containing a frame.

  • info_buffer_name (str) – A buffer with the information about the current frame to be streamed and the respective sizes

  • queue_head_tail_buffer_name (str, optional) – If buffer is passed than this Obj will read a a already created RawArray.

  • buffer_name (str, optional) – If queue_buffer is passed than this Obj will read a a already created RawArray containing the user interactions events stored in the queue_buffer.

  • port (int, optional) – Port to be used by the aiohttp server

  • host (str, optional, default localhost) – host to be used by the aiohttp server

  • provides_mjpeg (bool, default True) – If a MJPEG streaming should be available. If True you can consume that through host:port/video/mjpeg or if you want to interact you can consume that through your browser http://host:port?encoding=mjpeg

  • provides_webrtc (bool, default True) – If a WebRTC streaming should be available. http://host:port

  • avoid_unlink_shared_mem (bool, default False) – If True, then this will apply a monkey-patch solution to a python>=3.8 core bug

  • ms_jpeg (float, optional) – This it’s used only if the MJPEG will be used. The ms_jpeg represents the amount of miliseconds between to consecutive calls of the jpeg enconding.

  • run_app (bool, default True) – This will run the aiohttp application. The False condition is used just to be able to test the server.

web_server_raw_array#

fury.stream.server.main.web_server_raw_array(image_buffers=None, info_buffer=None, queue_head_tail_buffer=None, queue_buffer=None, port=8000, host='localhost', provides_mjpeg=True, provides_webrtc=True, ms_jpeg=16, run_app=True)[source]#

This will create a streaming webserver running on the given port and host using RawArrays.

Parameters:
  • image_buffers (list of buffers) – A list of buffers with each one containing a frame.

  • info_buffer (buffer) – A buffer with the information about the current frame to be streamed and the respective sizes

  • queue_head_tail_buffer (buffer) – If buffer is passed than this Obj will read a a already created RawArray.

  • queue_buffer (buffer) – If queue_buffer is passed than this Obj will read a a already created RawArray containing the user interactions events stored in the queue_buffer.

  • port (int, optional) – Port to be used by the aiohttp server

  • host (str, optional, default localhost) – host to be used by the aiohttp server

  • provides_mjpeg (bool, default True) – If a MJPEG streaming should be available. If True you can consume that through host:port/video/mjpeg or if you want to interact you can consume that through your browser http://host:port?encoding=mjpeg

  • provides_webrtc (bool, default True) – If a WebRTC streaming should be available. http://host:port

  • ms_jpeg (float, optional) – This it’s used only if the MJPEG will be used. The ms_jpeg represents the amount of miliseconds between to consecutive calls of the jpeg enconding.

  • run_app (bool, default True) – This will run the aiohttp application. The False condition is used just to be able to test the server.

ABC#

class fury.stream.tools.ABC[source]#

Bases: object

Helper class that provides a standard way to create an ABC using inheritance.

__init__()#

ArrayCircularQueue#

class fury.stream.tools.ArrayCircularQueue(max_size=10, dimension=6, head_tail_buffer=None, buffer=None)[source]#

Bases: GenericCircularQueue

This implements a MultiDimensional Queue which works with Arrays and RawArrays.

__init__(max_size=10, dimension=6, head_tail_buffer=None, buffer=None)[source]#

Stream system uses that to implement user interactions

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • head_tail_buffer (buffer, optional) – If buffer is not passed to __init__ then this obj will create a new RawArray to store head and tail position.

  • buffer (buffer, optional) – If buffer is not passed to __init__ then the multidimensional buffer obj will create a new RawArray to store the data

cleanup()[source]#
create_mem_resource()[source]#
dequeue()[source]#
enqueue(data)[source]#
load_mem_resource()[source]#

GenericCircularQueue#

class fury.stream.tools.GenericCircularQueue(max_size=None, dimension=8, use_shared_mem=False, buffer=None, buffer_name=None)[source]#

Bases: ABC

This implements a generic circular queue which works with shared memory resources.

__init__(max_size=None, dimension=8, use_shared_mem=False, buffer=None, buffer_name=None)[source]#
Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • use_shared_mem (bool, default False) – If the multidimensional memory resource should create or read using SharedMemory or RawArrays

  • buffer (RawArray, optional) –

  • buffer_name (str, optional) –

abstract cleanup()[source]#
abstract create_mem_resource()[source]#
abstract dequeue()[source]#
abstract enqueue(data)[source]#
property head#
abstract load_mem_resource()[source]#
set_head_tail(head, tail, lock=1)[source]#
property tail#

GenericImageBufferManager#

class fury.stream.tools.GenericImageBufferManager(max_window_size=None, num_buffers=2, use_shared_mem=False)[source]#

Bases: ABC

This implements a abstract (generic) ImageBufferManager with the n-buffer techinique.

__init__(max_window_size=None, num_buffers=2, use_shared_mem=False)[source]#
Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • use_shared_mem (bool, default False) –

async async_get_jpeg(ms=33)[source]#
property buffer_index#
abstract cleanup()[source]#
abstract create_mem_resource()[source]#
get_current_frame()[source]#

Get the current frame from the buffer.

get_jpeg()[source]#

Returns a jpeg image from the buffer.

Returns:

jpeg image.

Return type:

bytes

abstract load_mem_resource()[source]#
property next_buffer_index#
write_into(w, h, np_arr)[source]#

GenericMultiDimensionalBuffer#

class fury.stream.tools.GenericMultiDimensionalBuffer(max_size=None, dimension=8)[source]#

Bases: ABC

This implements a abstract (generic) multidimensional buffer.

__init__(max_size=None, dimension=8)[source]#
Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory

  • dimension (int, default 8) –

property buffer#
abstract cleanup()[source]#
abstract create_mem_resource()[source]#
get_start_end(idx)[source]#
abstract load_mem_resource()[source]#

IntervalTimer#

class fury.stream.tools.IntervalTimer(seconds, callback, *args, **kwargs)[source]#

Bases: object

A object that creates a timer that calls a function periodically.

__init__(seconds, callback, *args, **kwargs)[source]#
Parameters:
  • seconds (float) – A postive float number. Represents the total amount of seconds between each call

  • callback (function) – The function to be called

  • *args (args) – args to be passed to callback

  • **kwargs (kwargs) – kwargs to be passed to callback

start()[source]#

Start the timer

stop()[source]#

Stop the timer

IntervalTimerThreading#

class fury.stream.tools.IntervalTimerThreading(seconds, callback, *args, **kwargs)[source]#

Bases: object

Implements a object with the same behavior of setInterval from Js

__init__(seconds, callback, *args, **kwargs)[source]#
Parameters:
  • seconds (float) – A postive float number. Represents the total amount of seconds between each call

  • callback (function) – The function to be called

  • *args (args) – args to be passed to callback

  • **kwargs (kwargs) – kwargs to be passed to callback

Examples

def callback(arr):
    arr += [len(arr)]
arr = []
interval_timer = tools.IntervalTimer(1, callback, arr)
interval_timer.start()
time.sleep(5)
interval_timer.stop()
# len(arr) == 5

References

[1] https://stackoverflow.com/questions/3393612/run-certain-code-every-n-seconds

start()[source]#

Start the timer

stop()[source]#

Stop the timer

RawArrayImageBufferManager#

class fury.stream.tools.RawArrayImageBufferManager(max_window_size=(100, 100), num_buffers=2, image_buffers=None, info_buffer=None)[source]#

Bases: GenericImageBufferManager

This implements an ImageBufferManager using RawArrays.

__init__(max_window_size=(100, 100), num_buffers=2, image_buffers=None, info_buffer=None)[source]#
Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • info_buffer (buffer, optional) – A buffer with the information about the current frame to be streamed and the respective sizes

  • image_buffers (list of buffers, optional) – A list of buffers with each one containing a frame.

cleanup()[source]#
create_mem_resource()[source]#
load_mem_resource()[source]#

RawArrayMultiDimensionalBuffer#

class fury.stream.tools.RawArrayMultiDimensionalBuffer(max_size, dimension=4, buffer=None)[source]#

Bases: GenericMultiDimensionalBuffer

This implements a multidimensional buffer with RawArray.

__init__(max_size, dimension=4, buffer=None)[source]#

Stream system uses that to implemenet the CircularQueue with shared memory resources.

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory

  • dimension (int, default 8) –

  • buffer (buffer, optional) – If buffer is not passed to __init__ then the multidimensional buffer obj will create a new RawArray object to store the data If buffer is passed than this Obj will read a a already created RawArray

cleanup()[source]#
create_mem_resource()[source]#
load_mem_resource()[source]#

SharedMemCircularQueue#

class fury.stream.tools.SharedMemCircularQueue(max_size=10, dimension=6, head_tail_buffer_name=None, buffer_name=None)[source]#

Bases: GenericCircularQueue

This implements a MultiDimensional Queue which works with SharedMemory.

__init__(max_size=10, dimension=6, head_tail_buffer_name=None, buffer_name=None)[source]#

Stream system uses that to implemenet user interactions

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory. This will be used to construct the multidimensional buffer

  • dimension (int, default 8) – This will be used to construct the multidimensional buffer

  • head_tail_buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory with the head and tail informations

  • buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory to create the MultiDimensionalBuffer

cleanup()[source]#
create_mem_resource()[source]#
dequeue()[source]#
enqueue(data)[source]#
is_unlocked()[source]#
load_mem_resource()[source]#
lock()[source]#
unlock()[source]#

SharedMemImageBufferManager#

class fury.stream.tools.SharedMemImageBufferManager(max_window_size=(100, 100), num_buffers=2, image_buffer_names=None, info_buffer_name=None)[source]#

Bases: GenericImageBufferManager

This implements an ImageBufferManager using the SharedMemory approach.

__init__(max_window_size=(100, 100), num_buffers=2, image_buffer_names=None, info_buffer_name=None)[source]#

Note

Python >=3.8 is a requirement to use this object.

Parameters:
  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

  • info_buffer_name (str) – The name of a buffer with the information about the current frame to be streamed and the respective sizes

  • image_buffer_names (list of str, optional) – a list of buffer names. Each buffer contains a frame

cleanup()[source]#

Release the resources used by the Shared Memory Manager

create_mem_resource()[source]#
load_mem_resource()[source]#

SharedMemMultiDimensionalBuffer#

class fury.stream.tools.SharedMemMultiDimensionalBuffer(max_size, dimension=4, buffer_name=None)[source]#

Bases: GenericMultiDimensionalBuffer

This implements a generic multidimensional buffer with SharedMemory.

__init__(max_size, dimension=4, buffer_name=None)[source]#

Stream system uses that to implemenet the CircularQueue with shared memory resources.

Parameters:
  • max_size (int, optional) – If buffer_name or buffer was not passed then max_size it’s mandatory

  • dimension (int, default 8) –

  • buffer_name (str, optional) – if buffer_name is passed than this Obj will read a a already created SharedMemory

cleanup()[source]#
create_mem_resource()[source]#
load_mem_resource()[source]#

Timer#

class fury.stream.tools.Timer(interval, function, args=None, kwargs=None)[source]#

Bases: Thread

Call a function after a specified number of seconds:

t = Timer(30.0, f, args=None, kwargs=None) t.start() t.cancel() # stop the timer’s action if it’s still waiting

__init__(interval, function, args=None, kwargs=None)[source]#

This constructor should always be called with keyword arguments. Arguments are:

group should be None; reserved for future extension when a ThreadGroup class is implemented.

target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called.

name is the thread name. By default, a unique name is constructed of the form “Thread-N” where N is a small decimal number.

args is the argument tuple for the target invocation. Defaults to ().

kwargs is a dictionary of keyword arguments for the target invocation. Defaults to {}.

If a subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing anything else to the thread.

cancel()[source]#

Stop the timer if it hasn’t finished yet.

run()[source]#

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

abstractmethod#

fury.stream.tools.abstractmethod(funcobj)[source]#

A decorator indicating abstract methods.

Requires that the metaclass is ABCMeta or derived from it. A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods are overridden. The abstract methods can be called using any of the normal ‘super’ call mechanisms. abstractmethod() may be used to declare abstract methods for properties and descriptors.

Usage:

class C(metaclass=ABCMeta):

@abstractmethod def my_abstract_method(self, …):

remove_shm_from_resource_tracker#

fury.stream.tools.remove_shm_from_resource_tracker()[source]#

Monkey-patch multiprocessing.resource_tracker so SharedMemory won’t be tracked

Notes

More details at: https://bugs.python.org/issue38119

FuryStreamClient#

class fury.stream.widget.FuryStreamClient(showm, max_window_size=None, use_raw_array=True, whithout_iren_start=False, num_buffers=2)[source]#

Bases: object

This obj is responsible to create a StreamClient.

__init__(showm, max_window_size=None, use_raw_array=True, whithout_iren_start=False, num_buffers=2)[source]#

A StreamClient extracts a framebuffer from the OpenGL context and writes into a shared memory resource.

Parameters:
  • showm (ShowManager) –

  • max_window_size (tuple of ints, optional) – This allows resize events inside of the FURY window instance. Should be greater than the window size.

  • use_raw_array (bool, optional) – If False then FuryStreamClient will use SharedMemory instead of RawArrays. Notice that Python >=3.8 it’s a requirement to use SharedMemory)

  • whithout_iren_start (bool, optional) – Sometimes you can’t initiate the vtkInteractor instance.

  • num_buffers (int, optional) – Number of buffers to be used in the n-buffering techinique.

cleanup()[source]#

Release the shared memory resources if necessary.

start(ms=0, use_asyncio=False)[source]#

Start the stream client.

Parameters:
  • ms (float, optional) – positive number. This update the image buffer using a interval of ms milliseconds. If ms is 0 then the stream client will update the buffer after every Render event.

  • use_asyncio (bool, optional) – If False then the stream client will update the image using a threading technique.

stop()[source]#

Stop the stream client.

FuryStreamInteraction#

class fury.stream.widget.FuryStreamInteraction(showm, max_queue_size=50, use_raw_array=True, whithout_iren_start=False)[source]#

Bases: object

This obj. is responsible to manage the user interaction

__init__(showm, max_queue_size=50, use_raw_array=True, whithout_iren_start=False)[source]#
Parameters:
  • showm (ShowmManager) –

  • max_queue_size (int, optional) – maximum number of events to be stored.

  • use_raw_array (bool, optional) – If False then a CircularQueue will be created using SharedMemory instead of RawArrays. Notice that Python >=3.8 it’s requirement to use SharedMemory.

  • whithout_iren_start (bool, optional) – Set that to True if you can’t initiate the vtkInteractor instance.

cleanup()[source]#

Release the shared memory resources if necessary.

start(ms=3, use_asyncio=False)[source]#

Start the stream interaction client.

Parameters:
  • ms (float, optional) – positive number greather than zero.

  • use_asyncio (bool, optional) – If False then the interaction will be performed in a separate thread.

stop()[source]#

Stop the stream interaction client.

IFrame#

class fury.stream.widget.IFrame(src, width, height, extras: Iterable[str] | None = None, **kwargs)[source]#

Bases: object

Generic class to embed an iframe in an IPython notebook

__init__(src, width, height, extras: Iterable[str] | None = None, **kwargs)[source]#
iframe = '\n        <iframe\n            width="{width}"\n            height="{height}"\n            src="{src}{params}"\n            frameborder="0"\n            allowfullscreen\n            {extras}\n        ></iframe>\n        '#

Widget#

class fury.stream.widget.Widget(showm, ms_stream=33, ms_interaction=33, host='localhost', port=None, encoding='mjpeg', ms_jpeg=33, queue_size=20)[source]#

Bases: object

Thi Obj it’s able execute the fury streaming system using the SharedMemory object from Python multiprocessing.

__init__(showm, ms_stream=33, ms_interaction=33, host='localhost', port=None, encoding='mjpeg', ms_jpeg=33, queue_size=20)[source]#
Parameters:
  • showm (ShowmManager) –

  • ms_stream (float, optional) – time in mileseconds between each frame buffer update.

  • ms_interaction (float, optional) – time in mileseconds between each user interaction update.

  • host (str, optional) –

  • port (int, optional) –

  • encoding (str, optional) – If should use MJPEG streaming or WebRTC.

  • ms_jpeg (float, optional) – This it’s used only if the MJPEG will be used. The ms_jpeg represents the amount of miliseconds between to consecutive calls of the jpeg enconding.

  • queue_size (int, optional) – maximum number of user interactions to be stored

cleanup()[source]#

Release the shared memory

property command_string#

Return the command string to start the server

Returns:

command_string

Return type:

str

display(height=150)[source]#

Start the server and display the url in an iframe

return_iframe(height=200)[source]#

Return the jupyter div iframe used to show the stream

run_command()[source]#

Evaluate the command string to start the server

start(use_asyncio=False)[source]#

Start the fury client and the interaction client and return the url

Parameters:

use_asyncio (bool, optional) – If should use the asyncio version of the server. Default is False.

stop()[source]#

Stop the streaming server and release the shared memory

property url#

Return the url to access the server

check_port_is_available#

fury.stream.widget.check_port_is_available(host, port)[source]#

Check if a given port it’s available

Parameters:
  • host (str) –

  • port (int) –

Returns:

available

Return type:

bool

display#

fury.stream.widget.display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, raw=False, clear=False, **kwargs)[source]#

Display a Python object in all frontends.

By default all representations will be computed and sent to the frontends. Frontends can decide which representation is used and how.

In terminal IPython this will be similar to using print(), for use in richer frontends see Jupyter notebook examples with rich display logic.

Parameters:
  • *objs (object) – The Python objects to display.

  • raw (bool, optional) – Are the objects to be displayed already mimetype-keyed dicts of raw display data, or Python objects that need to be formatted before display? [default: False]

  • include (list, tuple or set, optional) – A list of format type strings (MIME types) to include in the format data dict. If this is set only the format types included in this list will be computed.

  • exclude (list, tuple or set, optional) – A list of format type strings (MIME types) to exclude in the format data dict. If this is set all format types will be computed, except for those included in this argument.

  • metadata (dict, optional) – A dictionary of metadata to associate with the output. mime-type keys in this dictionary will be associated with the individual representation formats, if they exist.

  • transient (dict, optional) – A dictionary of transient data to associate with the output. Data in this dict should not be persisted to files (e.g. notebooks).

  • display_id (str, bool optional) – Set an id for the display. This id can be used for updating this display area later via update_display. If given as True, generate a new display_id

  • clear (bool, optional) – Should the output area be cleared before displaying anything? If True, this will wait for additional output before clearing. [default: False]

  • **kwargs (additional keyword-args, optional) – Additional keyword-arguments are passed through to the display publisher.

Returns:

handle – Returns a handle on updatable displays for use with update_display(), if display_id is given. Returns None if no display_id is given (default).

Return type:

DisplayHandle

Examples

>>> class Json(object):
...     def __init__(self, json):
...         self.json = json
...     def _repr_pretty_(self, pp, cycle):
...         import json
...         pp.text(json.dumps(self.json, indent=2))
...     def __repr__(self):
...         return str(self.json)
...
>>> d = Json({1:2, 3: {4:5}})
>>> print(d)
{1: 2, 3: {4: 5}}
>>> display(d)
{
  "1": 2,
  "3": {
    "4": 5
  }
}
>>> def int_formatter(integer, pp, cycle):
...     pp.text('I'*integer)
>>> plain = get_ipython().display_formatter.formatters['text/plain']
>>> plain.for_type(int, int_formatter)
<function _repr_pprint at 0x...>
>>> display(7-5)
II
>>> del plain.type_printers[int]
>>> display(7-5)
2

See also

update_display()

Notes

In Python, objects can declare their textual representation using the __repr__ method. IPython expands on this idea and allows objects to declare other, rich representations including:

  • HTML

  • JSON

  • PNG

  • JPEG

  • SVG

  • LaTeX

A single object can declare some or all of these representations; all are handled by IPython’s display system.

The main idea of the first approach is that you have to implement special display methods when you define your class, one for each representation you want to use. Here is a list of the names of the special methods and the values they must return:

  • _repr_html_: return raw HTML as a string, or a tuple (see below).

  • _repr_json_: return a JSONable dict, or a tuple (see below).

  • _repr_jpeg_: return raw JPEG data, or a tuple (see below).

  • _repr_png_: return raw PNG data, or a tuple (see below).

  • _repr_svg_: return raw SVG data as a string, or a tuple (see below).

  • _repr_latex_: return LaTeX commands in a string surrounded by “$”,

    or a tuple (see below).

  • _repr_mimebundle_: return a full mimebundle containing the mapping

    from all mimetypes to data. Use this for any mime-type not listed above.

The above functions may also return the object’s metadata alonside the data. If the metadata is available, the functions will return a tuple containing the data and metadata, in that order. If there is no metadata available, then the functions will return the data only.

When you are directly writing your own classes, you can adapt them for display in IPython by following the above approach. But in practice, you often need to work with existing classes that you can’t easily modify.

You can refer to the documentation on integrating with the display system in order to register custom formatters for already existing types (integrating_rich_display).

New in version 5.4: display available without import

New in version 6.1: display available without import

Since IPython 5.4 and 6.1 display() is automatically made available to the user without import. If you are using display in a document that might be used in a pure python context or with older version of IPython, use the following import at the top of your file:

from IPython.display import display