Source code for tangram.widget.image_viewer

# Copyright 2017 The Tangram Developers. See the AUTHORS file at the
# top-level directory of this distribution and at
# https://github.com/renatoGarcia/tangram/blob/master/AUTHORS.
#
# This file is part of Tangram.
#
# Tangram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Tangram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Tangram in the COPYING and COPYING.LESSER files.
# If not, see <http://www.gnu.org/licenses/>.

from typing import Union

from gi.repository import Gtk, Clutter

from .._base import Base
from .._gui_thread import on_gtk_thread
from ._zoomable_canvas import _ZoomableCanvas


class _SceneViewer(Gtk.Grid):
    def __init__(self):
        super().__init__()
        self.set_orientation(Gtk.Orientation.VERTICAL)

        self._scrolled_window = Gtk.ScrolledWindow()
        # self._scrolled_window.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.AUTOMATIC)
        self._canvas = _ZoomableCanvas()
        self._scrolled_window.add(self._canvas)

        self._build_message_bar()

        self.add(self._scrolled_window)
        self.add(self._message_bar)

        self._scene = Clutter.ScrollActor()
        self._scene_signal_connections = []

        self._canvas.connect('zoom-event', self._on_zoom_event)

    def _build_message_bar(self):
        self._message_bar = Gtk.Grid()
        self._message_bar.set_orientation(Gtk.Orientation.HORIZONTAL)

        self._position_format = "({:.2f}, {:.2f})"
        self._empty_position = "(-, -)"
        self._position_label = Gtk.Label(self._empty_position)

        self._color_format = "(<span foreground='red'>{:03}</span>,"\
                             " <span foreground='green'>{:03}</span>,"\
                             " <span foreground='blue'>{:03}</span>)"
        self._empty_color = "(<span foreground='red'> - </span>,"\
                            " <span foreground='green'> - </span>,"\
                            " <span foreground='blue'> - </span>)"
        self._color_label = Gtk.Label(self._empty_color, use_markup=True)

        self._zoom_format = "Zoom: {:.2f}"
        self._zoom_label = Gtk.Label("Zoom: 1.0")

        self._message_bar.add(self._position_label)
        self._message_bar.add(self._color_label)
        self._message_bar.add(self._zoom_label)

    def set_scene(self, scene: Union[Clutter.ScrollActor, None]):
        for signal_id in self._scene_signal_connections:
            image = self._scene.get_first_child()
            image.disconnect(signal_id)
        self._scene_signal_connections.clear()

        if scene is None:
            self._scene = Clutter.ScrollActor()
        else:
            # Only the image is allowed as root node children.
            assert scene.get_n_children() == 1

            image = scene.get_first_child()
            self._scene_signal_connections.append(
                image.connect('motion-event', self._on_motion_event)
            )
            self._scene_signal_connections.append(
                image.connect('leave-event', self._on_leave_event)
            )
            self._scene = scene

        self._canvas.set_scene(self._scene)

    def _on_motion_event(self, actor, event):
        _, x, y = actor.transform_stage_point(event.x, event.y)

        r, g, b = actor._ndarray[int(y), int(x)]
        self._color_label.set_markup(self._color_format.format(r, g, b))

        self._position_label.set_text(self._position_format.format(x, y))

        return False

    def _on_leave_event(self, actor, event):
        self._position_label.set_text(self._empty_position)
        self._color_label.set_markup(self._empty_color)

        return False

    def _on_zoom_event(self, widget, zoom):
        self._zoom_label.set_text(self._zoom_format.format(zoom))


[docs]class ImageViewer(Base):
[docs] @on_gtk_thread def __init__(self, image=None, **kwargs): super().__init__(**kwargs) self._gtk_obj = _SceneViewer() self._image = image if self._image: self._gtk_obj.set_scene(self._image._root_actor)
@on_gtk_thread def set_image(self, image): self._image = image if self._image is None: self._gtk_obj.set_scene(None) else: self._gtk_obj.set_scene(self._image._root_actor)