2. 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 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 ORSServiceClass.ORSControllerAndHandler.ORSMouseHandlerAbstract.ORSMouseHandlerAbstract, itself being a subclass of ORSServiceClass.ORSControllerAndHandler.ORSHandlerAbstract.ORSHandlerAbstract.

The work of an handler is decomposed in 3 parts:

  1. preparation: setup and gathering of the initial information that will be needed in the later steps. This is done in the method 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).
  2. 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 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.
  3. finalization: completing the task and cleaning the data. This is done in the method 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 OrsEvent.eventdata.EventData).

Each handler class should declare in his class variable 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 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 ORSServiceClass.ORSControllerAndHandler.ORSMouseControler.ORSMouseControler.processEventData(). These calls to processEventData by the plugin are made in 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 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 ORSServiceClass.OrsPlugin.abstractPlugin.AbstractPlugin.stateDescriptors for each declared state (variable stateActivation of the instance of 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 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.

Source code example:

  1. Download the compressed file;

  2. Extract these files into a plugin extension folder;

  3. Start the application;

  4. 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;

  5. 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;

  6. 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;

  7. 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;

  8. 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;

  9. Press and hold the key Up or Down to increase or decrease the Value component of the background color;

  10. For the purpose of the demonstration, press any tool button to change the current application state, like Track;

  11. 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 primitiveHandlers.

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Import a dataset;
  5. 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;
  6. 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;
  7. Press the button on the UI of the plugin to set the state DemoAnnotationMouseHandlers: Zoom in area;
  8. 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.