.. meta:: :description: Infrastructure Mouse handlers state activation controller .. _mouseHandlers: Mouse Handlers ============== Mouse handlers is a code design with the objective to gather the code related to the processing of mouse events associated to a given application :ref:`state `. This is used to control how the mouse can interact with different kind of visual objects in the application. This code design allows common mouse behavior to be reused in different tools, and subclassing the basic class allows for behavior specialization. A mouse handler is a subclass of :class:`ORSServiceClass.ORSControllerAndHandler.ORSMouseHandlerAbstract.ORSMouseHandlerAbstract`, itself being a subclass of :class:`ORSServiceClass.ORSControllerAndHandler.ORSHandlerAbstract.ORSHandlerAbstract`. The work of an handler is decomposed in 3 parts: #. preparation: setup and gathering of the initial information that will be needed in the later steps. This is done in the method :meth:`ORSServiceClass.ORSControllerAndHandler.ORSHandlerAbstract.ORSHandlerAbstract.prepare`. It is done when the handler gets activated, usually when an action starts (for example, at the mouse button press). #. processing: performing the task of the handler while activated. This is usually where the main task of the handler has to be executed. This is done in the method :meth:`ORSServiceClass.ORSControllerAndHandler.ORSHandlerAbstract.ORSHandlerAbstract.process`. This is done only if the preparation has been completed successfully. It is done usually when an action starts (*EnterAction* stage) and while that action is active (*Action* stage), including on mouse move. #. finalization: completing the task and cleaning the data. This is done in the method :meth:`ORSServiceClass.ORSControllerAndHandler.ORSHandlerAbstract.ORSHandlerAbstract.reset`. This is done when the activation ends, but can also be done in other circumstances. These tasks are done using the given event data instance (see :class:`OrsEvent.eventdata.EventData`). Each handler class should declare in his class variable :attr:`ORSServiceClass.ORSControllerAndHandler.ORSHandlerAbstract.ORSHandlerAbstract.handleNames` the application states in which he can be used. Each application state should only be found in one handler. An handler is called via a *controller*, such as :class:`ORSServiceClass.ORSControllerAndHandler.ORSMouseControler.ORSMouseControler`. It is the role of the controller to instantiate the appropriate handler depending on the application state, to perform the different calls to that handler (preparation, processing and finalization) and to delete that handler when required. These calls to the handler by the controller are made in :meth:`ORSServiceClass.ORSControllerAndHandler.ORSMouseControler.ORSMouseControler.processEventData`. These calls to ``processEventData`` by the plugin are made in :ref:`handleTriggered `. A plugin instance should instantiate (usually, in the plugin *__init__*) and keep a controller, but never has to know what handler is used. **State Activation** To *activate* an handler means to call for his preparation and processing with the given event data. The handler remains *activated* until the call for his finalization is made. When calling the method :meth:`ORSServiceClass.ORSControllerAndHandler.ORSMouseControler.ORSMouseControler.processEventData` of the controller, the controller gets first the handler having declared the current state of the application in his supported states. The controller then requests from the plugin the *state activation* for the current application state. This state activation is declared in the plugin instance variable :attr:`ORSServiceClass.OrsPlugin.abstractPlugin.AbstractPlugin.stateDescriptors` for each declared state (variable ``stateActivation`` of the instance of :class:`ORSServiceClass.OrsPlugin.statedescriptor.StateDescriptor`). The handler is then activated or remains activated if the current action name match that state activation, or if that state activation name is empty. For this pattern to work, make sure that, for a given state: - the plugin declares a ``StateDescriptor`` in his class variable ``stateDescriptors`` with that state name; - the plugin defines an instance method decorated by *@action*, returning a valid :class:`ORSServiceClass.actionAndMenu.action.Action` instance; - the action method is state-dependent on that state; - the ``StateDescriptor`` instance contains the name of that action method in his ``stateActivation`` field; - the handler contains the name of that state in his ``handleNames`` class variable; - the handler class is imported by the plugin. This import statement can be done in the ``__init__.py`` file of the plugin or in the implementation file. :: class MyPlugin(OrsPlugin): StateA = 'StateAInMyPlugin' stateDescriptors = [StateDescriptor(state=StateA, stateActivation='actionStateAActivation')] @action(state=StateA) def actionStateAActivation(self): return Action() class MyHandler(ORSMouseHandlerAbstract): handleNames = ['StateAInMyPlugin'] In most cases, the action instance returned by the state activation action method is empty and all the logic is contained in the handler. .. _sourcecodeexample_plugin_DemoStateActivationMouseHandlers_4f8c6ed4ba7d11e7ade9448a5b5d70c0: Source code example: #. Download the :download:`compressed file `; #. Extract these files into a :ref:`plugin extension folder `; #. Start the application; #. Go to the Preferences to define the keys/mouse buttons for these actions: +-----------------------------------------------------------------------------+--------------------+--------------------+ | Action | Key | Mouse | +=============================================================================+====================+====================+ | Set the Hue and Saturation components of the background color of the view | | Left mouse | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Set the Value component of the background color of the view | | Left mouse | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Increases the Hue component of the background color of the view | Left | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Decreases the Hue component of the background color of the view | Right | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Increases the Saturation component of the background color of the view | Up | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Decreases the Saturation component of the background color of the view | Down | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Increases the Value component of the background color of the view | Up | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Decreases the Value component of the background color of the view | Down | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Set temporary state stateHueSaturation of DemoStateActivationMouseHandlers | Any unused key | | | | (ex: S) | | +-----------------------------------------------------------------------------+--------------------+--------------------+ | Set temporary state stateValue of DemoStateActivationMouseHandlers | Any unused key | | | | (ex: V) | | +-----------------------------------------------------------------------------+--------------------+--------------------+ Apply the changes and exit the Preferences; #. Open the top level menu *Demos* to see the menu item named *Demo: open the plugin DemoStateActivationMouseHandlers*. Click on that menu item to create an instance of the plugin and open his mainform; #. Press the button with the Hue-Saturation table image on the plugin UI to put the state of the application in *DemoStateActivationMouseHandlers: Hue-Saturation*. The light next to *Handler activated* is turned red because the current state of the application match the one specified by the handler of that state, but it is not yet activated. Bring the cursor on the view, and press and hold the left-mouse button to activate the handler. The light is turning green to indicate that the handler is activated. While the mouse button is pressed, move the cursor in the left-right direction to change the Hue component of the background color and in the up-down direction to change his Saturation component. When the mouse button is released, the action ends; #. Press and hold the key *Right* to decrease the Hue component of the background color. Note that the light of the *Handler activated* is still red because the handler doesn't get activated. Use the keys *Left*, *Right*, *Up* and *Down* to modify the Hue and Saturation components; #. Press the button with the Value slider image on the plugin UI to put the state of the application in *DemoStateActivationMouseHandlers: Value*. The light next to *Handler activated* is turned red because the current state of the application match the one specified by the handler of that state, but it is not yet activated. Bring the cursor on the view, and press and hold the left-mouse button to activate the handler. The light is turning green to indicate that the handler is activated. While the mouse button is pressed, move the cursor in the up-down direction to change the Value component of the background color. When the mouse button is released, the action ends; #. Press and hold the key *Up* or *Down* to increase or decrease the Value component of the background color; #. For the purpose of the demonstration, press any tool button to change the current application state, like *Track*; #. Press and hold the specified action key of the action *Set temporary state stateHueSaturation of DemoStateActivationMouseHandlers* (ex: *S*) to start this action. This put the current application state in *DemoStateActivationMouseHandlers: Hue-Saturation* and initializes the handler of that state. As soon as the mouse cursor is on the current view, the handler gets activated. By moving the mouse cursor, the handler is called to do his work, until the key to switch the state is released. The same pattern applies for the action *Set temporary state stateValue of DemoStateActivationMouseHandlers*. **Using annotations** Handlers requiring annotations (ruler, angle, rectangle, circle, ...) can be developed from the handler classes of :ref:`primitiveHandlers`. .. _sourcecodeexample_plugin_DemoAnnotationMouseHandlers_aedd61d0bd7711e7acfe448a5b5d70c0: Source code example: #. Download the :download:`compressed file `; #. Extract these files into a :ref:`plugin extension folder `; #. Start the application; #. Import a dataset; #. Go to the Preferences to define the mouse left-button for the action named *Zoom in area of DemoAnnotationMouseHandlers*. Apply the changes and exit the Preferences; #. Open the top level menu *Demos* to see the menu item named *Demo: open the plugin DemoAnnotationMouseHandlers*. Click on that menu item to create an instance of the plugin and open his mainform; #. Press the button on the UI of the plugin to set the state *DemoAnnotationMouseHandlers: Zoom in area*; #. Put the cursor in a 2D view, click and hold the mouse left-button and move the cursor to draw a rectangle. Release the mouse button. The view is zoomed to fit that rectangle area.