Source code for ilastik.applets.base.standardApplet
###############################################################################
# ilastik: interactive learning and segmentation toolkit
#
# Copyright (C) 2011-2014, the ilastik developers
# <team@ilastik.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# In addition, as a special exception, the copyright holders of
# ilastik give you permission to combine ilastik with applets,
# workflows and plugins which are not covered under the GNU
# General Public License.
#
# See the LICENSE file for details. License information is also available
# on the ilastik web site at:
# http://ilastik.org/license.html
###############################################################################
from ilastik.utility import MultiLaneOperatorABC, OpMultiLaneWrapper
from ilastik.applets.base.applet import Applet
from ilastik.applets.base.singleToMultiGuiAdapter import SingleToMultiGuiAdapter
[docs]class StandardApplet( Applet ):
"""
In most cases, it is easiest to use StandardApplet as a base class for your custom applet.
StandardApplets are designed to simplify two tasks for most use-cases: Creating a *top-level operator* and creating a GUI.
StandardApplet subclasses may expose their *top-level operator* in one of two ways:
1) (Advanced) Override the :py:attr:`Applet.topLevelOperator<ilastik.applets.base.applet.Applet.topLevelOperator>` property directly.
2) (Simple) Override BOTH :py:attr:`singleLaneOperatorClass` and :py:attr:`broadcastingSlots`, in which case a default implementation of :py:attr:`topLevelOperator` is provided for you.
StandardApplet subclasses may expose their GUI in one of three ways:
1) (Advanced) Override :py:meth:`createMultiLaneGui`.
2) (Simpler) Override :py:meth:`createSingleLaneGui`, in which case a default implementation of :py:meth:`createMultiLaneGui` is provided for you.
3) (Simplest) Override :py:attr:`singleLaneGuiClass`, in which case default implementations of :py:meth:`createSingleLaneGui` and :py:meth:`createMultiLaneGui` are provided for you.
"""
[docs] def __init__(self, name, workflow=None, interactive=True, *args, **kwargs):
"""
Constructor.
:param name: The applet's name as it will appear in the GUI (e.g. the Applet Drawer title).
:param workflow: The workflow this applet belongs to (not required if the subclass provides its own topLevelOperator).
"""
super(StandardApplet, self).__init__(name, *args, interactive=interactive, **kwargs)
self._gui = None
self.__topLevelOperator = None
self.__workflow = workflow
#
# Top-Level Operator
#
# Subclasses have 2 Choices:
# - Override topLevelOperator (advanced)
# - Override singleLaneOpeartorClass AND broadcastingSlots (easier; uses default topLevelOperator implementation)
@property
def singleLaneOperatorClass(self):
"""
Return the operator class which handles a single image.
Single-lane applets should override this property.
(Multi-lane applets must override ``topLevelOperator`` directly.)
"""
return NotImplemented
@property
def singleLaneOperatorInitArgs(self):
return ((), {}) # args, kwargs
@property
def broadcastingSlots(self):
"""
Slots that should be connected to all image lanes are referred to as "broadcasting" slots.
Single-lane applets should override this property to return a list of the broadcasting slots' names.
(Multi-lane applets must override ``topLevelOperator`` directly.)
"""
return NotImplemented
@property
def topLevelOperator(self):
"""
Get the top-level (multi-image-lane) operator for the applet.
This default implementation uses ``singleLaneOperatorClass``
and ``broadcastingSlots`` to generate the top-level operator.
Applets that must be multi-image-lane-aware must override this property.
Note that the top-level operator must adhere to the ``MultiLaneOperatorABC`` interface.
"""
if self.__topLevelOperator is None:
self.__createTopLevelOperator()
return self.__topLevelOperator
#
# GUI
#
# Subclasses have 3 choices:
# - Override createMultiLaneGui (advanced)
# - Override createSingleLaneGui (easier: uses default createMultiLaneGui implementation)
# - Override singleLaneGuiClass (easiest: uses default createSingleLaneGui and createMultiLaneGui implementations)
@property
def singleLaneGuiClass(self):
"""
Return the class that will be instantiated for each image lane the applet needs.
The class constructor should accept a single parameter: a single-lane of the top-level operator.
"""
return NotImplemented
[docs] def createSingleLaneGui(self, imageLaneIndex):
"""
This function is called to create new instances of single-lane applet GUIs.
If your applet's single-lane GUI requires special constructor arguments, then override this method.
Otherwise, this default implementation generates instances of your ``singleLaneGuiClass``.
"""
return self.__createSingleLaneGui(imageLaneIndex)
[docs] def getMultiLaneGui(self):
"""
Override from Applet base class.
"""
if self._gui is None:
self._gui = self.__createMultiLaneGui()
return self._gui
#
# Private
#
def __createSingleLaneGui(self, imageLaneIndex):
"""
Default implementation of createSingleLaneGui
"""
if self.singleLaneGuiClass is NotImplemented:
message = "Cannot create GUI.\n"
message += "StandardApplet subclasses must implement ONE of the following:\n"
message += "singleLaneGuiClass, createSingleLaneGui, or getMultiLaneGui"
raise NotImplementedError(message)
singleLaneOperator = self.topLevelOperator.getLane( imageLaneIndex )
return self.singleLaneGuiClass( self, singleLaneOperator )
def __createMultiLaneGui(self):
"""
This function serves as the default implementation of createMultiLaneGui().
It bundles multiple GUIs instantiated with ``createSingleLaneGui`` into one multi-image gui,
which is what the Applet interface expects.
"""
for cls in self.__class__.__mro__:
if 'createMultiLaneGui' in cls.__dict__:
return self.createMultiLaneGui()
if 'createSingleLaneGui' in cls.__dict__:
return SingleToMultiGuiAdapter( self, self.createSingleLaneGui, self.topLevelOperator )
if 'singleLaneGuiClass' in cls.__dict__:
assert isinstance(self.topLevelOperator, MultiLaneOperatorABC), "If your applet's top-level operator doesn't satisfy MultiLaneOperatorABC, you must implement createMultiLaneGui yourself."
return SingleToMultiGuiAdapter( self, self.__createSingleLaneGui, self.topLevelOperator )
raise Exception("Your applet must override one of the GUI creation methods. See StandardApplet docs for details.")
def __createTopLevelOperator(self):
"""
Called by the default implementation of ``topLevelOperator`` to create a multi-image operator by wrapping single-image operators.
"""
assert self.__topLevelOperator is None
operatorClass = self.singleLaneOperatorClass
operatorInitArgs, operatorInitKwargs = self.singleLaneOperatorInitArgs
broadcastingSlots = self.broadcastingSlots
if operatorClass is NotImplemented or broadcastingSlots is NotImplemented:
message = "Could not create top-level operator for {}\n".format( self.__class__ )
message += "StandardApplet subclasses must implement the singleLaneOperatorClass and broadcastingSlots"
message += " members OR override topLevelOperator themselves."
raise NotImplementedError(message)
if self.__workflow is None:
message = "Could not create top-level operator for {}\n".format( self.__class__ )
message += "Please initialize StandardApplet base class with a workflow object."
raise NotImplementedError(message)
self.__topLevelOperator = OpMultiLaneWrapper( self.singleLaneOperatorClass,
operator_args=operatorInitArgs,
operator_kwargs=operatorInitKwargs,
parent=self.__workflow,
broadcastingSlotNames=self.broadcastingSlots )