decorators

decorators

showWaitCursorWhileExecuting

ORSServiceClass.decorators.decorators.showWaitCursorWhileExecuting(function)

This decorator is used to change the cursor to busy while executing a method/function.

run_in_background_no_waiting

ORSServiceClass.decorators.decorators.run_in_background_no_waiting(function)

Decorator intended to make a function run in a separate thread (asynchronously).

Example:

@run_in_background_no_waiting
def task1():
    do_something

@run_in_background_no_waiting
def task2():
    do_something_too

t1 = task1()
t2 = task2()
...
t1.join()
t2.join()

run_in_background_no_waiting_auto_stop

ORSServiceClass.decorators.decorators.run_in_background_no_waiting_auto_stop(function)

Decorator intended to make a function run in a separate thread (asynchronously). The thread will stop on system exit.

Example:

@run_in_background_no_waiting_auto_stop
def task1():
    do_something

@run_in_background_no_waiting_auto_stop
def task2():
    do_something_too

t1 = task1()
t2 = task2()
...
t1.join()
t2.join()

run_in_background_waiting_until_finished

ORSServiceClass.decorators.decorators.run_in_background_waiting_until_finished(function)

Decorator intended to make a function run in a separate thread (synchronously).

Example:

@run_in_background_waiting_until_finished
def task1():
    do_something

task1()

printCaller

ORSServiceClass.decorators.decorators.printCaller(func)

This decorator prints the caller of the function.

printCallerWitArgs

ORSServiceClass.decorators.decorators.printCallerWitArgs(func)

This decorator prints the caller of the function (and the parameters).

infrastructure

interfaceMethod

ORSServiceClass.decorators.infrastructure.interfaceMethod(func)

This decorator is used to declare a method as an interface method. It can decorate only class methods.

This decorator takes care of the call to the logger with the given arguments. If the interface method has to be called without logging, call the interface method with the optional argument logging at False. This could be used, for example, to perform the logging only at the release of a slider motion instead of doing it at every slider value change.

The decorated method should contain a docstring written in reStructuredText. This docstring is used for validation and logging purposes.

Basic structure

The docstring of the method contains:

  • one or more paragraphs of text describing what the method does;

  • the declaration of all input arguments. These fields are used for each input argument:

  • the declaration of all output arguments. These fields are used for each output argument:

    Note that, when a single output is returned, there is no need to specify a name with the field declaration. However, when multiple outputs are declared, names should be given to each of these output variables and the method should always return all of these variables in the same order as they are declared in the docstring. The return values don’t have to be assigned to a variable. If they are assigned, the names of the variables in the code don’t have to match the names declared in the docstring (but it is recommended to use the same names for clarity).

@classmethod
@interfaceMethod
def createROIFromStructuredGrid(cls, aStructuredGrid, aTitle, fillROI):
    """
    This method creates a ROI with the shape of the given StructuredGrid instance
    (a Channel, a ROI or a MultiROI).
    The given title is set for the new ROI.

    A publish is also made on the new ROI so that it can appear in the list of objects.

    :param aStructuredGrid: the StructuredGrid instance (Channel, ROI or MultiROI) used as reference
        for the shape of the new ROI.
    :type aStructuredGrid: ORSModel.ors.StructuredGrid
    :param aTitle: title of the new ROI.
    :type aTitle: str
    :param fillROI: if True, the ROI will be filled.
        If False, the ROI will be empty.
    :type fillROI: bool

    :return: created ROI.
        This ROI has the same shape as the input StructuredGrid instance.
    :rtype: ORSModel.ors.ROI
    """

    if aStructuredGrid is None:
        return None

    # Creating a new ROI
    aNewROI = ROI()

    # Copying the shape of the StructuredGrid instance and setting the title
    aNewROI.copyShapeFromStructuredGrid(aStructuredGrid)
    aNewROI.setTitle(aTitle)

    # At this point, the ROI is empty
    if fillROI:
        # Filling on itself
        aNewROI.getReversed(aNewROI)

    # Making the ROI known to all the interested plugins and services
    aNewROI.publish()

    # Returning the new ROI
    return aNewROI

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoBasicStructureInterfaceMethod. Click on that menu item to create an instance of the plugin and open his mainform;
  5. Import a dataset;
  6. Write the title to give to the new ROI in the mainform of the plugin (in the field Title);
  7. Select the check box Fill ROI so that the ROI will be filled;
  8. Press the button OK to generate the new ROI with these specifications;
  9. Open the Action Log Viewer (in the Tools menu) and go the end of the text to see the logging of this call with the corresponding values.

Using inputs as lists

@classmethod
@interfaceMethod
def getTotalSize(cls, aListOfFiles):
    """
    This method computes the total size of the given files.

    :param aListOfFiles: the files to examine.
    :type aListOfFiles: file
    :count aListOfFiles: [0, None]

    :return: total size in bytes
    :rtype: int
    """

    totalSizeInBytes = 0  # Initialization
    for aFile in aListOfFiles:
        statinfo = os.stat(aFile)
        currentFileSizeInBytes = statinfo.st_size
        totalSizeInBytes += currentFileSizeInBytes

    return totalSizeInBytes

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoInputAsListInterfaceMethod. Click on that menu item to create an instance of the plugin and open his mainform;
  5. Press the button Select files to open a file explorer. Select a few files to examine and press Open. The total size in bytes is written in the mainform of the plugin.

Using multiple outputs

@classmethod
@interfaceMethod
def getBiggestMasks(cls, aChannel):
    """
    This method computes the masks (ROIs) over a dataset on a set of ranges.
    The ranges are obtained by splitting the full range of the dataset in 10 equal parts.
    Each mask is made by taking all the values of the dataset on that range, including the limits of the range.
    An overlap may exist in some situations.

    From all the masks computed, the two having the greatest count of voxels are returned.

    A list of the voxel count for each range is also returned. Since the ranges are overlapping,
    the total count of voxel from that list may exceed the total voxel count of the dataset.

    :param aChannel: input dataset to analyze
    :type aChannel: ORSModel.ors.Channel

    :return biggestMask: the mask having the most voxels
    :rtype biggestMask: ORSModel.ors.ROI
    :return secondBiggestMask: the mask having the most voxels after biggestMask
    :rtype secondBiggestMask: ORSModel.ors.ROI
    :return listVoxelCountInEachRange: list of the voxel count in each range
    :rtype listVoxelCountInEachRange: int
    :rcount listVoxelCountInEachRange: 10
    """

    if aChannel is None:
        # Returning all outputs with default values
        return None, None, 10*[0]

    IProgress = None
    outputROI = None

    listMasks = []  # Initialization
    listVoxelCountInEachRange = []  # Initialization
    minimalValue = aChannel.getMinimumValue()
    maximalValue = aChannel.getMaximumValue()
    step = (maximalValue - minimalValue)/10
    for stepIndex in range(10):
        # Computing the range limits
        minimalValueInRange = minimalValue + stepIndex*step
        maximalValueInRange = minimalValue + (stepIndex+1)*step

        # Getting the mask
        maskInRange = aChannel.getAsROIWithinRange(minimalValueInRange, maximalValueInRange, IProgress, outputROI)

        # Keeping the mask as a temporary result
        listMasks.append(maskInRange)

        # Getting the voxel count in this mask
        timeIndex = 0
        voxelCountInMask = maskInRange.getVoxelCount(timeIndex)
        listVoxelCountInEachRange.append(voxelCountInMask)

    # Finding the biggest masks
    # Sorting on the voxel count
    combinedListMaskAndVoxelCount = [(aMask, aCount) for aMask, aCount in zip(listMasks, listVoxelCountInEachRange)]
    combinedListMaskAndVoxelCount.sort(key=lambda val:val[1], reverse=True)

    biggestMask, _ = combinedListMaskAndVoxelCount.pop(0)
    secondBiggestMask, _ = combinedListMaskAndVoxelCount.pop(0)

    # Deleting all other temporary ROIs
    while len(combinedListMaskAndVoxelCount) > 0:
        aMask, _ = combinedListMaskAndVoxelCount.pop(0)
        aMask.deleteObject()

    # Setup of ROIs to return
    biggestMask.setTitle('Biggest mask')
    biggestMask.setInitialColor(orsColor(1, 0, 0, 0.5))
    biggestMask.publish()

    secondBiggestMask.setTitle('Second biggest mask')
    secondBiggestMask.setInitialColor(orsColor(0, 1, 0, 0.5))
    secondBiggestMask.publish()

    return biggestMask, secondBiggestMask, listVoxelCountInEachRange

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoMultipleOutputsInterfaceMethod. Click on that menu item to create an instance of the plugin and open his mainform;
  5. Import a dataset;
  6. Press the button OK to compute the masks for a set of 10 equal ranges of the dataset. The 2 ROIs having the most voxels are created. The relative voxel counts for each range are displayed on the UI.

action

ORSServiceClass.decorators.infrastructure.action(state='', title='')

This decorator is used to declare a method as containing an action.

The method decorated by this decorator should return a valid instance of Action (ORSServiceClass.actionAndMenu.action.Action) with all the required values.

The first argument (state) is the state associated to that action.

The second argument (title) is the string visible in the Configurable Actions section of the preferences, to identify the action.

When a key or mouse is pressed, the application looks for an associated action to be triggered. It starts by looking for actions having specified a state matching the current state of the application. If no such action has been found, it looks for a fallback action, having no state specification.

When an action is triggered, a 3-stages mechanism is started:

  1. the enterAction string of the action definition is executed once;
  2. the action string of the action definition is executed repetitively. When using a key press, this is done at least once, and repetitively until the key is released. When using a mouse click, this is called at each mouse movement (it is never called if the mouse doesn’t move between the mouse click and the mouse button release);
  3. the exitAction string of the action definition is executed once.

Class action

@classmethod
@action(title='Open the plugin DemoClassAction')
def actionOpenGUI(cls):
    anAction = Action(enterAction='DemoClassAction.openGUI()',
                      action='',
                      exitAction='')
    return anAction

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the Preferences and look in the Configurable Actions section for the name Open the plugin DemoClassAction. Set an unused keyboard key for that action, apply the changes and exit the Preferences;
  5. By using the specified action key, an instance of the plugin will be created and his mainform will be displayed.

Instance action

@action(title='Increment a counter in DemoInstanceAction')
def actionIncrementCounter(self):
    # The string in enterAction is an example of direct execution
    # The string in action is an example of call to an instance method
    anAction = Action(enterAction='{selfVarName}._triggersCount = 0; {selfVarName}.updateUI()'.format(selfVarName=self._varName),
                      action='{}.updateTriggersCount(1)'.format(self._varName),
                      exitAction='')
    return anAction

Note

A single instance will receive the trigger of an action even if there is multiple instances of the class. Therefore, it is recommended to use the mechanism of instance actions only when a single instance can be created. For plugins, this can be done by using the multiple=False definition flag. See ORSServiceClass.OrsPlugin.abstractPlugin.AbstractPlugin.

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoInstanceAction. Click on that menu item to create an instance of the plugin and open his mainform;
  5. Open the Preferences and look in the Configurable Actions section for the name Increment a counter in DemoInstanceAction. Set an unused keyboard key for that action, apply the changes and exit the Preferences;
  6. By using the specified action key, the counter will be reset when the key is pressed and will be incremented as long as the key is pressed. This value is shown on the UI.

State-dependent action

@action(state='stateDemoStateDependentAction_triggersA', title='Increment counter A in DemoStateDependentAction')
def actionIncrementCounterA(self):
    anAction = Action(enterAction='{selfVarName}._triggersACount = 0; {selfVarName}.updateUI()'.format(selfVarName=self._varName),
                      action='{}.updateTriggersCount("A", 1)'.format(self._varName),
                      exitAction='')
    return anAction

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoStateDependentAction. Click on that menu item to create an instance of the plugin and open his mainform;
  5. Open the Preferences and look in the Configurable Actions section for the name Increment counter A in DemoStateDependentAction. Set an unused keyboard key for that action. Set the same keyboard key for the action with the name Increment counter B in DemoStateDependentAction. Apply the changes and exit the Preferences;
  6. On the mainform of the plugin DemoStateDependentAction, press the button “Set the state for triggers A” to make responsive the action for the triggers A. Press the specified action key to make the counter A reset and increment as long as the key is pressed.
  7. On the mainform of the plugin DemoStateDependentAction, press the button “Set the state for triggers B” to make responsive the action for the triggers B. Press the specified action key to make the counter B reset and increment as long as the key is pressed.

interest

ORSServiceClass.decorators.infrastructure.interest(listSubjects=None)

This decorator is used to declare a method with an interest of specific entities. The decorated method is called when one of those entities is added or modified in the context of the plugin instance.

The first argument (listSubjects) is a single string or a list of strings. Each of these strings is the name of an entity. See Entities for the strings defined by the application.

The string ALL can be used to receive a call when any entity is added or modified.

Interest on entities defined by the application

from COMWrapper.ORS_def import OrsSelectedObjects
@interest(OrsSelectedObjects)
def interestSelectedObjects(self):
    # Do something
    pass

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoInterestOnApplicationDefinedEntities. Click on that menu item to create an instance of the plugin and open his mainform;
  5. Import a dataset;
  6. Draw a ruler;
  7. Select the dataset in the list of objects. The title of that object is written in the UI of the plugin with his class name Channel;
  8. Add the ruler in the selection. The title and class name VisualRuler of the ruler is added in the UI of the plugin.

Interest on user defined entities

@interest('DemoInterestOnUserDefinedEntities')
def interestDemoInterestOnUserDefinedEntities(self):
    # Do something
    pass

Source code example:

  1. Download the compressed file;
  2. Extract these files into a plugin extension folder. Note that there is 2 distinct plugins contained in the compressed file;
  3. Start the application;
  4. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoInterestOnUserDefinedEntities A. Click on that menu item to create an instance of the plugin A and open his mainform;
  5. Open the top level menu Demos to see the menu item named Demo: open the plugin DemoInterestOnUserDefinedEntities B. Click on that menu item to create an instance of the plugin B and open his mainform. Move that window so that both UIs can be seen simultaneously;
  6. Change the value in the spinbox of the plugin A. That value is updated in the UI of the plugin B;
  7. Note that multiple instances of each plugin may be used. When the value of the spinbox of an instance of the plugin A is changed, all instances of the plugin B will be updated with that new value.