Exemple #1
0
    def __init__(self, df, to_show, widths, wrap_around, **kwargs):
        super().__init__(**kwargs)
        self.add_class("content")
        d = Event(source=self,
                  watched_events=['wheel', "mousemove", "mouseleave"])
        d.on_dom_event(self.event_handler)

        self.to_show = to_show
        self.num_rows = min(len(df),
                            to_show if to_show % 2 == 0 else to_show + 1)
        self.idx = 0
        self.wrap_around = wrap_around
        self.df = df
        self.records = df.to_records()

        def row_on_click(event):
            if event["new"] != -1:
                self.value = event["new"]

        self.rows = deque([
            _Row(data=self.records[i],
                 widths=widths,
                 on_click=row_on_click,
                 style=["row_even", "row_odd"][i % 2])
            for i in range(self.num_rows)
        ])
        self.children = list(self.rows)[0:self.to_show]
Exemple #2
0
class MouseDataWidget(MouseData):
    """Get data from mouse input, using ipywidget/ipyevent methods (Jupyter: %matplotlib inline)
        usage:  data = MouseDataWidget(); data.display(); ...
        after input, stores 'data.m' points, with locations 'data.X' and targets 'data.Y'
        plot image widget available as "data.image"
    """
    def __init__(self, *args, **kwargs):
        from ipywidgets import Image
        from ipyevents import Event
        #self.plot = None   # set in super?
        super().__init__(*args, **kwargs)
        self.image = Image(value=blank_png, format='png')
        self.no_drag = Event(source=self.image,
                             watched_events=['dragstart'],
                             prevent_default_action=True)
        self.events = Event(source=self.image,
                            watched_events=['click', 'contextmenu'],
                            prevent_default_action=True)
        self.events.on_dom_event(self.__on_click)
        self.update_plot()

    def __get_coord(self, event):
        return np.array([event['dataX'] / self.w, 1 - event['dataY'] / self.h
                         ]) * 2 / (1 - 2 * self.border) - 1 - self.border

    def update_plot(self):
        import matplotlib.pyplot as plt
        from .plot import plotClassify2D
        fig = plt.figure(figsize=(6, 6))
        plt.gcf().add_axes((self.border, self.border, 1 - 2 * self.border,
                            1 - 2 * self.border))  # full width figure
        plt.axis(self.lim)
        plt.gca().set_xticks([])
        plt.gca().set_yticks([])
        if (self.m > 0) or (self.plot is not None):
            if self.plot is not None:
                self.plot(self.X, self.Y)  # user-provided plot f'n
            else:
                fig.gca().scatter(self.X[:, 0], self.X[:, 1],
                                  c=self.Y)  # or default
        self.image.value = plot_to_png(plt.gcf())
        self.w, self.h = plt.gcf().canvas.get_renderer(
        ).get_canvas_width_height()
        fig.clear()

    def display(self, fig=None, **kwargs):
        from IPython.display import display
        if self.plot is None:
            print(
                "Default plot. Left-click to add data; shift/alt/ctrl combos determine target value; right click to remove data."
            )
        display(self.image)

    def __on_click(self, event):
        if (event['type'] == 'click'):  # left click = add point
            self.add_point(self.__get_coord(event), wkeys(event))
        elif (event['type']
              == 'contextmenu') and (self.m > 0):  # right click = remove point
            self.remove_nearest(self.__get_coord(event))
        self.update_plot()
Exemple #3
0
class Icon(WidgetWrapper):
    def __init__(self, fontawesome_icon: str):
        """Clickable icon

        Parameters
        ----------
        fontawesome_icon: str
            icon string from the fontawesome https://fontawesome.com. For example, "fab fa-500px"
        """
        self.el_icon = widgets.HTML(
            value=f'<i class="{fontawesome_icon}"></i>')
        self.on_click_callback = lambda: 1
        self.event_listener = Event(source=self.el_icon,
                                    watched_events=['click'])
        self.event_listener.on_dom_event(self.fire_on_click_event)

    @property
    def widget(self):
        return self.el_icon

    def on_click(self, callback: Callable[[], None]):
        self.on_click_callback = callback

    def fire_on_click_event(self, event: dict):
        if event['type'] == 'click':
            self.on_click_callback()
    def construct_arrow_handler(self):
        """ Should only be run once """
        disp_events = Event(source=self, watched_events=['keydown'])

        def handle_disp(event):
            if event["type"] == "keydown":
                self.key_funcs[event["key"]]()

        disp_events.on_dom_event(handle_disp)
Exemple #5
0
    def __init__(self, data, widths, on_click, style, _types=None, **kwargs):
        super().__init__(**kwargs)
        self.add_class(style)
        self.observe(on_click, "value")
        d = Event(source=self, watched_events=['click'])
        d.on_dom_event(self.on_click)

        self.data = data
        self.cells = [_Cell(x, w) for x, w in zip(data, widths)]
        self.cells[0].add_class("index")
        self.children = self.cells
    def construct_click_handler(self):
        """ Should only be run once """
        disp_events = Event(source=self, watched_events=['click'])

        def handle_disp(event):
            if event["type"] == "click":
                x = self.images[self.page_index]._size[0]
                if event["relativeX"] > (x / 2):
                    self.next_page()
                elif event["relativeX"] < (x / 2):
                    self.previous_page()

        disp_events.on_dom_event(handle_disp)
Exemple #7
0
class HTML(WidgetWrapper):
    def __init__(self, html: str):
        self.el = widgets.HTML(value=html)
        self.on_click_callback = lambda: 1
        self.event_listener = Event(source=self.el, watched_events=['click'])
        self.event_listener.on_dom_event(self.fire_on_click_event)

    @property
    def widget(self):
        return self.el

    def on_click(self, callback: Callable[[], None]):
        self.on_click_callback = callback

    def fire_on_click_event(self, event: dict):
        if event['type'] == 'click':
            self.on_click_callback()
Exemple #8
0
class Button(WidgetWrapper):
    """An extended button widget that support mouse over event.
    """
    def __init__(self, **kwargs):
        """Same constructor as `ipywidgets.widgets.Button`"""
        self.el_btn = widgets.Button(**kwargs)
        self.el_btn.on_click(self.fire_on_click_event)
        self.on_click_callback = lambda x: x
        self.on_mouseenter_callback = lambda x: x
        self.on_mouseleave_callback = lambda x: x

        self.event_listener = Event(
            source=self.el_btn, watched_events=['mouseenter', 'mouseleave'])
        self.event_listener.on_dom_event(self.fire_mouse_event)

    @property
    def widget(self):
        return self.el_btn

    def on_click(self, callback: Callable[['Button'], None]):
        """Register for on click event"""
        self.on_click_callback = callback

    def on_mouseenter(self, callback: Callable[['Button'], None]):
        """Register for on mouse enter event"""
        self.on_mouseenter_callback = callback

    def on_mouseleave(self, callback: Callable[['Button'], None]):
        """Register for on mouse leave event"""
        self.on_mouseleave_callback = callback

    def fire_on_click_event(self, _btn: widgets.Button):
        """Fire the on_click event when users click the button"""
        self.on_click_callback(self)

    def fire_mouse_event(self, event: dict):
        """Show the content on mouse over"""
        if event['type'] == 'mouseenter':
            self.on_mouseenter_callback(self)
        else:
            self.on_mouseleave_callback(self)
Exemple #9
0
def test_callbacks():
    # Test that the initial callbacks look right, that we can add one, and
    # that clearing removes them all.
    event_widget = Event()

    assert len(event_widget._dom_handlers.callbacks) == 0

    def noop(event):
        pass

    def noop2(event):
        pass

    event_widget.on_dom_event(noop)

    assert noop in event_widget._dom_handlers.callbacks
    assert len(event_widget._dom_handlers.callbacks) == 1

    # Add noop2....
    event_widget.on_dom_event(noop2)
    assert event_widget._dom_handlers.callbacks == [noop, noop2]

    # Try removing noop2
    event_widget.on_dom_event(noop2, remove=True)
    assert event_widget._dom_handlers.callbacks == [noop]

    # Finally, clear all callbacks
    event_widget.reset_callbacks()

    assert len(event_widget._dom_handlers.callbacks) == 0
Exemple #10
0
def init(*args, **kwargs):
    global flag, downflag, shiftflag

    w = widgets.Image(value=m.system.image_data(), width=600)
    d = Event(source=w,
              watched_events=[
                  'mousedown', 'mouseup', 'mousemove', 'keyup', 'keydown',
                  'wheel'
              ])
    no_drag = Event(source=w,
                    watched_events=['dragstart'],
                    prevent_default_action=True)
    d.on_dom_event(listen_mouse)
    run = widgets.ToggleButton(
        value=False,
        description='Run',
        disabled=False,
        button_style='',  # 'success', 'info', 'warning', 'danger' or ''
        tooltip='run the simulation',
        icon='play')
    pause = widgets.ToggleButton(
        value=False,
        description='Pause',
        disabled=False,
        button_style='',  # 'success', 'info', 'warning', 'danger' or ''
        tooltip='pause the simulation',
        icon='pause')

    reset = widgets.ToggleButton(
        value=False,
        description='Reset',
        disabled=False,
        button_style='',  # 'success', 'info', 'warning', 'danger' or ''
        tooltip='reset the simulation',
        icon='stop')

    def onToggleRun(b):
        global flag
        if run.value:
            run.button_style = 'success'
            pause.value = False
            pause.button_style = ''
            reset.value = False
            reset.button_style = ''
            flag = True
        else:
            run.button_style = ''
            flag = False

    def onTogglePause(b):
        global flag
        if pause.value:
            pause.button_style = 'success'
            run.value = False
            run.button_style = ''
            reset.value = False
            reset.button_style = ''
            flag = False
        else:
            pause.button_style = ''
            flag = True

    def onToggleReset(b):
        global flag
        if reset.value:
            reset.button_style = 'success'
            pause.value = False
            pause.button_style = ''
            run.value = False
            run.button_style = ''
            flag = False
            m.Universe.reset()
        else:
            reset.button_style = ''
            #w = create_simulation()

    buttons = widgets.HBox([run, pause, reset])
    run.observe(onToggleRun, 'value')
    pause.observe(onTogglePause, 'value')
    reset.observe(onToggleReset, 'value')

    box = widgets.VBox([w, buttons])
    display(box)

    def background_threading():
        global flag
        while True:
            m.Simulator.context_make_current()
            if flag:
                m.step()
            w.value = m.system.image_data()
            m.Simulator.context_release()
            time.sleep(0.01)

    t = threading.Thread(target=background_threading)
    t.start()
Exemple #11
0
class Core:
    # All constants that will be injected into global scope in the user"s cell
    global_constants = {
        "pi": pi
    }

    ignite_globals = _ignite_globals

    def __init__(self, globals_dict):
        self.status_text = display(Code(""), display_id=True)
        self._globals_dict = globals_dict
        self._methods = {}

        self.stop_button = Button(description="Stop")
        self.stop_button.on_click(self.on_stop_button_clicked)
        self._globals_dict["canvas"] = Canvas()
        self.kb_mon = Event(source=self.canvas, watched_events=['keydown', 'keyup'], wait=1000 // FRAME_RATE,
                            prevent_default_actions=True)
        self.output_text = ""
        self.color_strings = {
            "default": "#888888"
        }
        match_255 = r"(?:(?:2(?:(?:5[0-5])|(?:[0-4][0-9])))|(?:[01]?[0-9]{1,2}))"
        match_alpha = r"(?:(?:1(?:\.0*)?)|(?:0(?:\.[0-9]*)?))"
        match_360 = r"(?:(?:3[0-5][0-9])|(?:[0-2]?[0-9]{1,2}))"
        match_100 = r"(?:100|[0-9]{1,2})"
        self.regexes = [
            re.compile(r"#[0-9A-Fa-f]{6}"),
            re.compile(r"rgb\({},{},{}\)".format(match_255, match_255, match_255)),
            re.compile(r"rgba\({},{},{},{}\)".format(match_255, match_255, match_255, match_alpha)),
            re.compile(r"hsl\({},{}%,{}%\)".format(match_360, match_100, match_100)),
            re.compile(r"hsla\({},{}%,{}%,{}\)".format(match_360, match_100, match_100, match_alpha))
        ]
        self.width, self.height = DEFAULT_CANVAS_SIZE
        self.mouse_x = 0
        self.mouse_y = 0
        self.mouse_is_pressed = False
        self.key = ""
        self._keys_held = {}

        # Settings for drawing text (https://ipycanvas.readthedocs.io/en/latest/drawing_text.html).
        self.font_settings = {
            'size': 12.0,
            'font': 'sans-serif',
            'baseline': 'top',
            'align': 'left'
        }

    ### Properties ###

    @property
    @ignite_global
    def canvas(self) -> Canvas:
        return self._globals_dict["canvas"]

    @property
    @ignite_global
    def mouse_x(self):
        return self._globals_dict["mouse_x"]

    @mouse_x.setter
    def mouse_x(self, val):
        self._globals_dict["mouse_x"] = val

    @property
    @ignite_global
    def mouse_y(self):
        return self._globals_dict["mouse_y"]

    @mouse_y.setter
    def mouse_y(self, val):
        self._globals_dict["mouse_y"] = val

    @property
    @ignite_global
    def mouse_is_pressed(self):
        return self._globals_dict["mouse_is_pressed"]

    @mouse_is_pressed.setter
    def mouse_is_pressed(self, val):
        self._globals_dict["mouse_is_pressed"] = val

    @property
    @ignite_global
    def key(self):
        return self._globals_dict["key"]

    @key.setter
    def key(self, val):
        self._globals_dict["key"] = val

    @property
    @ignite_global
    def width(self):
        return self._globals_dict["width"]

    @width.setter
    def width(self, val):
        self._globals_dict["width"] = val
        self.canvas.width = val

    @property
    @ignite_global
    def height(self):
        return self._globals_dict["height"]

    @height.setter
    def height(self, val):
        self._globals_dict["height"] = val
        self.canvas.height = val

    ### Library init ###

    # Updates last activity time
    @staticmethod
    def refresh_last_activity():
        global _sparkplug_last_activity
        _sparkplug_last_activity = time.time()

    # Creates canvas and starts thread
    def start(self, methods):
        self._methods = methods
        draw = self._methods.get("draw", None)

        if draw:
            self.print_status("Running...")
            display(self.stop_button)

        display(self.canvas)

        self.output_text_code = display(Code(self.output_text), display_id=True)

        self.canvas.on_mouse_down(self.on_mouse_down)
        self.canvas.on_mouse_up(self.on_mouse_up)
        self.canvas.on_mouse_move(self.on_mouse_move)

        self.kb_mon.on_dom_event(self.handle_kb_event)

        # Initialize text drawing settings for the canvas. ()
        self.canvas.font = f"{self.font_settings['size']}px {self.font_settings['font']}"
        self.canvas.text_baseline = 'top'
        self.canvas.text_align = 'left'

        thread = threading.Thread(target=self.loop)
        thread.start()

    def stop(self, message="Stopped"):
        global _sparkplug_running

        if not _sparkplug_running:
            return

        _sparkplug_running = False
        self.print_status(message)
        self.kb_mon.reset_callbacks()
        self.kb_mon.close()

        # Assuming we're using IPython to draw the canvas through the display() function.
        # Commenting this out for now, it throws exception since it does not derive BaseException
        # raise IpyExit

    # Loop method that handles drawing and setup
    def loop(self):
        global _sparkplug_active_thread_id, _sparkplug_running

        # Set active thread to this thread. This will stop any other active thread.
        current_thread_id = threading.current_thread().native_id
        _sparkplug_active_thread_id = current_thread_id
        _sparkplug_running = True
        self.refresh_last_activity()

        draw = self._methods.get("draw", None)
        setup = self._methods.get("setup", None)

        if setup:
            try:
                setup()
            except Exception as e:
                self.stop("Error in setup() function: " + str(e))
                return

        while _sparkplug_running:
            if _sparkplug_active_thread_id != current_thread_id \
                    or time.time() - _sparkplug_last_activity > NO_ACTIVITY_THRESHOLD:
                self.stop("Stopped due to inactivity")
                return

            if not draw:
                self.stop("Done drawing.")
                return

            with hold_canvas(self.canvas):
                try:
                    draw()
                except Exception as e:
                    self.stop("Error in draw() function: " + str(e))
                    return

            time.sleep(1 / FRAME_RATE)

    # Prints status to embedded error box
    def print_status(self, msg):
        self.status_text.update(Code(msg))

    # Prints output to embedded output box
    # Can't use @validate_args decorator for functions actually accepting variable arguments
    @ignite_global
    def print(self, *args, sep=' ', end='\n', flush=True):
        global _sparkplug_running
        self.output_text += sep.join([str(arg) for arg in args]) + end

        if _sparkplug_running and flush:
            self.output_text_code.update(Code(self.output_text))

    # Update mouse_x, mouse_y, and call mouse_down handler
    def on_mouse_down(self, x, y):
        self.refresh_last_activity()
        self.mouse_x, self.mouse_y = int(x), int(y)
        self.mouse_is_pressed = True

        mouse_down = self._methods.get("mouse_down", None)
        if mouse_down:
            mouse_down()

    # Update mouse_x, mouse_y, and call mouse_up handler
    def on_mouse_up(self, x, y):
        self.refresh_last_activity()
        self.mouse_x, self.mouse_y = int(x), int(y)
        self.mouse_is_pressed = False

        mouse_up = self._methods.get("mouse_up", None)
        if mouse_up:
            mouse_up()

    # Update mouse_x, mouse_y, and call mouse_moved handler
    def on_mouse_move(self, x, y):
        self.refresh_last_activity()
        self.mouse_x, self.mouse_y = int(x), int(y)

        mouse_moved = self._methods.get("mouse_moved", None)
        if mouse_moved:
            mouse_moved()

    def on_stop_button_clicked(self, button):
        self.stop()

    @extern
    def handle_kb_event(self, event): pass

    ### User overrideable functions ###

    # The function bodies here do not matter, they are discarded
    @ignite_global(mutable=True)
    def setup(self): pass

    @ignite_global(mutable=True)
    def draw(self): pass

    @ignite_global(mutable=True)
    def mouse_up(self): pass

    @ignite_global(mutable=True)
    def mouse_down(self): pass

    @ignite_global(mutable=True)
    def mouse_moved(self): pass

    @ignite_global(mutable=True)
    def key_pressed(self): pass

    @ignite_global(mutable=True)
    def key_released(self): pass

    @ignite_global(mutable=True)
    def key_repeated(self): pass

    ### Global functions ###

    # From .util.helper_functions.keyboard_functions

    @extern
    def keys_held(self, *args): pass

    @extern
    def key_held(self, *args): pass

    # From .util.helper_functions.canvas_functions

    @extern
    def size(self, *args): pass

    @extern
    def fill_style(self): pass

    @extern
    def stroke_style(self, *args): pass

    @extern
    def clear(self, *args): pass

    @extern
    def background(self, *args): pass

    # From util.helper_functions.rect_functions

    @extern
    def rect(self, *args): pass

    @extern
    def fill_rect(self, *args): pass

    @extern
    def stroke_rect(self, *args): pass

    @extern
    def clear_rect(self, *args): pass

    # From util.helper_functions.square_functions

    @extern
    def square(self, *args): pass

    @extern
    def stroke_square(self, *args): pass

    @extern
    def fill_square(self, *args): pass

    # From util.helper_functions.circle_functions

    @extern
    def circle(self, *args): pass

    @extern
    def fill_circle(self, *args): pass

    @extern
    def stroke_circle(self, *args): pass

    # From util.helper_functions.ellipse_functions

    @extern
    def ellipse(self, *args): pass

    @extern
    def fill_ellipse(self, *args): pass

    @extern
    def stroke_ellipse(self, *args): pass

    # From util.helper_functions.arc_functions

    @extern
    def arc(self, *args): pass

    @extern
    def fill_arc(self, *args): pass

    @extern
    def stroke_arc(self, *args): pass

    # From util.helper_functions.triangle_functions

    @extern
    def triangle(self, *args): pass

    @extern
    def fill_triangle(self, *args): pass

    @extern
    def stroke_triangle(self, *args): pass

    # From util.helper_functions.text_functions

    @extern
    def text_size(self, *args): pass

    @extern
    def text_align(self, *args): pass

    @extern
    def text(self, *args): pass

    # From util.helper_functions.line_functions

    @extern
    def draw_line(self, *args): pass

    @extern
    def line(self, *args): pass

    @extern
    def line_width(self, *args): pass

    # An alias to line_width
    @extern
    def stroke_width(self, *args): pass

    ### Helper Functions ###

    # From util.helper_functions.misc_functions

    @extern
    def parse_color(self, *args, func_name="parse_color"): pass

    @extern
    def color(self, *args): pass

    @extern
    def parse_color_string(self, func_name, s): pass

    @extern
    def arc_args(self, *args): pass

    @extern
    def random(self, *args): pass

    @extern
    def randint(self, *args): pass
Exemple #12
0
class ViewInteractiveWidget(Canvas):
    """Remote controller for VTK render windows.

    Parameters
    ----------
    allow_wheel : bool, optional
        Capture wheel events and allow zooming using the mouse wheel.

    quality : float, optional
        Full rendering image quality.  100 for best quality, 0 for min
        quality.  Default 85.

    quick_quality : float, optional
        Quick rendering image quality during mouse dragging.  100 for
        best quality, 0 for min quality.  Default 50.  Keep this
        number low to allow rapid rendering on limited bandwidth.

    on_close : callable
        A callable function with no arguments to be triggered when the widget
        is destroyed. This is useful to have a callback to close/clean up the
        render window.

    """
    def __init__(self,
                 render_window,
                 log_events=True,
                 transparent_background=False,
                 allow_wheel=True,
                 quality=85,
                 quick_quality=50,
                 on_close=None,
                 **kwargs):
        """Accepts a vtkRenderWindow."""

        super().__init__(**kwargs)
        if quality < 0 or quality > 100:
            raise ValueError('`quality` parameter must be between 0 and 100')
        self._quality = quality
        self._render_window = weakref.ref(render_window)
        self.render_window.SetOffScreenRendering(1)  # Force off screen
        self.transparent_background = transparent_background

        self._full_quality = quality
        self._quick_quality = quick_quality

        # Frame rate (1/renderDelay)
        self.last_render_time = 0
        self.quick_render_delay_sec = 0.01
        self.quick_render_delay_sec_range = [0.02, 2.0]
        self.adaptive_render_delay = True
        self.last_mouse_move_event = None

        # refresh if mouse is just moving (not dragging)
        self.track_mouse_move = False

        self.message_timestamp_offset = None

        self.layout.width = '100%'
        self.layout.height = 'auto'

        # Set Canvas size from window size
        self.width, self.height = self.render_window.GetSize()

        # record first render time
        tstart = time.time()
        self.update_canvas()
        self._first_render_time = time.time() - tstart
        log.debug('First image in %.5f seconds', self._first_render_time)

        # this is the minimum time to render anyway
        self.set_quick_render_delay(self._first_render_time)

        self.dragging = False

        self.interaction_events = Event()
        # Set the throttle or debounce time in millseconds (must be an non-negative integer)
        # See https://github.com/mwcraig/ipyevents/pull/55
        self.interaction_events.throttle_or_debounce = "throttle"
        self.interaction_events.wait = INTERACTION_THROTTLE
        self.interaction_events.source = self

        allowed_events = [
            "dragstart",
            "mouseenter",
            "mouseleave",
            "mousedown",
            "mouseup",
            "mousemove",
            "keyup",
            "keydown",
            "contextmenu",  # prevent context menu from appearing on right-click
        ]

        # May be disabled out so that user can scroll through the
        # notebook using mousewheel
        if allow_wheel:
            allowed_events.append("wheel")

        self.interaction_events.watched_events = allowed_events

        # self.interaction_events.msg_throttle = 1  # does not seem to have effect
        self.interaction_events.prevent_default_action = True
        self.interaction_events.on_dom_event(self.handle_interaction_event)

        # Errors are not displayed when a widget is displayed,
        # this variable can be used to retrieve error messages
        self.error = None

        # Enable logging of UI events
        self.log_events = log_events
        self.logged_events = []
        self.elapsed_times = []
        self.age_of_processed_messages = []

        if hasattr(on_close, '__call__'):
            self._on_close = on_close
        else:
            self._on_close = lambda: None

    @property
    def render_window(self):
        """reference the weak reference"""
        ren_win = self._render_window()
        if ren_win is None:
            raise RuntimeError('VTK render window has closed')
        return ren_win

    @property
    def interactor(self):
        return self.render_window.GetInteractor()

    def set_quick_render_delay(self, delay_sec):
        if delay_sec < self.quick_render_delay_sec_range[0]:
            delay_sec = self.quick_render_delay_sec_range[0]
        elif delay_sec > self.quick_render_delay_sec_range[1]:
            delay_sec = self.quick_render_delay_sec_range[1]
        self.quick_render_delay_sec = delay_sec

    def update_canvas(self, force_render=True, quality=75):
        """Updates the canvas with the current render"""
        raw_img = self.get_image(force_render=force_render)
        f = BytesIO()
        PIL.Image.fromarray(raw_img).save(f, 'JPEG', quality=quality)
        image = Image(value=f.getvalue(), width=self.width, height=self.height)
        self.draw_image(image)

    def get_image(self, force_render=True):
        if force_render:
            self.render_window.Render()
        return self._fast_image

    @property
    def _fast_image(self):
        import vtk.util.numpy_support as nps
        import vtk
        arr = vtk.vtkUnsignedCharArray()
        self.render_window.GetRGBACharPixelData(0, 0, self.width - 1,
                                                self.height - 1, 0, arr)

        data = nps.vtk_to_numpy(arr).reshape(self.height, self.width, -1)[::-1]

        if self.transparent_background:
            return data
        else:  # ignore alpha channel
            return data[:, :, :-1]

    @throttle(0.1)
    def full_render(self):
        try:
            import time
            tstart = time.time()
            self.update_canvas(True, self._full_quality)
            self.last_render_time = time.time()
            log.debug('full render in %.5f seconds', time.time() - tstart)
        except Exception as e:
            self.error = str(e)

    def send_pending_mouse_move_event(self):
        if self.last_mouse_move_event is not None:
            self.update_interactor_event_data(self.last_mouse_move_event)
            self.interactor.MouseMoveEvent()
            self.last_mouse_move_event = None

    @throttle(0.01)
    def quick_render(self):
        try:
            self.send_pending_mouse_move_event()
            self.update_canvas(quality=self._quick_quality)
            if self.log_events:
                self.elapsed_times.append(time.time() - self.last_render_time)
            self.last_render_time = time.time()
        except Exception as e:
            self.error = str(e)

    def update_interactor_event_data(self, event):
        try:
            if event["event"] == "keydown" or event["event"] == "keyup":
                key = event["key"]
                sym = KEY_TO_SYM[key] if key in KEY_TO_SYM.keys() else key
                self.interactor.SetKeySym(sym)
                if len(key) == 1:
                    self.interactor.SetKeyCode(key)
                self.interactor.SetRepeatCount(1)
            else:
                self.interactor.SetEventPosition(
                    event["offsetX"], self.height - event["offsetY"])
            self.interactor.SetShiftKey(event["shiftKey"])
            self.interactor.SetControlKey(event["ctrlKey"])
            self.interactor.SetAltKey(event["altKey"])
        except Exception as e:
            self.error = str(e)

    def handle_interaction_event(self, event):
        event_name = event["event"]

        # we have to scale the mouse movement here relative to the
        # canvas size, otherwise mouse movement won't correspond to
        # the render window.

        if 'offsetX' in event:
            event['offsetX'] = round(
                event["clientX"] -
                event["boundingRectLeft"])  #re-calculate coordinates
            scale_x = self.width / event['boundingRectWidth']
            event['offsetX'] = round(event['offsetX'] * scale_x)
            event['offsetY'] = round(
                event["clientY"] -
                event["boundingRectTop"])  #re-calculate coordinates
            scale_y = self.height / event['boundingRectHeight']
            event['offsetY'] = round(event['offsetY'] * scale_y)

        try:
            if self.log_events:
                self.logged_events.append(event)
            if event_name == "mousemove":

                if self.message_timestamp_offset is None:
                    self.message_timestamp_offset = (
                        time.time() - event["timeStamp"] * 0.001)

                self.last_mouse_move_event = event
                if not self.dragging and not self.track_mouse_move:
                    return
                if self.adaptive_render_delay:
                    ageOfProcessedMessage = time.time() - (
                        event["timeStamp"] * 0.001 +
                        self.message_timestamp_offset)
                    if ageOfProcessedMessage > 1.5 * self.quick_render_delay_sec:
                        # we are falling behind, try to render less frequently
                        self.set_quick_render_delay(
                            self.quick_render_delay_sec * 1.05)
                    elif ageOfProcessedMessage < 0.5 * self.quick_render_delay_sec:
                        # we can keep up with events, try to render more frequently
                        self.set_quick_render_delay(
                            self.quick_render_delay_sec / 1.05)
                    if self.log_events:
                        self.age_of_processed_messages.append([
                            ageOfProcessedMessage, self.quick_render_delay_sec
                        ])
                # We need to render something now it no rendering
                # since self.quick_render_delay_sec
                if time.time(
                ) - self.last_render_time > self.quick_render_delay_sec:
                    self.quick_render()
            elif event_name == "mouseenter":
                self.update_interactor_event_data(event)
                self.interactor.EnterEvent()
                self.last_mouse_move_event = None
            elif event_name == "mouseleave":
                self.update_interactor_event_data(event)
                self.interactor.LeaveEvent()
                self.last_mouse_move_event = None
                if self.dragging:  # have to trigger a leave event and release event
                    self.interactor.LeftButtonReleaseEvent()
                    self.dragging = False
                self.full_render()
            elif event_name == "mousedown":
                self.dragging = True
                self.send_pending_mouse_move_event()
                self.update_interactor_event_data(event)
                if event["button"] == 0:
                    self.interactor.LeftButtonPressEvent()
                elif event["button"] == 2:
                    self.interactor.RightButtonPressEvent()
                elif event["button"] == 1:
                    self.interactor.MiddleButtonPressEvent()
                self.full_render()  # does this have to be rendered?
            elif event_name == "mouseup":
                self.send_pending_mouse_move_event()
                self.update_interactor_event_data(event)
                if event["button"] == 0:
                    self.interactor.LeftButtonReleaseEvent()
                elif event["button"] == 2:
                    self.interactor.RightButtonReleaseEvent()
                elif event["button"] == 1:
                    self.interactor.MiddleButtonReleaseEvent()
                self.dragging = False
                self.full_render()
            elif event_name == "keydown":
                self.send_pending_mouse_move_event()
                self.update_interactor_event_data(event)
                self.interactor.KeyPressEvent()
                self.interactor.CharEvent()
                if (event["key"] != "Shift" and event["key"] != "Control"
                        and event["key"] != "Alt"):
                    self.full_render()
            elif event_name == "keyup":
                self.send_pending_mouse_move_event()
                self.update_interactor_event_data(event)
                self.interactor.KeyReleaseEvent()
                if (event["key"] != "Shift" and event["key"] != "Control"
                        and event["key"] != "Alt"):
                    self.full_render()
            elif event_name == 'wheel':
                if 'wheel' in self.interaction_events.watched_events:
                    self.send_pending_mouse_move_event()
                    self.update_interactor_event_data(event)
                    if event['deltaY'] < 0:
                        self.interactor.MouseWheelForwardEvent()
                    else:
                        self.interactor.MouseWheelBackwardEvent()
                    self.full_render()

        except Exception as e:
            self.error = str(e)

    def close(self):
        super().close()
        self._on_close()

    def __del__(self):
        super().__del__()
        self.close()
Exemple #13
0
class ImageButton(VBox, HasTraits):
    """
    Represents simple image with label and toggle button functionality.

    # Class methods

    - clear(): Clear image infos

    - on_click(p_function): Handle click events

    # Class methods

    - clear(): Clear image infos

    - on_click(p_function): Handle click events

    - reset_callbacks(): Reset event callbacks
    """
    debug_output = Output(layout={'border': '1px solid black'})
    active = Bool()
    image_path = Unicode()
    label_value = Unicode()

    def __init__(self, im_path=None, label=None,
                 im_name=None, im_index=None,
                 display_label=True, image_width='50px', image_height=None):

        self.display_label = display_label
        self.label = 'None'
        self.image = Image(
            layout=Layout(display='flex',
                          justify_content='center',
                          align_items='center',
                          align_content='center',
                          width=image_width,
                          height=image_height),
        )

        if self.display_label:  # both image and label
            self.label = HTML(
                value='?',
                layout=Layout(display='flex',
                              justify_content='center',
                              align_items='center',
                              align_content='center'),
            )
        else:  # no label (capture image case)
            self.im_name = im_name
            self.im_index = im_index
            self.image.layout.border = 'solid 1px gray'
            self.image.layout.object_fit = 'contain'

        super().__init__(layout=Layout(align_items='center',
                                       margin='3px',
                                       padding='2px'))
        if not im_path:
            self.clear()

        self.d = Event(source=self, watched_events=['click'])

    @observe('image_path')
    def _read_image(self, change=None):
        new_path = change['new']
        if new_path:
            self.image.value = open(new_path, "rb").read()
            if not self.children:
                self.children = (self.image,)
                if self.display_label:
                    self.children += (self.label,)
        else:
            #do not display image widget
            self.children = []


    @observe('label_value')
    def _read_label(self, change=None):
        new_label = change['new']

        if isinstance(self.label, HTML):
            self.label.value = new_label
        else:
            self.label = new_label

    def clear(self):
        if isinstance(self.label, HTML):
            self.label.value = ''
        else:
            self.label = ''
        self.image_path = ''
        self.active = False

    @observe('active')
    def mark(self, ev):
        # pad to compensate self size with border
        if self.active:
            if self.display_label:
                self.layout.border = 'solid 2px #1B8CF3'
                self.layout.padding = '0px'
            else:
                self.image.layout.border = 'solid 3px #1B8CF3'
                self.image.layout.padding = '0px'
        else:
            if self.display_label:
                self.layout.border = 'none'
                self.layout.padding = '2px'
            else:
                self.image.layout.border = 'solid 1px gray'

    @property
    def name(self):
        return Path(self.image_path).name

    @debug_output.capture(clear_output=False)
    def on_click(self, cb):
        self.d.on_dom_event(cb)

    def reset_callbacks(self):
        self.d.reset_callbacks()
Exemple #14
0
def showCmd(b, layout, out):
    layout.toggle_hide(names=("show",))
    params, summary = layout["view"].children
    controls, view, fov_controls = params.children
    with out:
        logger.info("Loading results ...")
        show_ret = show(
            **layout.kwargs,
            n=0,
            f1=None,
            f2=None,
            gui=True,
        )
        if show_ret == 1:
            out.clear_output(wait=True)
            return
        model, fig, item, ax, fov = show_ret
    with view:
        plt.show()
    with summary:
        display(model.summary)
    n = widgets.BoundedIntText(
        value=0,
        min=0,
        max=model.data.Nt - 1,
        description=f"AOI (0-{model.data.Nt-1})",
        style={"description_width": "initial"},
        layout={"width": "150px"},
    )
    n_counter = widgets.BoundedIntText(
        min=0,
        max=model.data.Nt - 1,
    )
    f1_text = widgets.BoundedIntText(
        value=0,
        min=0,
        max=model.data.F - 15,
        step=1,
        description=f"Frame (0-{model.data.F-1})",
        style={"description_width": "initial"},
        layout={"width": "300px"},
    )
    f1_counter = widgets.BoundedIntText(
        min=0,
        max=model.data.F - 15,
    )
    f1_slider = widgets.IntSlider(
        value=0,
        min=0,
        max=model.data.F - 15,
        step=1,
        continuous_update=True,
        readout=False,
        readout_format="d",
        layout={"width": "210px"},
    )
    f1_box = widgets.VBox(layout=widgets.Layout(width="310px"))
    f1_incr = widgets.Button(description="+15", layout=widgets.Layout(width="45px"))
    f1_decr = widgets.Button(description="-15", layout=widgets.Layout(width="45px"))
    f1_controls = widgets.HBox(
        children=[f1_decr, f1_slider, f1_incr],
        layout=widgets.Layout(width="305px"),
    )
    f1_box.children = [f1_text, f1_controls]
    widgets.jslink((f1_slider, "value"), (f1_text, "value"))
    widgets.jslink((f1_text, "value"), (f1_counter, "value"))
    widgets.jslink((n, "value"), (n_counter, "value"))
    zoom = widgets.Checkbox(
        value=False,
        description="Zoom out frames ['z']",
        indent=False,
        layout={"width": "240px"},
    )
    labels = widgets.Checkbox(
        value=False,
        description="Show labels",
        indent=False,
        layout={"width": "240px"},
    )
    targets = widgets.Checkbox(
        value=False,
        description="Show target location ['o']",
        indent=False,
        layout={"width": "240px"},
    )
    nonspecific = widgets.Checkbox(
        value=True,
        description="Show non-specific spots ['n']",
        indent=False,
        layout={"width": "240px"},
    )
    exclude_aoi = widgets.Checkbox(
        value=not model.data.mask[0],
        description="Exclude AOI from analysis ['e']",
        indent=False,
        layout={"width": "240px"},
    )
    checkboxes = widgets.VBox(layout=widgets.Layout(width="250px"))
    if model.data.labels is None:
        checkboxes.children = [zoom, targets, nonspecific, exclude_aoi]
    else:
        checkboxes.children = [zoom, targets, nonspecific, exclude_aoi, labels]
    controls.children = [n, f1_box, checkboxes]
    # fov controls
    if fov is not None:
        for dtype in fov.dtypes:
            fov_controls.add_child(
                dtype, widgets.Checkbox(value=True, description=f"Show {dtype} AOIs")
            )
            fov_controls[dtype].observe(
                partial(
                    showAOIs,
                    fov=fov,
                    n=n_counter,
                    item=item,
                    fig=fig,
                ),
                names="value",
            )
    fov_controls.add_child("save_data", widgets.Button(description="Save data"))
    # callbacks
    n.observe(
        partial(
            updateParams,
            f1=f1_slider,
            model=model,
            fig=fig,
            item=item,
            ax=ax,
            targets=targets,
            nonspecific=nonspecific,
            labels=labels,
            fov_controls=fov_controls,
            exclude_aoi=exclude_aoi,
            show_fov=layout["show_fov"],
        ),
        names="value",
    )
    f1_slider.observe(
        partial(
            updateRange,
            n=n,
            model=model,
            fig=fig,
            item=item,
            ax=ax,
            zoom=zoom,
            targets=targets,
            fov=fov,
        ),
        names="value",
    )
    f1_incr.on_click(partial(incrementRange, x=15, counter=f1_counter))
    f1_decr.on_click(partial(incrementRange, x=-15, counter=f1_counter))
    zoom.observe(
        partial(zoomOut, f1=f1_slider, model=model, fig=fig, item=item, ax=ax),
        names="value",
    )
    labels.observe(
        partial(showLabels, n=n_counter, model=model, item=item, ax=ax),
        names="value",
    )
    targets.observe(
        partial(
            showTargets,
            n=n_counter,
            f1=f1_slider,
            model=model,
            item=item,
            ax=ax,
        ),
        names="value",
    )
    nonspecific.observe(
        partial(showNonspecific, n=n_counter, model=model, item=item, ax=ax),
        names="value",
    )
    exclude_aoi.observe(
        partial(
            excludeAOI, n=n_counter, model=model, item=item, show_fov=layout["show_fov"]
        ),
        names="value",
    )
    fov_controls["save_data"].on_click(
        partial(saveData, data=model.data, path=model.path, out=out)
    )
    # mouse click UI
    fig.canvas.mpl_connect(
        "button_release_event",
        partial(onFrameClick, counter=f1_counter),
    )
    # key press UI
    d = Event(source=layout, watched_events=["keyup"], prevent_default_action=True)
    d.on_dom_event(
        partial(
            onKeyPress,
            n=n_counter,
            f1=f1_counter,
            zoom=zoom,
            targets=targets,
            nonspecific=nonspecific,
            exclude_aoi=exclude_aoi,
        )
    )
    with out:
        logger.info("Loading results: Done")

    out.clear_output(wait=True)
    b.disabled = True
Exemple #15
0
class ImageViewEvent(ImageViewJpw):
    def __init__(self, logger=None, rgbmap=None, settings=None):
        ImageViewJpw.__init__(self,
                              logger=logger,
                              rgbmap=rgbmap,
                              settings=settings)

        self._button = 0

        # maps EventListener events to callback handlers
        self._evt_dispatch = {
            'mousedown': self.button_press_event,
            'mouseup': self.button_release_event,
            'mousemove': self.motion_notify_event,
            'wheel': self.scroll_event,
            'mouseenter': self.enter_notify_event,
            'mouseleave': self.leave_notify_event,
            'keydown': self.key_press_event,
            'keyup': self.key_release_event,
        }

        # mapping from EventListener key events to ginga key events
        self._keytbl = {
            'shiftleft': 'shift_l',
            'shiftright': 'shift_r',
            'controlleft': 'control_l',
            'controlright': 'control_r',
            'altleft': 'alt_l',
            'altright': 'alt_r',
            'osleft': 'super_l',
            'osright': 'super_r',
            'contextmenu': 'menu_r',
            'backslash': 'backslash',
            'space': 'space',
            'escape': 'escape',
            'enter': 'return',
            'tab': 'tab',
            'arrowright': 'right',
            'arrowleft': 'left',
            'arrowup': 'up',
            'arrowdown': 'down',
            'pageup': 'page_up',
            'pagedown': 'page_down',
            'f1': 'f1',
            'f2': 'f2',
            'f3': 'f3',
            'f4': 'f4',
            'f5': 'f5',
            'f6': 'f6',
            'f7': 'f7',
            'f8': 'f8',
            'f9': 'f9',
            'f10': 'f10',
            'f11': 'f11',
            'f12': 'f12',
        }

        self._keytbl2 = {
            '`': 'backquote',
            '"': 'doublequote',
            "'": 'singlequote',
        }

        # Define cursors for pick and pan
        #hand = openHandCursor()
        hand = 'fleur'
        self.define_cursor('pan', hand)
        cross = 'cross'
        self.define_cursor('pick', cross)

        for name in ('motion', 'button-press', 'button-release', 'key-press',
                     'key-release', 'drag-drop', 'scroll', 'map', 'focus',
                     'enter', 'leave', 'pinch', 'rotate', 'pan', 'swipe',
                     'tap'):
            self.enable_callback(name)

    def set_widget(self, jp_imgw):
        """Call this method with the Jupyter image widget (image_w)
        that will be used.
        """
        super(ImageViewEvent, self).set_widget(jp_imgw)

        self.jp_evt = EventListener(source=jp_imgw)
        self.jp_evt.watched_events = [
            'keydown', 'keyup', 'mouseenter', 'mouseleave', 'mousedown',
            'mouseup', 'mousemove', 'wheel', 'contextmenu'
        ]
        self.jp_evt.prevent_default_action = True

        self.jp_evt.on_dom_event(self._handle_event)
        self.logger.info("installed event handlers")

        return self.make_callback('map')

    def _handle_event(self, event):
        # TODO: need focus events and maybe a map event
        # TODO: Set up widget as a drag and drop destination
        evt_kind = event['type']
        handler = self._evt_dispatch.get(evt_kind, None)
        if handler is not None:
            return handler(event)
        return False

    def transkey(self, keycode, keyname=None):
        keycode = str(keycode).lower()
        if keyname is None:
            keyname = keycode
        self.logger.debug("key code in jupyter '%s'" % (keycode))
        res = self._keytbl.get(keycode, None)
        if res is None:
            res = self._keytbl2.get(keyname, keyname)
        return res

    def get_keyTable(self):
        return self._keytbl

    def focus_event(self, event, has_focus):
        return self.make_callback('focus', has_focus)

    def enter_notify_event(self, event):
        enter_focus = self.t_.get('enter_focus', False)
        if enter_focus:
            # TODO: set focus on canvas
            pass
        return self.make_callback('enter')

    def leave_notify_event(self, event):
        self.logger.debug("leaving widget...")
        return self.make_callback('leave')

    def key_press_event(self, event):
        keyname = self.transkey(event['code'], keyname=event['key'])
        self.logger.debug("key press event, key=%s" % (keyname))
        return self.make_ui_callback('key-press', keyname)

    def key_release_event(self, event):
        keyname = self.transkey(event['code'], keyname=event['key'])
        self.logger.debug("key release event, key=%s" % (keyname))
        return self.make_ui_callback('key-release', keyname)

    def button_press_event(self, event):
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y

        button = 0
        button |= 0x1 << event['button']
        self._button = button
        self.logger.debug("button event at %dx%d, button=%x" % (x, y, button))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('button-press', button, data_x, data_y)

    def button_release_event(self, event):
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y

        button = 0
        button |= 0x1 << event['button']
        self._button = 0
        self.logger.debug("button release at %dx%d button=%x" % (x, y, button))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('button-release', button, data_x, data_y)

    def motion_notify_event(self, event):
        button = self._button
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y

        self.logger.debug("motion event at %dx%d, button=%x" % (x, y, button))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('motion', button, data_x, data_y)

    def scroll_event(self, event):
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y
        dx, dy = event['deltaX'], event['deltaY']

        if (dx != 0 or dy != 0):
            # <= This browser gives us deltas for x and y
            # Synthesize this as a pan gesture event
            self.make_ui_callback('pan', 'start', 0, 0)
            self.make_ui_callback('pan', 'move', -dx, -dy)
            return self.make_ui_callback('pan', 'stop', 0, 0)

        # <= This code path should not be followed under normal
        # circumstances.
        # we leave it here in case we want to make the scroll
        # callback configurable in the future

        # TODO: calculate actual angle of direction
        if dy < 0:
            direction = 0.0  # up
        elif dy > 0:
            direction = 180.0  # down
        else:
            return False

        # 15 deg is standard 1-click turn for a wheel mouse
        num_deg = 15.0
        self.logger.debug("scroll deg=%f direction=%f" % (num_deg, direction))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('scroll', direction, num_deg, data_x,
                                     data_y)
Exemple #16
0
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)
Exemple #17
0
class Cipher(object):
    """
    Main cipher class

    Creates a list Character objects used to map the cipher into plaintext
    """
    cipher    = None
    alphabet  = None
    _vbox     = None
    _hbox     = None
    _label    = None
    _event    = None
    _html     = None
    _use      = 'cipher'
    _cindex   = 0
    _currentr = 0
    _currentc = 0

    def __init__(self, ciphertext, invert=False):
        self.cipher = []
        self.ciphertext = ciphertext.upper()

        # ------------------------------------------------------------
        # `lacunatext` is the full ciphertext, each character removed
        # from Z.
        # ------------------------------------------------------------
        self.lacunatext = ''.join(
            [helpers.distancefrom(c, 'Z') for c in self.ciphertext]
        )
        if invert:
            self.ciphertext = self.lacunatext
            self.lacunatext = ciphertext

        helpers.distance_calculator(self.ciphertext, self.lacunatext)

        # ------------------------------------------------------------
        # A polarity table is formed. This is used to help determine
        # the rules. As each character is found, the polarity of that
        # character changes
        # ------------------------------------------------------------
        self.alphabet = {
            character: False for character in helpers.alphabet
        }

        # ------------------------------------------------------------
        # Create an object for each character setting the value of
        # 'use_alt' to the current value of the boolean alphabet.
        # We then  invert the alphabet flag for the next occurance of
        # that character.
        # ------------------------------------------------------------
        for i, c, l in zip(range(1, self.length + 1), self.ciphertext, self.lacunatext):
            cipher_flag = self.alphabet[c] if c not in ['M', 'Z'] else True
            lacuna_flag = self.alphabet[c] if l not in ['M', 'Z'] else True
            self.cipher.append(
                Character(c, i, cipher_flag, self.ciphertext)
            )
            self.alphabet[c] = not self.alphabet[c]

        # Used for displaying the current position on the ciphergrid
        self._currentc = 'A'
        self._currentr = 0

    def setup_jupyter(self):
        """
        Sets up elements on the page for use with a Jupyter notebook.
        """
        self._label = Label('Move the cursor over the cell and use the left and right arrow keys to navigate')
        self._hbox = HBox()
        self._html = HTML('<h3>Label position?</h3>')

        self._inner = VBox()
        self._vbox = VBox([self._html, self._inner, self._label])
        self._event = Event(source=self._vbox, watched_events=['keydown'])
        self._event.on_dom_event(self.handle_event)
        return self

    @property
    def length(self):
        """ Return the length of the current cipher """
        return len(self.ciphertext)

    def intermediate(self, pos):
        """ Get the intermediate character from the cipher """
        return self[pos].decipher

    def __str__(self):
        return ''.join([str(c) for c in self])

    def __iter__(self):
        return getattr(self, self._use).__iter__()

    def __getitem__(self, key):
        return getattr(self, self._use).__getitem__(key)

    def __len__(self):
        return self.length

    @globalout.capture()
    def handle_event(self, event):
        """ Jupyter ipyevents binding code """
        if 'code' in event.keys():
            if event['shiftKey']:
                event['code'] = 'Shift+' + event['code']
            try:
                self.setposition(event['code'])
                self._draw()
            except IndexError:
                # If we're out of index, we're beyond the end of the cipher
                # simply call again to move to the bext row
                self.handle_event(event)

    def setposition(self, code):
        """
        Sets the current cipher position on the grids
        """
        pagesize = 10
        shiftpage = 5
        prevrow = ((26 - helpers.a2i(self._currentc)) + helpers.a2i(self._currentc))
        nextrow = helpers.a2i(self._currentc) + (26 - helpers.a2i(self._currentc))
        lastrow = (len(self) % 26) - nextrow if (len(self) % 26) - nextrow > 0 \
            else (len(self) - (26 * (len(self)  //  26)))

        primary = {
            'ArrowLeft':  self._cindex - 1 if self._cindex > 0 else len(self) - 1,
            'ArrowRight': self._cindex + 1 if self._cindex < len(self)-1 else 0,
            'PageDown': self._cindex + pagesize if (self._cindex + pagesize) < len(self)-1 \
                else 0 + ((self._cindex + pagesize) - len(self)),
            'PageUp': self._cindex - pagesize if (self._cindex - pagesize) >= 0 \
                else (len(self) - (pagesize - self._cindex)),
            'Shift+ArrowLeft': self._cindex - shiftpage if (self._cindex - shiftpage) >= 0 \
                else (len(self) - (shiftpage - self._cindex)),
            'Shift+ArrowRight': self._cindex + shiftpage if (self._cindex + shiftpage) < len(self)-1 \
                else 0 + ((self._cindex + shiftpage) - len(self)),
            'Home':  0,
            'End': len(self) - 1,
            'ArrowDown': (self._cindex + prevrow) if self._cindex + prevrow < len(self) \
                else helpers.a2i(self._currentc) - 1,
            # cant get this to work properly
            #'ArrowUp': (self._cindex - nextrow) if self._cindex - nextrow >= 0 else lastrow,
            'ArrowUp': helpers.a2i(self._currentc) - 1 + (
                (26 * (self._currentr - 1)) if self._currentr - 1 >= 0 else (26 * (len(self) // 26))
            )

        }
        self._cindex = primary[code] if code in primary.keys() else self._cindex
        self._currentr = self._cindex // 26
        self._currentc = helpers.i2a((self._cindex % 26) + 1)

    def _draw(self):
        """ Jupyter notebook code to draw widgets """
        left       = Output()
        right      = Output()
        properties = Output()
        conditions = Output()
        deciphered = Output()
        tablekey   = Output()

        tables = {
            True:  [],
            False: []
        }
        for key in self[self._cindex].cipher.keys():
            characters = self[self._cindex].cipher[key].get()
            for i, character in zip(range(len(characters)), characters):
                partial = Output()
                df = self[self._cindex].all_positions(helpers.i2a(character))
                style = df.style.set_caption(
                    '{} ({})'.format(character, helpers.i2a(character))
                ).set_table_attributes(
                    'style="font-size: 10px"'
                ).hide_index()
                if self[self._cindex].table == key and df.equals(self[self._cindex].all_positions()):
                    style.set_properties(**{'background-color': '#FF0000', 'color': '#FFFFFF'})

                with partial:
                    display.display(style)
                tables[key].append(partial)

        with properties:
            display.display(
                self[self._cindex].properties_frame
                    .style.set_caption('Properties')
                    .set_table_attributes(
                        'style="font-size: 10px"'
                    ).set_properties(
                        subset=['Value'],
                        **{'width': '120px'}
                    ).hide_index()
            )

        with conditions:
            df = self[self._cindex].condition_frame.reset_index()
            df.columns = ['', 'index', 'cipher', 'lacuna',]
            display.display(
                df.style.set_caption('Conditions')
                    .set_table_attributes(
                        'style="font-size: 10px"'
                    ).hide_index()
            )

        with deciphered:
            display.display(self.as_dataframe())

        with tablekey:
            display.display(self.table_key)

        with left:
            display.display(self[self._cindex].cipher[True].apply)
        with right:
            display.display(self[self._cindex].cipher[False].apply)

        subtables = VBox()
        subtables.children = [HBox(tables[True]), HBox(tables[False])]

        self._hbox.children = [left, right]
        self._inner.children = [
            self._hbox,
            HBox([VBox([properties, conditions]), subtables, VBox([deciphered, tablekey])]),
            globalout
        ]
        self._html.value = '<h3>Current character {} ({}), lacuna {} ({}) index {}, deciphered to {} algorithm {}</h3>'.format(
            self[self._cindex].character,
            self[self._cindex].cindex,
            self[self._cindex].lacuna,
            self[self._cindex].lindex,
            self._cindex + 1,
            str(self[self._cindex]),
            self[self._cindex].algorithm + 1
        )

    @property
    def table_key(self):
        key = pd.DataFrame([
            ['', 'Cipher character',],
            ['', 'Active character',],
            ['', 'Cipher lacuna',],
            ['', 'Active lacuna',],
            ['', 'Both match',],
            ['', 'Properties match',],
            ['', 'Contitions match',],
        ], columns=['Colour', 'Description'])
        return key.style.applymap(
                lambda _: 'background-color: #00FF00', pd.IndexSlice[0, 'Colour',]
            ).applymap(
                lambda _: 'background-color: #FF0000; color: #FFFFFF', pd.IndexSlice[1, 'Colour',]
            ).applymap(
                lambda _: 'background-color: #90EE90', pd.IndexSlice[2, 'Colour',]
            ).applymap(
                lambda _: 'background-color: #FBACA8', pd.IndexSlice[3, 'Colour',]
            ).applymap(
                lambda _: 'background-color: #EDC9AF', pd.IndexSlice[4, 'Colour',]
            ).applymap(
                lambda _: 'background-color: #D291BC', pd.IndexSlice[5, 'Colour',]
            ).applymap(
                lambda _: 'background-color: #85E3FF', pd.IndexSlice[6, 'Colour',]
            ).set_table_attributes(
                'style="font-size: 10px"'
            ).hide_index().set_caption('Key')


    def as_dataframe(self, ciphertext=False):
        """
        display the finished cipher in a dataframe
        """
        n = 26
        ciphertext = [str(i) for i in self] if not ciphertext else [i for i in ciphertext]
        df = pd.DataFrame(
            [self[i:i + n] for i in range(0, len(ciphertext), n)]
        )
        mask = df.applymap(lambda x: x is None)
        cols = df.columns[(mask).any()]
        for col in df[cols]:
            df.loc[mask[col], col] = ''
        df.columns = helpers.alphabet
        style =  df.style.hide_index().set_caption(
            'Deciphered plaintext'
        ).set_table_attributes(
            'style="font-size: 10px"'
        ).applymap(
            Highlighter(None, None).highlightr,
            subset=pd.IndexSlice[self._currentr, self._currentc]
        )

        for i in range(len(self)):
            if i == self._cindex:
                continue
            row = i // 26
            col = helpers.i2a((i % 26) + 1)
            properties_match = self[i].properties_table == self[self._cindex].properties_table
            conditions_match = self[i].condition_table == self[self._cindex].condition_table
            if properties_match and conditions_match:
                style = style.applymap(
                    Highlighter(None, None).highlights,
                    subset=pd.IndexSlice[row, col]
                )
            elif properties_match:
                style = style.applymap(
                    Highlighter(None, None).highlightl,
                    subset=pd.IndexSlice[row, col]
                )
            elif conditions_match:
                style = style.applymap(
                    Highlighter(None, None).highlightb,
                    subset=pd.IndexSlice[row, col]
                )
        return style

    def display(self, index=1):
        """
        Display a given character in a Jupyter cell
        """
        index = 1 if index == 0 else index
        display.clear_output(wait=True)
        if index is not None:
            self._cindex = (index - 1)
            self._currentc = helpers.i2a((self._cindex % 26)+1)
        self._currentr = self._cindex // 26
        self._draw()
        display.display(self._vbox)
Exemple #18
0
class ImageViewEvent(ImageViewJpw):

    def __init__(self, logger=None, rgbmap=None, settings=None):
        ImageViewJpw.__init__(self, logger=logger, rgbmap=rgbmap,
                              settings=settings)

        self._button = 0

        # maps EventListener events to callback handlers
        self._evt_dispatch = {
            'mousedown': self.button_press_event,
            'mouseup': self.button_release_event,
            'mousemove': self.motion_notify_event,
            'wheel': self.scroll_event,
            'mouseenter': self.enter_notify_event,
            'mouseleave': self.leave_notify_event,
            'keydown': self.key_press_event,
            'keyup': self.key_release_event,
        }

        # mapping from EventListener key events to ginga key events
        self._keytbl = {
            'shiftleft': 'shift_l',
            'shiftright': 'shift_r',
            'controlleft': 'control_l',
            'controlright': 'control_r',
            'altleft': 'alt_l',
            'altright': 'alt_r',
            'osleft': 'super_l',
            'osright': 'super_r',
            'contextmenu': 'menu_r',
            'backslash': 'backslash',
            'space': 'space',
            'escape': 'escape',
            'enter': 'return',
            'tab': 'tab',
            'arrowright': 'right',
            'arrowleft': 'left',
            'arrowup': 'up',
            'arrowdown': 'down',
            'pageup': 'page_up',
            'pagedown': 'page_down',
            'f1': 'f1',
            'f2': 'f2',
            'f3': 'f3',
            'f4': 'f4',
            'f5': 'f5',
            'f6': 'f6',
            'f7': 'f7',
            'f8': 'f8',
            'f9': 'f9',
            'f10': 'f10',
            'f11': 'f11',
            'f12': 'f12',
        }

        self._keytbl2 = {
            '`': 'backquote',
            '"': 'doublequote',
            "'": 'singlequote',
        }

        # Define cursors for pick and pan
        #hand = openHandCursor()
        hand = 'fleur'
        self.define_cursor('pan', hand)
        cross = 'cross'
        self.define_cursor('pick', cross)

        for name in ('motion', 'button-press', 'button-release',
                     'key-press', 'key-release', 'drag-drop',
                     'scroll', 'map', 'focus', 'enter', 'leave',
                     'pinch', 'rotate', 'pan', 'swipe', 'tap'):
            self.enable_callback(name)

    def set_widget(self, jp_imgw):
        """Call this method with the Jupyter image widget (image_w)
        that will be used.
        """
        super(ImageViewEvent, self).set_widget(jp_imgw)

        self.jp_evt = EventListener(source=jp_imgw)
        self.jp_evt.watched_events = [
            'keydown', 'keyup', 'mouseenter', 'mouseleave',
            'mousedown', 'mouseup', 'mousemove', 'wheel',
            'contextmenu'
        ]
        self.jp_evt.prevent_default_action = True

        self.jp_evt.on_dom_event(self._handle_event)
        self.logger.info("installed event handlers")

        return self.make_callback('map')

    def _handle_event(self, event):
        # TODO: need focus events and maybe a map event
        # TODO: Set up widget as a drag and drop destination
        evt_kind = event['type']
        handler = self._evt_dispatch.get(evt_kind, None)
        if handler is not None:
            return handler(event)
        return False

    def transkey(self, keycode, keyname=None):
        keycode = str(keycode).lower()
        if keyname is None:
            keyname = keycode
        self.logger.debug("key code in jupyter '%s'" % (keycode))
        res = self._keytbl.get(keycode, None)
        if res is None:
            res = self._keytbl2.get(keyname, keyname)
        return res

    def get_key_table(self):
        return self._keytbl

    def focus_event(self, event, has_focus):
        return self.make_callback('focus', has_focus)

    def enter_notify_event(self, event):
        enter_focus = self.t_.get('enter_focus', False)
        if enter_focus:
            # TODO: set focus on canvas
            pass
        return self.make_callback('enter')

    def leave_notify_event(self, event):
        self.logger.debug("leaving widget...")
        return self.make_callback('leave')

    def key_press_event(self, event):
        keyname = self.transkey(event['code'], keyname=event['key'])
        self.logger.debug("key press event, key=%s" % (keyname))
        return self.make_ui_callback('key-press', keyname)

    def key_release_event(self, event):
        keyname = self.transkey(event['code'], keyname=event['key'])
        self.logger.debug("key release event, key=%s" % (keyname))
        return self.make_ui_callback('key-release', keyname)

    def button_press_event(self, event):
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y

        button = 0
        button |= 0x1 << event['button']
        self._button = button
        self.logger.debug("button event at %dx%d, button=%x" % (x, y, button))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('button-press', button, data_x, data_y)

    def button_release_event(self, event):
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y

        button = 0
        button |= 0x1 << event['button']
        self._button = 0
        self.logger.debug("button release at %dx%d button=%x" % (x, y, button))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('button-release', button, data_x, data_y)

    def motion_notify_event(self, event):
        button = self._button
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y

        self.logger.debug("motion event at %dx%d, button=%x" % (x, y, button))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('motion', button, data_x, data_y)

    def scroll_event(self, event):
        x, y = event['dataX'], event['dataY']
        self.last_win_x, self.last_win_y = x, y
        dx, dy = event['deltaX'], event['deltaY']

        if (dx != 0 or dy != 0):
            # <= This browser gives us deltas for x and y
            # Synthesize this as a pan gesture event
            self.make_ui_callback('pan', 'start', 0, 0)
            self.make_ui_callback('pan', 'move', -dx, -dy)
            return self.make_ui_callback('pan', 'stop', 0, 0)

        # <= This code path should not be followed under normal
        # circumstances.
        # we leave it here in case we want to make the scroll
        # callback configurable in the future

        # TODO: calculate actual angle of direction
        if dy < 0:
            direction = 0.0   # up
        elif dy > 0:
            direction = 180.0   # down
        else:
            return False

        # 15 deg is standard 1-click turn for a wheel mouse
        num_deg = 15.0
        self.logger.debug("scroll deg=%f direction=%f" % (
            num_deg, direction))

        data_x, data_y = self.check_cursor_location()

        return self.make_ui_callback('scroll', direction, num_deg,
                                     data_x, data_y)
Exemple #19
0
def mouse_hover(widget, callback):
    e = Event(source=widget, watched_events=['mousemove'])
    e.on_dom_event(callback)
Exemple #20
0
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)
Exemple #21
0
def run():
    # Time to build the figure!

    fig, ax = plt.subplots(figsize=[9, 9])

    # Draw the stars.

    def scatter_stars():
        marker_size = (0.5 + limiting_magnitude - stars['magnitude']) ** 2.0

        mask = (
            (marker_size > 0.25)
            & (stars['x'] > -0.3)
            & (stars['x'] < 0.3)
            & (stars['y'] > -0.3)
            & (stars['y'] < 0.3)
        )

        #print('Number of stars:', mask.sum())

        #scatter.set_data(stars['x'][mask], stars['y'][mask])
        xy = np.array([stars['x'][mask], stars['y'][mask]])
        scatter.set_offsets(xy.T)

        s = marker_size[mask] #[bright_stars]]
        scatter.set_sizes(s)

    scatter = ax.scatter([], [], color='k')
    scatter_stars()

    # Finally, title the plot and set some final parameters.

    angle = np.pi - field_of_view_degrees / 360.0 * np.pi
    limit = np.sin(angle) / (1.0 - np.cos(angle))
    #print(limit)
    ax.set_xlim(-limit, limit)
    ax.set_ylim(-limit, limit)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)
    ax.set_aspect(1.0)
    ax.set_title('Stars')

    from ipywidgets import Label, HTML, HBox, Image, VBox, Box, HBox, interact
    from ipyevents import Event
    from IPython.display import display

    l = Label('Click or type on me!')
    l.layout.border = '2px solid red'

    h = HTML('Event info')
    d = Event(source=l, watched_events=['click', 'keydown', 'mouseenter'])

    def handle_event(event):
        lines = ['{}: {}'.format(k, v) for k, v in event.items()]
        content = '<br>'.join(lines)
        h.value = content

    d.on_dom_event(handle_event)

    #display(l, h)

    import ipywidgets as widgets

    def demo(i):
        field_of_view_degrees = 45.0 - i
        angle = np.pi - field_of_view_degrees / 360.0 * np.pi
        limit = np.sin(angle) / (1.0 - np.cos(angle))

        ax.set_xlim(-limit, limit)
        ax.set_ylim(-limit, limit)
        return fig

    #widgets.interact(demo, i = d)

    def callback(event):
        try:
            return callback2(event)
        except Exception as e:
            place.value = str(e)

    def callback2(event):
        nonlocal limit
        place.value = str(event['code'])
        code = event['code']
        if event['code'] == 'PageUp':
            limit /= 1.1
            ax.set_xlim(-limit, limit)
            ax.set_ylim(-limit, limit)
        elif event['code'] == 'PageDown':
            limit *= 1.1
            ax.set_xlim(-limit, limit)
            ax.set_ylim(-limit, limit)
        elif code.startswith('Arrow'):
            if code == 'ArrowUp':
                set_center(0, 0.5)
            elif code == 'ArrowDown':
                set_center(0, -0.5)
            elif code == 'ArrowLeft':
                set_center(0.1, 0)
            elif code == 'ArrowRight':
                set_center(-0.1, 0)
            set_positions()
            scatter_stars()
            place.value = 'Surv'

        #PageUp: bigger
        #    PageDown: smaller
        redraw()

    from io import BytesIO

    def redraw():
        io = BytesIO()
        plt.savefig(io, format="png")
        #plt.close()
        plt.ioff()
        image.value = io.getvalue()

    image = Image(format='png')
    redraw()

    # The layout bits below make sure the image display looks the same in lab and classic notebook 
    image.layout.max_width = '4in'
    image.layout.height = 'auto'

    im_events = Event()
    im_events.source = image
    im_events.watched_events = ['keydown']
    im_events.on_dom_event(callback)

    place = HTML('Test')
    return VBox([place, image])
class ViewInteractiveWidget(Canvas):
    """Remote controller for Slicer viewers.
  :param layoutLabel: specify view by label (displayed in the view's header in the layout, such as R, Y, G, 1)
  :param renderView: specify view by renderView object (ctkVTKRenderView).
  """
    def __init__(self, layoutLabel=None, renderView=None, **kwargs):
        from ipyevents import Event
        #import time

        super().__init__(**kwargs)

        # Find renderView from layoutLabel
        layoutManager = slicer.app.layoutManager()
        # Find it among 3D views
        if not renderView:
            for threeDViewIndex in range(layoutManager.threeDViewCount):
                threeDWidget = layoutManager.threeDWidget(threeDViewIndex)
                if (threeDWidget.mrmlViewNode().GetLayoutLabel()
                        == layoutLabel) or (layoutLabel is None):
                    renderView = threeDWidget.threeDView()
                    break
        # Find it among slice views
        if not renderView:
            sliceViewNames = layoutManager.sliceViewNames()
            for sliceViewName in sliceViewNames:
                sliceWidget = layoutManager.sliceWidget(sliceViewName)
                if (sliceWidget.mrmlSliceNode().GetLayoutLabel()
                        == layoutLabel) or (layoutLabel is None):
                    renderView = sliceWidget.sliceView()
                    break

        if not renderView:
            if layoutLabel:
                raise ValueError(
                    "renderView is not specified and view cannot be found by layout label "
                    + layoutLabel)
            else:
                raise ValueError("renderView is not specified")

        self.renderView = renderView

        # Frame rate (1/renderDelay)
        self.lastRenderTime = 0
        self.quickRenderDelaySec = 0.1
        self.quickRenderDelaySecRange = [0.02, 2.0]
        self.adaptiveRenderDelay = True
        self.lastMouseMoveEvent = None

        # Quality vs performance
        self.compressionQuality = 50
        self.trackMouseMove = False  # refresh if mouse is just moving (not dragging)

        self.messageTimestampOffset = None

        # If not receiving new rendering request for 10ms then a render is requested
        self.fullRenderRequestTimer = qt.QTimer()
        self.fullRenderRequestTimer.setSingleShot(True)
        self.fullRenderRequestTimer.setInterval(500)
        self.fullRenderRequestTimer.connect('timeout()', self.fullRender)

        # If not receiving new rendering request for 10ms then a render is requested
        self.quickRenderRequestTimer = qt.QTimer()
        self.quickRenderRequestTimer.setSingleShot(True)
        self.quickRenderRequestTimer.setInterval(self.quickRenderDelaySec *
                                                 1000)
        self.quickRenderRequestTimer.connect('timeout()', self.quickRender)

        # Get image size
        image = self.getImage()
        self.width = image.width
        self.height = image.height
        self.draw_image(image)

        self.interactor = self.renderView.interactorStyle().GetInteractor()

        self.dragging = False

        self.interactionEvents = Event()
        self.interactionEvents.source = self
        self.interactionEvents.watched_events = [
            'dragstart',
            'mouseenter',
            'mouseleave',
            'mousedown',
            'mouseup',
            'mousemove',
            #'wheel',  # commented out so that user can scroll through the notebook using mousewheel
            'keyup',
            'keydown',
            'contextmenu'  # prevent context menu from appearing on right-click
        ]
        #self.interactionEvents.msg_throttle = 1  # does not seem to have effect
        self.interactionEvents.prevent_default_action = True
        self.interactionEvents.on_dom_event(self.handleInteractionEvent)

        self.keyToSym = {
            'ArrowLeft': 'Left',
            'ArrowRight': 'Right',
            'ArrowUp': 'Up',
            'ArrowDown': 'Down',
            'BackSpace': 'BackSpace',
            'Tab': 'Tab',
            'Enter': 'Return',
            #'Shift': 'Shift_L',
            #'Control': 'Control_L',
            #'Alt': 'Alt_L',
            'CapsLock': 'Caps_Lock',
            'Escape': 'Escape',
            ' ': 'space',
            'PageUp': 'Prior',
            'PageDown': 'Next',
            'Home': 'Home',
            'End': 'End',
            'Delete': 'Delete',
            'Insert': 'Insert',
            '*': 'asterisk',
            '+': 'plus',
            '|': 'bar',
            '-': 'minus',
            '.': 'period',
            '/': 'slash',
            'F1': 'F1',
            'F2': 'F2',
            'F3': 'F3',
            'F4': 'F4',
            'F5': 'F5',
            'F6': 'F6',
            'F7': 'F7',
            'F8': 'F8',
            'F9': 'F9',
            'F10': 'F10',
            'F11': 'F11',
            'F12': 'F12'
        }

        # Errors are not displayed when a widget is displayed,
        # this variable can be used to retrieve error messages
        self.error = None

        # Enable logging of UI events
        self.logEvents = False
        self.loggedEvents = []
        self.elapsedTimes = []
        self.ageOfProcessedMessages = []

    def setQuickRenderDelay(self, delaySec):
        """Delay this much after a view update before sending a low-resolution update."""
        if delaySec < self.quickRenderDelaySecRange[0]:
            delaySec = self.quickRenderDelaySecRange[0]
        elif delaySec > self.quickRenderDelaySecRange[1]:
            delaySec = self.quickRenderDelaySecRange[1]
        self.quickRenderDelaySec = delaySec
        self.quickRenderRequestTimer.setInterval(self.quickRenderDelaySec *
                                                 1000)

    def setFullRenderDelay(self, delaySec):
        """Delay this much after a view update before sending a full-resolution update."""
        self.fullRenderRequestTimer.setInterval(delaySec)

    def getImage(self, compress=True, forceRender=True):
        """Retrieve an image from the view."""
        from ipywidgets import Image
        slicer.app.processEvents()
        if forceRender:
            self.renderView.forceRender()
        screenshot = self.renderView.grab()
        bArray = qt.QByteArray()
        buffer = qt.QBuffer(bArray)
        buffer.open(qt.QIODevice.WriteOnly)
        if compress:
            screenshot.save(buffer, "JPG", self.compressionQuality)
        else:
            screenshot.save(buffer, "PNG")
        return Image(value=bArray.data(),
                     width=screenshot.width(),
                     height=screenshot.height())

    def fullRender(self):
        """Perform a full render now."""
        try:
            import time
            self.fullRenderRequestTimer.stop()
            self.quickRenderRequestTimer.stop()
            self.draw_image(self.getImage(compress=False, forceRender=True))
            self.lastRenderTime = time.time()
        except Exception as e:
            self.error = str(e)

    def sendPendingMouseMoveEvent(self):
        if self.lastMouseMoveEvent is not None:
            self.updateInteractorEventData(self.lastMouseMoveEvent)
            self.interactor.MouseMoveEvent()
            self.lastMouseMoveEvent = None

    def quickRender(self):
        """Perform a quick render now."""
        try:
            import time
            self.fullRenderRequestTimer.stop()
            self.quickRenderRequestTimer.stop()
            self.sendPendingMouseMoveEvent()
            self.draw_image(self.getImage(compress=True, forceRender=False))
            self.fullRenderRequestTimer.start()
            if self.logEvents:
                self.elapsedTimes.append(time.time() - self.lastRenderTime)
            self.lastRenderTime = time.time()
        except Exception as e:
            self.error = str(e)

    def updateInteractorEventData(self, event):
        try:
            if event['event'] == 'keydown' or event['event'] == 'keyup':
                key = event['key']
                sym = self.keyToSym[key] if key in self.keyToSym.keys(
                ) else key
                self.interactor.SetKeySym(sym)
                if len(key) == 1:
                    self.interactor.SetKeyCode(key)
                self.interactor.SetRepeatCount(1)
            else:
                self.interactor.SetEventPosition(
                    event['offsetX'], self.height - event['offsetY'])
            self.interactor.SetShiftKey(event['shiftKey'])
            self.interactor.SetControlKey(event['ctrlKey'])
            self.interactor.SetAltKey(event['altKey'])
        except Exception as e:
            self.error = str(e)

    def handleInteractionEvent(self, event):
        try:
            if self.logEvents:
                self.loggedEvents.append(event)
            if event['event'] == 'mousemove':
                import time
                if self.messageTimestampOffset is None:
                    self.messageTimestampOffset = time.time(
                    ) - event['timeStamp'] * 0.001
                self.lastMouseMoveEvent = event
                if not self.dragging and not self.trackMouseMove:
                    return
                if self.adaptiveRenderDelay:
                    ageOfProcessedMessage = time.time() - (
                        event['timeStamp'] * 0.001 +
                        self.messageTimestampOffset)
                    if ageOfProcessedMessage > 1.5 * self.quickRenderDelaySec:
                        # we are falling behind, try to render less frequently
                        self.setQuickRenderDelay(self.quickRenderDelaySec *
                                                 1.05)
                    elif ageOfProcessedMessage < 0.5 * self.quickRenderDelaySec:
                        # we can keep up with events, try to render more frequently
                        self.setQuickRenderDelay(self.quickRenderDelaySec /
                                                 1.05)
                    if self.logEvents:
                        self.ageOfProcessedMessages.append(
                            [ageOfProcessedMessage, self.quickRenderDelaySec])
                # We need to render something now it no rendering since self.quickRenderDelaySec
                if time.time(
                ) - self.lastRenderTime > self.quickRenderDelaySec:
                    self.quickRender()
                else:
                    self.quickRenderRequestTimer.start()
            elif event['event'] == 'mouseenter':
                self.updateInteractorEventData(event)
                self.interactor.EnterEvent()
                self.lastMouseMoveEvent = None
                self.quickRenderRequestTimer.start()
            elif event['event'] == 'mouseleave':
                self.updateInteractorEventData(event)
                self.interactor.LeaveEvent()
                self.lastMouseMoveEvent = None
                self.quickRenderRequestTimer.start()
            elif event['event'] == 'mousedown':
                self.dragging = True
                self.sendPendingMouseMoveEvent()
                self.updateInteractorEventData(event)
                if event['button'] == 0:
                    self.interactor.LeftButtonPressEvent()
                elif event['button'] == 2:
                    self.interactor.RightButtonPressEvent()
                elif event['button'] == 1:
                    self.interactor.MiddleButtonPressEvent()
                self.fullRender()
            elif event['event'] == 'mouseup':
                self.sendPendingMouseMoveEvent()
                self.updateInteractorEventData(event)
                if event['button'] == 0:
                    self.interactor.LeftButtonReleaseEvent()
                elif event['button'] == 2:
                    self.interactor.RightButtonReleaseEvent()
                elif event['button'] == 1:
                    self.interactor.MiddleButtonReleaseEvent()
                self.dragging = False
                self.fullRender()
            elif event['event'] == 'keydown':
                self.sendPendingMouseMoveEvent()
                self.updateInteractorEventData(event)
                self.interactor.KeyPressEvent()
                self.interactor.CharEvent()
                if event['key'] != 'Shift' and event[
                        'key'] != 'Control' and event['key'] != 'Alt':
                    self.fullRender()
            elif event['event'] == 'keyup':
                self.sendPendingMouseMoveEvent()
                self.updateInteractorEventData(event)
                self.interactor.KeyReleaseEvent()
                if event['key'] != 'Shift' and event[
                        'key'] != 'Control' and event['key'] != 'Alt':
                    self.fullRender()
        except Exception as e:
            self.error = str(e)
Exemple #23
0
class ImageLabeler:
    def __init__(self, label_name):
        self.clix = 0  # current landmark index
        self.length = len(db)
        self.n_panels = 5
        self.panels = [ImageAndLabel() for _ in range(self.n_panels)]
        self.label_name = label_name
        self.landmarks = Landmarks({k: None for k in db.keys()})

        self.c = widgets.HTML('Click or type on me!')
        button = widgets.Button(description="Save",
                                layout=widgets.Layout(width='auto'))
        button.on_click(self.save)
        w = widgets.HBox([*self.panels])
        w = widgets.VBox([w, self.c, button])
        self.widget = w

        self.d = Event(source=self.widget, watched_events=['keydown'])
        self.d.on_dom_event(self.handle_event)

        self.target_label = 'q'

        self.render()
        display(self.widget)

    def save(self, _):
        db.save()

    def render(self):
        self.update_panels()
        self.c.value = '<pre style="line-height: 12px;">' + db.vc().to_string(
        ).replace('\n', '\n') + '</pre>'

    def update_panels(self):
        for pix in range(self.n_panels):
            self.update_panel(pix, self.clix + pix)

    def update_panel(self, pix, lix):
        if not 0 <= lix < len(self.landmarks):
            self.panels[pix].clear()
            return
        coord = self.landmarks.coordinates()[lix]
        is_target_label = self.landmarks[coord]  # T, F or None
        data = lego_images.get_crop(coord)
        if is_target_label:
            yn, color = 'yes', 'green'
        elif is_target_label is None:
            yn, color = '?', 'grey'
        else:
            yn, color = 'no', 'red'
        ch = 'hjkl;'[pix]

        style = f'font-size: x-large; color: {color};'
        style2 = f'font-family: mono; color: grey;'
        caption = [
            f'<span style="{style}">{yn}</span>',
            f'({lix + 1} of {self.length})',
            f'<span style="{style2}">{ch}</span>',
        ]
        caption = '<br>'.join(caption)
        caption = f'<div style="text-align: center;">{caption}</span>'

        self.panels[pix].update(data, caption)
        self.panels[pix].layout.border = f'5px solid {color}'

    def toggle_label(self, clix):
        if not 0 <= clix < len(self.landmarks):
            return
        coord = self.landmarks.coordinates()[clix]
        current = self.landmarks[coord]
        new = True if current is None else not current
        self.landmarks[coord] = new
        db.update(coord, new)

    def falsify_if_unset(self, clix):
        if not 0 <= clix < len(self.landmarks):
            return
        coord = self.landmarks.coordinates()[clix]
        current = self.landmarks[coord]
        new = False if current is None else current
        self.landmarks[coord] = new
        db.update(coord, new)

    def handle_event(self, event):
        key = event['key']
        if key in 'hjkl;':
            pix = 'hjkl;'.index(key)
            self.toggle_label(self.clix + pix)
        elif key in ['[', 'ArrowLeft']:
            self.clix -= self.n_panels
        elif key in [']', 'ArrowRight']:
            self.clix += self.n_panels
        elif key in ['Enter']:
            for i in range(self.n_panels):
                self.falsify_if_unset(self.clix + i)
            self.clix += self.n_panels
        elif key == 'ArrowUp':
            self.padding += 5
        elif key == 'ArrowDown':
            self.padding -= 5
        else:
            self.c.value = f"You pressed: {key}"
            return
        self.clix = max(self.clix, 0)
        max_ix = len(self.landmarks) // self.n_panels * self.n_panels
        self.clix = min(max_ix, self.clix)
        self.render()