Tutorial 1 - Creating your first plug-in

Files:

This first plug-in is a simple "Hello world" example. It does very little interesting but is a framework for a plug-in. It demonstrates a basic executable plug-in framework, use of PlugInArgList, and use of Progress.

If you would like to run this tutorial as you follow along with the code, see Running Tutorials With Opticks. If you would like to build and experiment with this tutorial as you follow along with the code, see Building and Running Tutorial plug-ins. If you are running this tutorial in Opticks, you will need to select "Tutorial 1" from the Tutorial toolbar to execute it.

The Header File

A plug-in is a C++ class which implements the PlugIn interface. Opticks provides a number of shell classes with a default implementation of much of the interface. PlugInShell implements PlugIn and provides mutators for the PlugIn attributes. Most plug-ins provide additional functionality beyond PlugIn. Most will implement at least Executable. A majority of the Executable methods have default implementations in ExecutableShell so we'll use that as a starting point. ExecutableShell does not implement the Executable::getInputSpecification(), Executable::getOutputSpecification(), and Executable::execute() methods so our plug-in must provide implementations for at least these methods as well as a constructor and a destructor. Our plug-in does not have any state so there are no private members and there are no helper methods.

The Implementation File

The various properties exposed by the PlugIn and Executable interfaces are set using mutators from ExecutableShell. Most of these are set in the plug-in's constructor. Certain shell classes will set default values such as plug-in type. See the documentation for the specific shell class for further information.
Tutorial1::Tutorial1()
{
   setDescriptorId("{5D8F4DD0-9B20-42B1-A060-589DFBC85D00}");
The descriptor ID uniquely identifies a plug-in. When loading plug-ins, Opticks will generate an error if there are multiple plug-ins with the same descriptor ID. This string is usually a UUID enclosed in {}. A UUID can be generated with the "Create GUID" tool on Windows or the makeuuid program on Solaris.
   setName("Tutorial 1");
   setDescription("Creating your first plug-in.");
   setCreator("Opticks Community");
   setVersion("Sample");
   setCopyright("Copyright (C) 2008, Ball Aerospace & Technologies Corp.");
These statements set some informational metadata such as a name and description.
   setProductionStatus(false);
If the production status of any loaded plug-in is false, or the status of the application is development, all data and generated products will contain a "Not for production use" message. We are setting this to false since this example plug-in should never be used in a production environment. We suggest that you set this using a #define in a configuration file common to all the plug-ins in a plug-in suite or in your build environment. When building a production plug-in this should be set to true.
   setType("Sample");
The plug-in type is a free-form string. Plug-ins can be searched and accessed based on type and certain types, such as Importer, have special meaning to Opticks. Generally, if your plug-in should be a special type, the type will be set by a corresponding shell class such as ImporterShell.
   setMenuLocation("[Tutorial]/Tutorial 1");
The menu location exposes your plug-in to the user. There are other ways to expose plug-in execution. See MenuBar::addCommand() for an alternate method. Plug-ins which execute automatically or are designed to be executed only in wizards will not usually set a menu location. The location is a "path" indicating a menu hierarchy. The square brackets [] indicate that the menu exists on the "Tutorial" toolbar. Refer to ExecutableShell::setMenuLocation() for more information on menu location strings.
   setAbortSupported(false);
}
This disables the abort button in the progress dialog. Our plug-in does not support aborting right now. We'll add that in later.

The Tutorial1::~Tutorial() and Tutorial1::getOutputSpecification() methods don't do anything interesting right now but Tutorial1::getInputSpecification() does.

bool Tutorial1::getInputSpecification(PlugInArgList*& pInArgList)
{
   pInArgList = Service<PlugInManagerServices>()->getPlugInArgList();
   VERIFY(pInArgList != NULL);
The VERIFY() macro checks for errors and if one is detected, logs a message and returns false. There are a few variations on VERIFY() including VERIFYNRV() and VERIFYRV(). See the reference documentation for a complete list. VERIFY() should only be used for detecting programming errors as it may display an error dialog to the user with developer oriented debugging information. In this case, PlugInManagerServices::getPlugInArgList() will never return a NULL pointer unless there is a serious run-time error such as insufficient memory.

Service is an Opticks Resource which provides access to a service singleton such as PlugInManagerServices. We'll talk more about Resource and Service in a later tutorial. PlugInManagerServices is being used to create a new PlugInArgList. If we have no arguments, the PlugInArgList should be set to NULL like we are doing in Tutorial1::getOutputSpecification().

   pInArgList->addArg<Progress>(Executable::ProgressArg(), NULL, "Progress reporter");
   return true;
}
We are adding a new Progress argument. Some interfaces, such as Executable, expect certain plug-in arguments. The argument name is defined in the interface and should be used when creating and accessing those plug-in arguments. If you are adding an argument specific to your plug-in, you can use any name you like as long as it is not already used by another argument on one of the plug-in interfaces. Executable::ProgressArg() can be NULL when the plug-in is called in batch mode. Since NULL is a valid value, we'll make NULL the default. The last argument is an optional description. It is displayed to the user in various places. If the meaning of the argument is not obvious, a short description should be provided.

Finally, the Tutorial1::execute() method provides the real functionality of the plug-in. It is called when the user selects the menu location.

bool Tutorial1::execute(PlugInArgList* pInArgList, PlugInArgList* pOutArgList)
{
   if (pInArgList == NULL)
   {
      return false;
   }
We created this PlugInArgList in Tutorial1::getInputSpecification() so it should not be NULL. We set the output argument list to NULL in Tutorial1::getOutputSpecification() since we have no output arguments. If we were returning data in the output argument list, we would ensure that pOutArgList is not NULL as well.
   Progress* pProgress = pInArgList->getPlugInArgValue<Progress>(Executable::ProgressArg());
This gets the value of the Executable::ProgressArg() argument we declared in Tutorial1::getInputSpecification().
   if (pProgress != NULL)
   {
      pProgress->updateProgress("This demonstrates display of a warning.", 0, WARNING);
      pProgress->updateProgress("This demonstrates display of an error.", 0, ERRORS);
      pProgress->updateProgress("Hello World!", 100, NORMAL);
   }
Remember that Executable::ProgressArg() is allowed to be NULL when calling Executable::execute() so we need to check for NULL here. If we have a valid progress, we display a message to the user. We indicate that the progress is 100%. Normally, we should always set the progress to 100% before returning successfully from Executable::execute(). The progress dialog may close when the plug-in finishes executing so make sure the "Automatically close on process completion" option is not checked in "Tools->Options->Session->General->Progress Dialog".
   return true;
}
Finally, indicate that the plug-in successfully executed.

Software Development Kit - Opticks 4.8.0 Build 15482