コード例 #1
0
ファイル: galrywidget.py プロジェクト: DBGray/galry
    def __init__(self, format=None, autosave=None, getfocus=True, **kwargs):
        """Constructor. Call `initialize` and initialize the companion classes
        as well."""
        if format is not None:
            super(GalryWidget, self).__init__(format)
        else:
            super(GalryWidget, self).__init__()

        self.initialized = False
        self.just_initialized = False

        # background color as a 4-tuple (R,G,B,A)
        self.bgcolor = (0, 0, 0, 0)
        self.autosave = None

        # default window size
        # self.width, self.height = 600, 600

        # FPS counter, used for debugging
        self.fps_counter = FpsCounter()
        self.display_fps = DISPLAY_FPS
        self.activate3D = None

        # widget creation parameters
        self.bindings = None
        self.companion_classes_initialized = False

        # constrain width/height ratio when resizing of zooming
        self.constrain_ratio = False
        self.constrain_navigation = False
        self.activate_help = True
        self.activate_grid = False

        # Capture keyboard events.
        if getfocus:
            self.setFocusPolicy(Qt.WheelFocus)

        # Capture mouse events.
        self.setMouseTracking(True)

        # Initialize the objects providing the core features of the widget.
        self.user_action_generator = UserActionGenerator()

        self.is_fullscreen = False

        self.events_to_signals = {}

        # keyword arguments without "_manager" => passed to initialize
        self.initialize(**kwargs)

        # initialize companion classes if it has not been done in initialize
        if not self.companion_classes_initialized:
            self.initialize_companion_classes()
        self.initialize_bindings()

        # update rendering options
        self.paint_manager.set_rendering_options(activate3D=self.activate3D, constrain_ratio=self.constrain_ratio)

        self.autosave = autosave
コード例 #2
0
ファイル: galrywidget.py プロジェクト: ValrynMilly/galry
class GalryWidget(QGLWidget):
    """Efficient interactive 2D visualization widget.
    
    This QT widget is based on OpenGL and depends on both PyQT (or PySide)
    and PyOpenGL. It implements low-level mechanisms for interaction processing
    and acts as a glue between the different managers (PaintManager, 
    BindingManager, InteractionManager).
    
    """

    w = 600.0
    h = 600.0

    # Initialization methods
    # ----------------------
    def __init__(self, format=None, autosave=None, getfocus=True, **kwargs):
        """Constructor. Call `initialize` and initialize the companion classes
        as well."""
        if format is not None:
            super(GalryWidget, self).__init__(format)
        else:
            super(GalryWidget, self).__init__()

        self.initialized = False
        self.just_initialized = False

        self.i = 0

        # background color as a 4-tuple (R,G,B,A)
        self.bgcolor = (0, 0, 0, 0)
        self.autosave = None

        # default window size
        # self.width, self.height = 600, 600

        # FPS counter, used for debugging
        self.fps_counter = FpsCounter()
        self.display_fps = DISPLAY_FPS
        self.activate3D = None

        # widget creation parameters
        self.bindings = None
        self.companion_classes_initialized = False

        # constrain width/height ratio when resizing of zooming
        self.constrain_ratio = False
        self.constrain_navigation = False
        self.momentum = False
        self.activate_help = True
        self.activate_grid = False
        self.block_refresh = False

        # Capture keyboard events.
        if getfocus:
            self.setFocusPolicy(Qt.WheelFocus)

        # Capture mouse events.
        self.setMouseTracking(True)

        # Capture touch events.
        self.setAcceptTouchEvents = True
        self.grabGesture(QtCore.Qt.PinchGesture)
        self.mouse_blocked = False  # True during a pinch gesture

        # Initialize the objects providing the core features of the widget.
        self.user_action_generator = UserActionGenerator()

        self.is_fullscreen = False

        self.events_to_signals = {}

        # keyword arguments without "_manager" => passed to initialize
        self.initialize(**kwargs)

        # initialize companion classes if it has not been done in initialize
        if not self.companion_classes_initialized:
            self.initialize_companion_classes()
        self.initialize_bindings()

        # update rendering options
        self.paint_manager.set_rendering_options(activate3D=self.activate3D, constrain_ratio=self.constrain_ratio)

        self.autosave = autosave

    def set_bindings(self, *bindings):
        """Set the interaction mode by specifying the binding object.
        
        Several binding objects can be given for the binding manager, such that
        the first one is the currently active one.
        
        Arguments:
          * bindings: a list of classes instances deriving from
            Bindings.
            
        """
        bindings = list(bindings)
        if not bindings:
            bindings = [PlotBindings()]
        # if type(bindings) is not list and type(bindings) is not tuple:
        # bindings = [bindings]
        # if binding is a class, try instanciating it
        for i in xrange(len(bindings)):
            if not isinstance(bindings[i], Bindings):
                bindings[i] = bindings[i]()
        self.bindings = bindings

    def set_companion_classes(self, **kwargs):
        """Set specified companion classes, unspecified ones are set to
        default classes.
        
        Arguments:
          * **kwargs: the naming convention is: `paint_manager=PaintManager`.
            The key `paint_manager` is the name the manager is accessed from 
            this widget and from all other companion classes. The value
            is the name of the class, it should end with `Manager`.
        
        """
        if not hasattr(self, "companion_classes"):
            self.companion_classes = {}

        self.companion_classes.update(kwargs)

        # default companion classes
        self.companion_classes.update(
            [(k, v) for k, v in DEFAULT_MANAGERS.iteritems() if k not in self.companion_classes]
        )

    def initialize_bindings(self):
        """Initialize the interaction bindings."""
        if self.bindings is None:
            self.set_bindings()
        self.binding_manager.add(*self.bindings)

    def initialize_companion_classes(self):
        """Initialize companion classes."""
        # default companion classes
        if not getattr(self, "companion_classes", None):
            self.set_companion_classes()

        # create the managers
        for key, val in self.companion_classes.iteritems():
            log_debug("Initializing '%s'" % key)
            obj = val(self)
            setattr(self, key, obj)

        # link all managers
        for key, val in self.companion_classes.iteritems():
            for child_key, child_val in self.companion_classes.iteritems():
                # no self-reference
                if child_key == key:
                    continue
                obj = getattr(self, key)
                setattr(obj, child_key, getattr(self, child_key))

        self.interaction_manager.constrain_navigation = self.constrain_navigation
        self.companion_classes_initialized = True

    def initialize(self, **kwargs):
        """Initialize the widget.
        
        Parameters such as bindings, companion_classes can be
        set here, by overriding this method. If initializations must be done
        after companion classes instanciation, then
        self.initialize_companion_classes can be called here.
        Otherwise, it will be called automatically after initialize().
        
        """
        pass

    def clear(self):
        """Clear the view."""
        self.paint_manager.reset()

    def reinit(self):
        """Reinitialize OpenGL.
        
        The clear method should be called before.
        
        """
        self.initializeGL()
        self.resizeGL(self.w, self.h)
        self.updateGL()

    # OpenGL widget methods
    # ---------------------
    def initializeGL(self):
        """Initialize OpenGL parameters."""
        self.paint_manager.initializeGL()
        self.initialized = True
        self.just_initialized = True

    def paintGL(self):
        """Paint the scene.
        
        Called as soon as the window needs to be painted (e.g. call to 
        `updateGL()`).
        
        This method calls the `paint_all` method of the PaintManager.
        
        """
        if self.just_initialized:
            self.process_interaction("Initialize", do_update=False)
        # paint fps
        if self.display_fps:
            self.paint_fps()
        # paint everything
        self.paint_manager.paintGL()
        # compute FPS
        self.fps_counter.tick()
        if self.autosave:
            if "%" in self.autosave:
                autosave = self.autosave % self.i
            else:
                autosave = self.autosave
            self.save_image(autosave, update=False)
        self.just_initialized = False
        self.i += 1

    def paint_fps(self):
        """Display the FPS on the top-left of the screen."""
        self.paint_manager.update_fps(int(self.fps_counter.get_fps()))

    def resizeGL(self, width, height):
        self.w, self.h = width, height
        self.paint_manager.resizeGL(width, height)

    def sizeHint(self):
        return QtCore.QSize(self.w, self.h)

    # Event methods
    # -------------
    def event(self, e):
        r = super(GalryWidget, self).event(e)
        if e.type() == QtCore.QEvent.Gesture:
            e.accept()
            gesture = e.gesture(QtCore.Qt.PinchGesture)
            self.pinchEvent(gesture)
            if gesture.state() == Qt.GestureStarted:
                self.mouse_blocked = True
            elif gesture.state() == Qt.GestureFinished:
                self.mouse_blocked = False
            return False
        return r

    def pinchEvent(self, e):
        self.user_action_generator.pinchEvent(e)
        self.process_interaction()

    def mousePressEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mousePressEvent(e)
        self.process_interaction()

    def mouseReleaseEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mouseReleaseEvent(e)
        self.process_interaction()

    def mouseDoubleClickEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mouseDoubleClickEvent(e)
        self.process_interaction()

    def mouseMoveEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mouseMoveEvent(e)
        self.process_interaction()

    def keyPressEvent(self, e):
        self.user_action_generator.keyPressEvent(e)
        self.process_interaction()
        # Close the application when pressing Q
        if e.key() == QtCore.Qt.Key_Q:
            if hasattr(self, "window"):
                self.close_widget()

    def keyReleaseEvent(self, e):
        self.user_action_generator.keyReleaseEvent(e)

    def wheelEvent(self, e):
        self.user_action_generator.wheelEvent(e)
        self.process_interaction()

    def reset_action_generator(self):
        self.user_action_generator.reset()

    def leaveEvent(self, e):
        self.process_interaction(None)

    # Normalization methods
    # ---------------------
    def normalize_position(self, x, y):
        """Window coordinates ==> world coordinates."""
        if not hasattr(self.paint_manager, "renderer"):
            return (0, 0)
        vx, vy = self.paint_manager.renderer.viewport
        x = -vx + 2 * vx * x / float(self.w)
        y = -(-vy + 2 * vy * y / float(self.h))
        return x, y

    def normalize_diff_position(self, x, y):
        """Normalize the coordinates of a difference vector between two
        points.
        """
        if not hasattr(self.paint_manager, "renderer"):
            return (0, 0)
        vx, vy = self.paint_manager.renderer.viewport
        x = 2 * vx * x / float(self.w)
        y = -2 * vy * y / float(self.h)
        return x, y

    def normalize_action_parameters(self, parameters):
        """Normalize points in the action parameters object in the window
        coordinate system.
        
        Arguments:
          * parameters: the action parameters object, containing all
            variables related to user actions.
            
        Returns:
           * parameters: the updated parameters object with normalized
             coordinates.
             
        """
        parameters["mouse_position"] = self.normalize_position(*parameters["mouse_position"])
        parameters["mouse_position_diff"] = self.normalize_diff_position(*parameters["mouse_position_diff"])
        parameters["mouse_press_position"] = self.normalize_position(*parameters["mouse_press_position"])
        parameters["pinch_position"] = self.normalize_position(*parameters["pinch_position"])
        parameters["pinch_start_position"] = self.normalize_position(*parameters["pinch_start_position"])
        return parameters

    # Signal methods
    # --------------
    def connect_events(self, arg1, arg2):
        """Makes a connection between a QT signal and an interaction event.
        
        The signal parameters must correspond to the event parameters.
        
        Arguments:
          * arg1: a QT bound signal or an interaction event.
          * arg2: an interaction event or a QT bound signal.
        
        """
        if type(arg1) == str:
            self.connect_event_to_signal(arg1, arg2)
        elif type(arg2) == str:
            self.connect_signal_to_event(arg1, arg2)

    def connect_signal_to_event(self, signal, event):
        """Connect a QT signal to an interaction event.
        
        The event parameters correspond to the signal parameters.
        
        Arguments:
          * signal: a QT signal.
          * event: an InteractionEvent string.
        
        """
        if signal is None:
            raise Exception("The signal %s is not defined" % signal)
        slot = lambda *args, **kwargs: self.process_interaction(event, args, **kwargs)
        signal.connect(slot)

    def connect_event_to_signal(self, event, signal):
        """Connect an interaction event to a QT signal.
        
        The event parameters correspond to the signal parameters.
        
        Arguments:
          * event: an InteractionEvent string.
          * signal: a QT signal.
        
        """
        self.events_to_signals[event] = signal

    # Binding mode methods
    # --------------------
    def switch_interaction_mode(self):
        """Switch the interaction mode."""
        binding = self.binding_manager.switch()
        # set base cursor
        # self.interaction_manager.base_cursor = binding.base_cursor
        return binding

    def set_interaction_mode(self, mode):
        """Set the interaction mode.
        
        Arguments:
          * mode: either a class deriving from `Bindings` and which has been
            specified in `set_bindings`, or directly a `Bindings` instance.
        
        """
        binding = self.binding_manager.set(mode)
        # set base cursor
        # self.interaction_manager.base_cursor = binding.base_cursor
        return binding

    # Interaction methods
    # -------------------
    def get_current_action(self):
        """Return the current user action with the action parameters."""
        # get current action
        action = self.user_action_generator.action

        # get current key if the action was KeyPress
        key = self.user_action_generator.key

        # get key modifier
        key_modifier = self.user_action_generator.key_modifier

        # retrieve action parameters and normalize using the window size
        parameters = self.normalize_action_parameters(self.user_action_generator.get_action_parameters())
        return action, key, key_modifier, parameters

    def get_current_event(self):
        """Return the current interaction event corresponding to the current
        user action."""
        # get the current interaction mode
        binding = self.binding_manager.get()

        # get current user action
        action, key, key_modifier, parameters = self.get_current_action()

        # get the associated interaction event
        event, param_getter = binding.get(action, key=key, key_modifier=key_modifier)

        # get the parameter object by calling the param getter
        if param_getter is not None and parameters is not None:
            args = param_getter(parameters)
        else:
            args = None

        return event, args

    def set_current_cursor(self):
        cursor = self.interaction_manager.get_cursor()
        # if no cursor set, then use the default one in the current binding
        # mode
        if cursor is None:
            cursor = self.binding_manager.get().get_base_cursor()
        qcursor = get_cursor(cursor)
        if qcursor:
            self.setCursor(qcursor)

    def process_interaction(self, event=None, args=None, do_update=None):
        """Process user interaction.
        
        This method is called after each user action (mouse, keyboard...).
        It finds the right action associated to the command, then the event 
        associated to that action.
        
        Arguments:
          * event=None: if None, the current event associated to the current
            user action is retrieved. Otherwise, an event can be directly
            passed here to force the trigger of any interaction event.
          * args=None: the arguments of the event if event is not None.
        
        """
        if event is None:
            # get current event from current user action
            event, args = self.get_current_event()

        if event == "Animate" and self.block_refresh:
            return

        prev_event = self.interaction_manager.prev_event

        # handle interaction mode change
        if event == "SwitchInteractionMode":
            binding = self.switch_interaction_mode()
            log_debug("Switching interaction mode to %s." % binding.__class__.__name__)

        # process the interaction event
        self.interaction_manager.process_event(event, args)

        # raise a signal if there is one associated to the current event
        if event in self.events_to_signals:
            self.events_to_signals[event].emit(*args)

        # set cursor
        self.set_current_cursor()

        # clean current action (unique usage)
        self.user_action_generator.clean_action()

        # update the OpenGL view
        if do_update is None:
            do_update = (
                # (not isinstance(self, GalryTimerWidget)) and
                event is not None or prev_event is not None
            )

        if do_update:
            self.updateGL()

    # Miscellaneous
    # -------------
    def save_image(self, file=None, update=True):
        """Save a screenshot of the widget in the specified file."""
        if file is None:
            file = "image.png"
        if update:
            self.updateGL()
        image = self.grabFrameBuffer()
        image.save(file, "PNG")

    def toggle_fullscreen(self):
        self.is_fullscreen = not self.is_fullscreen
        if self.is_fullscreen:
            if hasattr(self.window, "showFullScreen"):
                self.window.showFullScreen()
        else:
            if hasattr(self.window, "showNormal"):
                self.window.showNormal()

    def close_widget(self):
        self.user_action_generator.close()
        if hasattr(self, "window"):
            if hasattr(self.window, "close"):
                self.window.close()

    # Focus methods
    # -------------
    def focusOutEvent(self, event):
        self.user_action_generator.focusOutEvent(event)
コード例 #3
0
ファイル: galrywidget.py プロジェクト: fiath/test
class GalryWidget(QGLWidget):
    """Efficient interactive 2D visualization widget.
    
    This QT widget is based on OpenGL and depends on both PyQT (or PySide)
    and PyOpenGL. It implements low-level mechanisms for interaction processing
    and acts as a glue between the different managers (PaintManager, 
    BindingManager, InteractionManager).
    
    """

    w = 600.
    h = 600.

    # Initialization methods
    # ----------------------
    def __init__(self, format=None, autosave=None, getfocus=True, **kwargs):
        """Constructor. Call `initialize` and initialize the companion classes
        as well."""
        if format is not None:
            super(GalryWidget, self).__init__(format)
        else:
            super(GalryWidget, self).__init__()

        self.initialized = False
        self.just_initialized = False

        self.i = 0

        # background color as a 4-tuple (R,G,B,A)
        self.bgcolor = (0, 0, 0, 0)
        self.autosave = None

        # default window size
        # self.width, self.height = 600, 600

        # FPS counter, used for debugging
        self.fps_counter = FpsCounter()
        self.display_fps = DISPLAY_FPS
        self.activate3D = None

        # widget creation parameters
        self.bindings = None
        self.companion_classes_initialized = False

        # constrain width/height ratio when resizing of zooming
        self.constrain_ratio = False
        self.constrain_navigation = False
        self.momentum = False
        self.activate_help = True
        self.activate_grid = False
        self.block_refresh = False

        # Capture keyboard events.
        if getfocus:
            self.setFocusPolicy(Qt.WheelFocus)

        # Capture mouse events.
        self.setMouseTracking(True)

        # Capture touch events.
        self.setAcceptTouchEvents = True
        self.grabGesture(QtCore.Qt.PinchGesture)
        self.mouse_blocked = False  # True during a pinch gesture

        # Initialize the objects providing the core features of the widget.
        self.user_action_generator = UserActionGenerator()

        self.is_fullscreen = False

        self.events_to_signals = {}

        # keyword arguments without "_manager" => passed to initialize
        self.initialize(**kwargs)

        # initialize companion classes if it has not been done in initialize
        if not self.companion_classes_initialized:
            self.initialize_companion_classes()
        self.initialize_bindings()

        # update rendering options
        self.paint_manager.set_rendering_options(
            activate3D=self.activate3D,
            constrain_ratio=self.constrain_ratio,
        )

        self.autosave = autosave

    def set_bindings(self, *bindings):
        """Set the interaction mode by specifying the binding object.
        
        Several binding objects can be given for the binding manager, such that
        the first one is the currently active one.
        
        Arguments:
          * bindings: a list of classes instances deriving from
            Bindings.
            
        """
        bindings = list(bindings)
        if not bindings:
            bindings = [PlotBindings()]
        # if type(bindings) is not list and type(bindings) is not tuple:
        # bindings = [bindings]
        # if binding is a class, try instanciating it
        for i in xrange(len(bindings)):
            if not isinstance(bindings[i], Bindings):
                bindings[i] = bindings[i]()
        self.bindings = bindings

    def set_companion_classes(self, **kwargs):
        """Set specified companion classes, unspecified ones are set to
        default classes.
        
        Arguments:
          * **kwargs: the naming convention is: `paint_manager=PaintManager`.
            The key `paint_manager` is the name the manager is accessed from 
            this widget and from all other companion classes. The value
            is the name of the class, it should end with `Manager`.
        
        """
        if not hasattr(self, "companion_classes"):
            self.companion_classes = {}

        self.companion_classes.update(kwargs)

        # default companion classes
        self.companion_classes.update([(k,v) for k,v in \
            DEFAULT_MANAGERS.iteritems() if k not in self.companion_classes])

    def initialize_bindings(self):
        """Initialize the interaction bindings."""
        if self.bindings is None:
            self.set_bindings()
        self.binding_manager.add(*self.bindings)

    def initialize_companion_classes(self):
        """Initialize companion classes."""
        # default companion classes
        if not getattr(self, "companion_classes", None):
            self.set_companion_classes()

        # create the managers
        for key, val in self.companion_classes.iteritems():
            log_debug("Initializing '%s'" % key)
            obj = val(self)
            setattr(self, key, obj)

        # link all managers
        for key, val in self.companion_classes.iteritems():
            for child_key, child_val in self.companion_classes.iteritems():
                # no self-reference
                if child_key == key:
                    continue
                obj = getattr(self, key)
                setattr(obj, child_key, getattr(self, child_key))

        self.interaction_manager.constrain_navigation = self.constrain_navigation
        self.companion_classes_initialized = True

    def initialize(self, **kwargs):
        """Initialize the widget.
        
        Parameters such as bindings, companion_classes can be
        set here, by overriding this method. If initializations must be done
        after companion classes instanciation, then
        self.initialize_companion_classes can be called here.
        Otherwise, it will be called automatically after initialize().
        
        """
        pass

    def clear(self):
        """Clear the view."""
        self.paint_manager.reset()

    def reinit(self):
        """Reinitialize OpenGL.
        
        The clear method should be called before.
        
        """
        self.initializeGL()
        self.resizeGL(self.w, self.h)
        self.updateGL()

    # OpenGL widget methods
    # ---------------------
    def initializeGL(self):
        """Initialize OpenGL parameters."""
        self.paint_manager.initializeGL()
        self.initialized = True
        self.just_initialized = True

    def paintGL(self):
        """Paint the scene.
        
        Called as soon as the window needs to be painted (e.g. call to 
        `updateGL()`).
        
        This method calls the `paint_all` method of the PaintManager.
        
        """
        if self.just_initialized:
            self.process_interaction('Initialize', do_update=False)
        # paint fps
        if self.display_fps:
            self.paint_fps()
        # paint everything
        self.paint_manager.paintGL()
        # compute FPS
        self.fps_counter.tick()
        if self.autosave:
            if '%' in self.autosave:
                autosave = self.autosave % self.i
            else:
                autosave = self.autosave
            self.save_image(autosave, update=False)
        self.just_initialized = False
        self.i += 1

    def paint_fps(self):
        """Display the FPS on the top-left of the screen."""
        self.paint_manager.update_fps(int(self.fps_counter.get_fps()))

    def resizeGL(self, width, height):
        self.w, self.h = width, height
        self.paint_manager.resizeGL(width, height)

    def sizeHint(self):
        return QtCore.QSize(self.w, self.h)

    # Event methods
    # -------------
    def event(self, e):
        r = super(GalryWidget, self).event(e)
        if e.type() == QtCore.QEvent.Gesture:
            e.accept()
            gesture = e.gesture(QtCore.Qt.PinchGesture)
            self.pinchEvent(gesture)
            if gesture.state() == Qt.GestureStarted:
                self.mouse_blocked = True
            elif gesture.state() == Qt.GestureFinished:
                self.mouse_blocked = False
            return False
        return r

    def pinchEvent(self, e):
        self.user_action_generator.pinchEvent(e)
        self.process_interaction()

    def mousePressEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mousePressEvent(e)
        self.process_interaction()

    def mouseReleaseEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mouseReleaseEvent(e)
        self.process_interaction()

    def mouseDoubleClickEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mouseDoubleClickEvent(e)
        self.process_interaction()

    def mouseMoveEvent(self, e):
        if self.mouse_blocked:
            return
        self.user_action_generator.mouseMoveEvent(e)
        self.process_interaction()

    def keyPressEvent(self, e):
        self.user_action_generator.keyPressEvent(e)
        self.process_interaction()
        # Close the application when pressing Q
        if e.key() == QtCore.Qt.Key_Q:
            if hasattr(self, 'window'):
                self.close_widget()

    def keyReleaseEvent(self, e):
        self.user_action_generator.keyReleaseEvent(e)

    def wheelEvent(self, e):
        self.user_action_generator.wheelEvent(e)
        self.process_interaction()

    def reset_action_generator(self):
        self.user_action_generator.reset()

    def leaveEvent(self, e):
        self.process_interaction(None)

    # Normalization methods
    # ---------------------
    def normalize_position(self, x, y):
        """Window coordinates ==> world coordinates."""
        if not hasattr(self.paint_manager, 'renderer'):
            return (0, 0)
        vx, vy = self.paint_manager.renderer.viewport
        x = -vx + 2 * vx * x / float(self.w)
        y = -(-vy + 2 * vy * y / float(self.h))
        return x, y

    def normalize_diff_position(self, x, y):
        """Normalize the coordinates of a difference vector between two
        points.
        """
        if not hasattr(self.paint_manager, 'renderer'):
            return (0, 0)
        vx, vy = self.paint_manager.renderer.viewport
        x = 2 * vx * x / float(self.w)
        y = -2 * vy * y / float(self.h)
        return x, y

    def normalize_action_parameters(self, parameters):
        """Normalize points in the action parameters object in the window
        coordinate system.
        
        Arguments:
          * parameters: the action parameters object, containing all
            variables related to user actions.
            
        Returns:
           * parameters: the updated parameters object with normalized
             coordinates.
             
        """
        parameters["mouse_position"] = self.normalize_position(\
                                                *parameters["mouse_position"])
        parameters["mouse_position_diff"] = self.normalize_diff_position(\
                                            *parameters["mouse_position_diff"])
        parameters["mouse_press_position"] = self.normalize_position(\
                                            *parameters["mouse_press_position"])
        parameters["pinch_position"] = self.normalize_position(\
                                            *parameters["pinch_position"])
        parameters["pinch_start_position"] = self.normalize_position(\
                                            *parameters["pinch_start_position"])
        return parameters

    # Signal methods
    # --------------
    def connect_events(self, arg1, arg2):
        """Makes a connection between a QT signal and an interaction event.
        
        The signal parameters must correspond to the event parameters.
        
        Arguments:
          * arg1: a QT bound signal or an interaction event.
          * arg2: an interaction event or a QT bound signal.
        
        """
        if type(arg1) == str:
            self.connect_event_to_signal(arg1, arg2)
        elif type(arg2) == str:
            self.connect_signal_to_event(arg1, arg2)

    def connect_signal_to_event(self, signal, event):
        """Connect a QT signal to an interaction event.
        
        The event parameters correspond to the signal parameters.
        
        Arguments:
          * signal: a QT signal.
          * event: an InteractionEvent string.
        
        """
        if signal is None:
            raise Exception("The signal %s is not defined" % signal)
        slot = lambda *args, **kwargs: \
                self.process_interaction(event, args, **kwargs)
        signal.connect(slot)

    def connect_event_to_signal(self, event, signal):
        """Connect an interaction event to a QT signal.
        
        The event parameters correspond to the signal parameters.
        
        Arguments:
          * event: an InteractionEvent string.
          * signal: a QT signal.
        
        """
        self.events_to_signals[event] = signal

    # Binding mode methods
    # --------------------
    def switch_interaction_mode(self):
        """Switch the interaction mode."""
        binding = self.binding_manager.switch()
        # set base cursor
        # self.interaction_manager.base_cursor = binding.base_cursor
        return binding

    def set_interaction_mode(self, mode):
        """Set the interaction mode.
        
        Arguments:
          * mode: either a class deriving from `Bindings` and which has been
            specified in `set_bindings`, or directly a `Bindings` instance.
        
        """
        binding = self.binding_manager.set(mode)
        # set base cursor
        # self.interaction_manager.base_cursor = binding.base_cursor
        return binding

    # Interaction methods
    # -------------------
    def get_current_action(self):
        """Return the current user action with the action parameters."""
        # get current action
        action = self.user_action_generator.action

        # get current key if the action was KeyPress
        key = self.user_action_generator.key

        # get key modifier
        key_modifier = self.user_action_generator.key_modifier

        # retrieve action parameters and normalize using the window size
        parameters = self.normalize_action_parameters(
            self.user_action_generator.get_action_parameters())
        return action, key, key_modifier, parameters

    def get_current_event(self):
        """Return the current interaction event corresponding to the current
        user action."""
        # get the current interaction mode
        binding = self.binding_manager.get()

        # get current user action
        action, key, key_modifier, parameters = self.get_current_action()

        # get the associated interaction event
        event, param_getter = binding.get(action,
                                          key=key,
                                          key_modifier=key_modifier)

        # get the parameter object by calling the param getter
        if param_getter is not None and parameters is not None:
            args = param_getter(parameters)
        else:
            args = None

        return event, args

    def set_current_cursor(self):
        cursor = self.interaction_manager.get_cursor()
        # if no cursor set, then use the default one in the current binding
        # mode
        if cursor is None:
            cursor = self.binding_manager.get().get_base_cursor()
        qcursor = get_cursor(cursor)
        if qcursor:
            self.setCursor(qcursor)

    def process_interaction(self, event=None, args=None, do_update=None):
        """Process user interaction.
        
        This method is called after each user action (mouse, keyboard...).
        It finds the right action associated to the command, then the event 
        associated to that action.
        
        Arguments:
          * event=None: if None, the current event associated to the current
            user action is retrieved. Otherwise, an event can be directly
            passed here to force the trigger of any interaction event.
          * args=None: the arguments of the event if event is not None.
        
        """
        if event is None:
            # get current event from current user action
            event, args = self.get_current_event()

        if event == 'Animate' and self.block_refresh:
            return

        prev_event = self.interaction_manager.prev_event

        # handle interaction mode change
        if event == 'SwitchInteractionMode':
            binding = self.switch_interaction_mode()
            log_debug("Switching interaction mode to %s." % \
                binding.__class__.__name__)

        # process the interaction event
        self.interaction_manager.process_event(event, args)

        # raise a signal if there is one associated to the current event
        if event in self.events_to_signals:
            self.events_to_signals[event].emit(*args)

        # set cursor
        self.set_current_cursor()

        # clean current action (unique usage)
        self.user_action_generator.clean_action()

        # update the OpenGL view
        if do_update is None:
            do_update = (
                # (not isinstance(self, GalryTimerWidget)) and
                (event is not None or prev_event is not None))

        if do_update:
            self.updateGL()

    # Miscellaneous
    # -------------
    def save_image(self, file=None, update=True):
        """Save a screenshot of the widget in the specified file."""
        if file is None:
            file = "image.png"
        if update:
            self.updateGL()
        image = self.grabFrameBuffer()
        image.save(file, "PNG")

    def toggle_fullscreen(self):
        self.is_fullscreen = not self.is_fullscreen
        if self.is_fullscreen:
            if hasattr(self.window, 'showFullScreen'):
                self.window.showFullScreen()
        else:
            if hasattr(self.window, 'showNormal'):
                self.window.showNormal()

    def close_widget(self):
        self.user_action_generator.close()
        if hasattr(self, 'window'):
            if hasattr(self.window, 'close'):
                self.window.close()

    # Focus methods
    # -------------
    def focusOutEvent(self, event):
        self.user_action_generator.focusOutEvent(event)
コード例 #4
0
ファイル: galrywidget.py プロジェクト: bmswgnp/rnicu
    def __init__(self, format=None, autosave=None, getfocus=True, **kwargs):
        """Constructor. Call `initialize` and initialize the companion classes
        as well."""
        if format is not None:
            super(GalryWidget, self).__init__(format)
        else:
            super(GalryWidget, self).__init__()
        
        self.initialized = False
        self.just_initialized = False
        
        self.i = 0
        
        # background color as a 4-tuple (R,G,B,A)
        self.bgcolor = (0, 0, 0, 0)
        self.autosave = None
        
        # default window size
        # self.width, self.height = 600, 600
        
        # FPS counter, used for debugging
        self.fps_counter = FpsCounter()
        self.display_fps = DISPLAY_FPS
        self.activate3D = None

        # widget creation parameters
        self.bindings = None
        self.companion_classes_initialized = False
        
        # constrain width/height ratio when resizing of zooming
        self.constrain_ratio = False
        self.constrain_navigation = False
        self.activate_help = True
        self.activate_grid = False
        self.block_refresh = False
        
        # Capture keyboard events.
        if getfocus:
            self.setFocusPolicy(Qt.WheelFocus)
        
        # Capture mouse events.
        self.setMouseTracking(True)
        
        # Capture touch events.
        self.setAcceptTouchEvents = True
        self.grabGesture(QtCore.Qt.PinchGesture)
        self.mouse_blocked = False  # True during a pinch gesture
        
        # Initialize the objects providing the core features of the widget.
        self.user_action_generator = UserActionGenerator()
        
        self.is_fullscreen = False
        
        self.events_to_signals = {}
        
        # keyword arguments without "_manager" => passed to initialize                  
        self.initialize(**kwargs)
        
        # initialize companion classes if it has not been done in initialize
        if not self.companion_classes_initialized:
            self.initialize_companion_classes()
        self.initialize_bindings()
        
        # update rendering options
        self.paint_manager.set_rendering_options(
                        activate3D=self.activate3D,
                        constrain_ratio=self.constrain_ratio,
                        )
        
        self.autosave = autosave