ParaView/Plugin HowTo: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
No edit summary
Line 11: Line 11:
* Client plugins - extend the paraview client
* Client plugins - extend the paraview client
* A plugin may contain both client and server extensions and work if the appropriate client side libraries are on the server.
* A plugin may contain both client and server extensions and work if the appropriate client side libraries are on the server.
== Loading Plugins ==
There are two ways to load Plugins into ParaView.  One is to manually add it using the Plugin Manager found under the Tools menu.  You can browse both on the client and server side for plugins.  The other method is to set the PV_PLUGIN_PATH environment variable to a list of colon or semi-colon separated directories where plugins exists.  If PV_PLUGIN_PATH is set, the plugins will be loaded at startup.


== Building Plugins ==
== Building Plugins ==

Revision as of 20:01, 1 June 2007

Overview

Plugins can be used to extend ParaView in several ways

  • Add new VTK objects (readers, writers, filters, etc...)
  • Add custom Qt widgets
  • Add custom toolbars
  • Add custom views in addition to the existing render view, line chart and bar chart.

Types of plugins

  • Server plugins - extend the paraview server
  • Client plugins - extend the paraview client
  • A plugin may contain both client and server extensions and work if the appropriate client side libraries are on the server.

Loading Plugins

There are two ways to load Plugins into ParaView. One is to manually add it using the Plugin Manager found under the Tools menu. You can browse both on the client and server side for plugins. The other method is to set the PV_PLUGIN_PATH environment variable to a list of colon or semi-colon separated directories where plugins exists. If PV_PLUGIN_PATH is set, the plugins will be loaded at startup.

Building Plugins

To create a plugin, one must have their own build of ParaView3. Binary downloads do not include header files or import libraries (on applicable platforms).

The beginning of a CMakeLists.txt file contains

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})

Where CMake will ask for the ParaView_DIR which you point to your ParaView build. The PARAVIEW_USE_FILE includes build parameters and macros for building plugins.

Adding a custom filter, reader or writer

Standard VTK procedure is followed for writing these classes. XML for ParaView must also be written, and an example of that can be found at ParaView:Extend. If we have a class vtkMyFilter with MyFilter.xml, we put that in a server plugin as follows:

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})
ADD_PARAVIEW_PLUGIN(MyFilterSMPlugin "1.0" 
                    SERVER_MANAGER_XML MyFilter.xml
                    SERVER_MANAGER_SOURCES vtkMyFilter.cxx )

The plugin library will be named MyFilterSMPlugin.dll, libMyFilterSMPlugin.so or named as appropriate for the platform. The name is arbitrary. The XML, though client-side, is embedded into the plugin. When the ParaView server loads the plugin, the XML will be given to the client. If the reader or writer already exists in VTK, one can just create the server manager XML and create a plugin from that (exclude SERVER_MANAGER_SOURCES in the previous example). If the server manager XML already exists in ParaView, a server plugin is not needed.

For readers and writers, we also need to make a client plugin. We create an XML file defining the reader or writer information.

<ParaViewReaders>
  <Reader name="MyReader"
          extensions="myreader mr"
          file_description="My Reader Files">
  </Reader>
</ParaViewReaders>

The name corresponds with the name found in the server manager XML. The extensions is to define what file extensions for file types and is used by the file dialog for filtering. The file_description is also used by the file dialog to give a friendly name for filtering.

We can have multiple client side XML files for different readers, writers, etc... We embed those into a Qt resource in an qrc file as follows:

<RCC>
  <qresource prefix="/ParaViewResources" >
    <file>MyReader.xml</file>
  </qresource>
<RCC>

Then we create the CMakeLists.txt file to build the client side plugin for the reader.

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})
ADD_PARAVIEW_PLUGIN(MyFilterGUIPlugin "1.0" 
                    GUI_RESOURCES MyReader.qrc )

Adding a toolbar

New toolbars can be added from plugins to do virtually any operation. Let's make a toolbar with two buttons to create a sphere and cylinder source.

In MyToolBarActions.h, we derive a class from QActionGroup, which is a class to hold a group of actions. Each action can have an icon and text.

#include <QActionGroup>
#include <QApplication>
#include <QStyle>
#include "pqApplicationCore.h"
#include "pqServerManagerModel.h"

class MyToolBarActions : public QActionGroup
{
   Q_OBJECT
 public:
   MyToolBarActions(QObject* p) : QActionGroup(p)
   {
     // let's use a Qt icon (we could make our own)
     QIcon icon = qApp->style()->standardIcon(QStyle::SP_MessageBoxCritical);
     QAction* a = new QAction(icon, "Create Sphere", this);
     a->setData("SphereSource");
     this->addAction(a);
     a = new QAction(icon, "Create Cylinder", this);
     a->setData("CylinderSource");
     this->addAction(a);
     QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(onAction(QAction*)));
   }
 public slots:
   void onAction(QAction* a)
   {
     pqApplicationCore* core = pqApplicationCore::instance();
     pqServerManagerModel* sm = core->getServerManagerModel();
     if(sm->getNumberOfServers())
       {
       pqServer* s = sm->getServerByIndex(0);
       QString source_type = a->data().toString();
       core->createSourceOnServer(source_type.toAscii().data(), s);
       }
   }
};

To build the plugin, the CMakeLists.txt file is:

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})
QT4_WRAP_CPP(MOC_SRCS MyToolBarActions.h)
ADD_PARAVIEW_ACTION_GROUP(IFACES IFACE_SRCS CLASS_NAME MyToolBarActions
                         GROUP_NAME "ToolBar/MyActions")
ADD_PARAVIEW_PLUGIN(MyToolBar "1.0"
                    GUI_INTERFACES ${IFACES}
                    SOURCES ${MOC_SRCS} ${IFACE_SRCS})

For "ToolBar/MyActions," "ToolBar" is a keyword that means the action action group goes in a ToolBar called "MyActions." When the plugin is loaded, an extra toolbar will show up with two buttons.

Adding an object panel

Object Panels are the panels for editing object properties.

ParaView3 contains automatic panel generation code which is suitable for most objects. If you find your object doesn't have a good auto-generated panel, you can make your own. There are different classes you can derive from depending on how much customization you need to do, or how much existing code you can leverage.

  • pqObjectPanel - the base class for all object panels with limited functionality.
  • pqNamedObjectPanel - if your widgets (line edit, combo boxes, etc..) have names corresponding with the server manager XML, then this class will automatically hook those widgets up with the server manager.
  • pqLoadedFormObjectPanel - if you want to design your form in the Qt Designer, you get a .ui file as a result. You can use this class to load the .ui file. If the names of widgets created in the Qt Designer correspond with server manager objects, they will automatically be hooked up.
  • pqAutoGeneratedObjectPanel - a panel that has widgets automatically created based on the properties of server manager objects. You can extend this if you have small adjustments to make or small features to add.

Now let's say we have our own panel we want to make for a ConeSource. In this example, we'll just add a simple label saying that this panel came from the plugin. In ConePanel.h:

#include "pqAutoGeneratedObjectPanel.h"
#include <QLabel>
#include <QLayout>

class ConePanel : public pqAutoGeneratedObjectPanel
{
  Q_OBJECT
public:
  ConePanel(pqProxy* pxy, QWidget* p)
    : pqAutoGeneratedObjectPanel(pxy, p)
  {
    this->layout()->addWidget(new QLabel("This is from a plugin", this));
  }
};

Then in our CMakeLists.txt file:

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})
QT4_WRAP_CPP(MOC_SRCS ConePanel.h)
ADD_PARAVIEW_OBJECT_PANEL(IFACES IFACE_SRCS CLASS_NAME ConePanel
                         XML_NAME ConeSource XML_GROUP sources)
ADD_PARAVIEW_PLUGIN(GUIConePanel "1.0"
                    GUI_INTERFACES ${IFACES}
                    SOURCES ${MOC_SRCS} ${IFACE_SRCS})

Adding a custom view

ParaView contains a render view for rendering 3d images. It also contains chart views to visualize data in line charts and histogram charts. You may want to create another custom view that does your own view of the data.

For this example, we'll just make a simple Qt widget with labels showing the displays that have been added to the view.

To make a custom view, we need both client and server side plugins.

For our server side, we simply have:

<ServerManagerConfiguration>
 <ProxyGroup name="displays">
  <GenericViewDisplayProxy name="MyDisplay"
    base_proxygroup="displays" base_proxyname="GenericViewDisplay">
  </GenericViewDisplayProxy>
 </ProxyGroup>
 <ProxyGroup name="views">
  <ViewModuleProxy name="MyViewViewModule"
    base_proxygroup="rendermodules" base_proxyname="ViewModule"
    display_name="MyDisplay">
  </ViewModuleProxy>
 </ProxyGroup>
 <ProxyGroup name="filters">
  <SourceProxy name="MyExtractEdges" class="vtkExtractEdges"
    label="My Extract Edges">
    <InputProperty
      name="Input"
      command="SetInputConnection">
        <ProxyGroupDomain name="groups">
          <Group name="sources"/>
          <Group name="filters"/>
        </ProxyGroupDomain>
        <DataTypeDomain name="input_type">
          <DataType value="vtkDataSet"/>
        </DataTypeDomain>
    </InputProperty>
    <Hints>
      <View type="MyView"/>
    </Hints>
  </SourceProxy>
 </ProxyGroup>
</ServerManagerConfiguration>

We define "MyDisplay" as a simple display proxy, and "MyViewModule" as a simple view module. We have our own filter "MyExtractEdges" with a hint saying it prefers to be shown in a view of type "MyView." So if we create a MyExtractEdges in ParaView3, it'll automatically be shown in our custom view.

We build the server plugin with a CMakeLists.txt file as:

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})
ADD_PARAVIEW_PLUGIN(SMMyView "1.0" SERVER_MANAGER_XML MyViewSM.xml)


Our client side plugin will contain an extension of pqGenericViewModule. We can let ParaView give us a display panel for these displays, or we can make our own deriving from pqDisplayPanel. In this example, we'll make a simple display panel.

We implement MyView in MyView.h:

#include "pqGenericViewModule.h"
#include <QMap>
#include <QLabel>
#include <QVBoxLayout>
#include <vtkSMProxy.h>
#include <pqDisplay.h>
#include <pqServer.h>
#include <pqPipelineSource.h>

/// a simple view that shows a QLabel with the display's name in the view
class MyView : public pqGenericViewModule
{
 Q_OBJECT
public:
 MyView(const QString& viewtypemodule, const QString& group, const QString& name,
        vtkSMAbstractViewModuleProxy* viewmodule, pqServer* server, QObject* p)
    : pqGenericViewModule(viewtypemodule, group, name, viewmodule, server, p)
   {
   this->MyWidget = new QWidget;
   new QVBoxLayout(this->MyWidget);

   // connect to display creation so we can show them in our view
   this->connect(this, SIGNAL(displayAdded(pqDisplay*)),
     SLOT(onDisplayAdded(pqDisplay*)));
   this->connect(this, SIGNAL(displayRemoved(pqDisplay*)),
     SLOT(onDisplayRemoved(pqDisplay*)));

   }
 ~MyView()
   {
   delete this->MyWidget;
   }

 /// we don't support save images
 bool saveImage(int, int, const QString& ) { return false; }
 vtkImageData* captureImage(int) { return NULL; }

 /// return the QWidget to give to ParaView's view manager
 QWidget* getWidget()
   {
   return this->MyWidget;
   }
 /// returns whether this view can display the given source
 bool canDisplaySource(pqPipelineSource* source) const
   {
   if(!source ||
      this->getServer()->GetConnectionID() != source->getServer()->GetConnectionID() ||
      QString("MyExtractEdges") != source->getProxy()->GetXMLName())
     {
     return false;
     }
   return true;
   }

protected slots:
 void onDisplayAdded(pqDisplay* d)
   {
   QString text = QString("Display (%1)").arg(d->getProxy()->GetSelfIDAsString());
   QLabel* label = new QLabel(text, this->MyWidget);
   this->MyWidget->layout()->addWidget(label);
   this->Labels.insert(d, label);
   }

 void onDisplayRemoved(pqDisplay* d)
   {
   QLabel* label = this->Labels.take(d);
   if(label)
     {
     this->MyWidget->layout()->removeWidget(label);
     delete label;
     }
   }

protected:

 QWidget* MyWidget;
 QMap<pqDisplay*, QLabel*> Labels;

};

And MyDisplay.h is:

#include "pqDisplayPanel.h"
#include <QVBoxLayout>
#include <QLabel>

class MyDisplay : public pqDisplayPanel
{
  Q_OBJECT
public:
  MyDisplay(pqDisplay* display, QWidget* p)
    : pqDisplayPanel(display, p)
    {
    QVBoxLayout* l = new QVBoxLayout(this);
    l->addWidget(new QLabel("From Plugin", this));
    }
};

The CMakeLists.txt file to build the client plugin would be:

FIND_PACKAGE(ParaView REQUIRED)
INCLUDE(${PARAVIEW_USE_FILE})

QT4_WRAP_CPP(MOC_SRCS MyView.h MyDisplay.h)
 
ADD_PARAVIEW_VIEW_MODULE(IFACES IFACE_SRCS VIEW_TYPE MyView VIEW_XML_GROUP views
                         DISPLAY_XML MyDisplay DISPLAY_PANEL MyDisplay)

ADD_PARAVIEW_PLUGIN(GUIMyView "1.0" GUI_INTERFACES ${IFACES}
                    SOURCES ${MOC_SRCS} ${IFACE_SRCS})

We load the plugins in ParaView, and we create something like a Cone, then create a "My Extract Edges" filter. The multiview manager will create a new view and the label "Display (151)".