ParaView/Plugin HowTo
Overview
ParaView comes with plethora of functionality bundled in: several readers, multitude of filters, quite a few different types of views etc. However, it is not uncommon for developers to want to add new functionality to ParaView for eg. to add support to their new file format, incorporate a new filter into paraview etc. ParaView makes it possible to add new functionlity by using an extensive plugin mechanism.
Plugins can be used to extend ParaView in several ways:
- Add new readers, writers, filters
- Add custom GUI components such as toolbar buttons to perform common tasks
- Add new views in for display data
Examples for different types of plugins are provided with the ParaView source under Examples/Plugins/.
This document has major sections:
- First section covers how to use existing plugins in ParaView.
- Second section contains information for developers about writing new plugins for ParaView.
Using Plugins
Plugins are distributed as shared libraries (*.so on Unix, *.dylib on Mac, *.dll on Windows etc). For a plugin to be loadable in ParaView, it must be built with the same version of ParaView as it is expected to be deployed on. Plugins can be classified into two broad categories:
- Server-side plugins
- These are plugins that extend the algorithmic capabilities for ParaView eg. new filters, readers, writers etc. Since in ParaView data is processed on the server-side, these plugins need to be loaded on the server.
- Client-side plugins
- These are plugins that extend the ParaView GUI eg. property panels for new filters, toolbars, views etc. These plugins need to be loaded on the client.
Oftentimes a plugin has both server-side as well as client-side components to it eg. a plugin that adds a new filter and a property panel that goes with that filter. Such plugins need to be loaded both on the server as well as the client.
Generally, users don't have to worry whether a plugin is a server-side or client-side plugin. Simply load the plugin on the server as well as the client. ParaView will include relevant components from plugin on each of the processes.
There are two ways for loading plugins:
- Using the GUI (Plugin Manager)
- Plugins can be loaded into ParaView using the Plugin Manager accessible from Tools | Manage Plugins/Extensions menu. The Plugin Manager has two sections for loading local plugins and remote plugins (enabled only when connected to a server). To load a plugin on the local as well as remote side, simply browse to the plugin shared library. If the loading is successful, the plugin will appear in the list of loaded plugins. The Plugin manager also lists the paths it searched to load plugins automatically.
- Using environment variable (Auto-loading plugins)
- Plugins loaded using the Plugin Manager are not saved across sessions. Hence one has to reload those plugins every time ParaView is started. If one wants ParaView to automatically load a set of plugins on startup, one can use the PV_PLUGIN_PATH environment variable. PV_PLUGIN_PATH can be used to list a set of directories (separated by colon (:) or semi-colon (;)) which ParaView will search on startup to load plugins. This enviromnent variable needs to be set on both the client node to load local plugins as well as the remote server to load remote plugins.
Writing Plugins
This section covers writing and compiling different types of Plugins. To create a plugin, one must have their own build of ParaVeiw3. Binaries downloaded from www.paraview.org do not include necessary header files or import libraries (where applicable) for compiling plugins.
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.
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 ExtendingParaView. 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. <source lang="xml">
<ParaViewReaders> <Reader name="MyReader" extensions="myreader mr" file_description="My Reader Files"> </Reader> </ParaViewReaders>
</source> 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: <source lang="xml">
<RCC> <qresource prefix="/ParaViewResources" > <file>MyReader.xml</file> </qresource> </RCC>
</source>
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 )
In ParaView 3.3+, if you want to add your filter to a category such as "Common" or make your own category, you can create an XML file like the following, and add it to your qrc file. The icon is optional. The menu_label is optional. The "&" is a way to add a shortcut key in Qt.. <source lang="xml">
<ParaViewFilters> <Category name="Common"> <Filter name="Triangulate" icon=":/MyIcons/MyTriangulateIcon.png"/> </Category> <Category name="My Category" menu_label="&My Category"> <Filter name="MyFilter"/> </Category> </ParaViewFilters>
</source>
In ParaView 3.3+, the qrc file is optional. Instead, one can do:
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(MyFilterGUIPlugin "1.0" GUI_RESOURCE_FILES MyReader.xml )
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. <source lang="cpp">
#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(); pqObjectBuilder* builder = core->getObjectBuilder(); pqServerManagerModel* sm = core->getServerManagerModel(); pqUndoStack* stack = core->getUndoStack(); if(sm->getNumberOfServers()) { // just create it on the first server connection pqServer* s = sm->getServerByIndex(0); QString source_type = a->data().toString(); // make this operation undo-able if undo is enabled if(stack) { stack->beginUndoSet(QString("Create %1").arg(source_type)); } builder->createSource("sources", source_type.toAscii().data(), s); if(stack) { stack->endUndoSet(); } } } };
</source>
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.
To make your own, there is an explanation found on CustomObjectPanels
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:
<source lang="cpp">
#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)); } };
</source>
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: <source lang="xml">
<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>
</source>
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: <source lang="cpp">
#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; };
</source>
And MyDisplay.h is: <source lang="cpp">
#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)); } };
</source>
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)".
In ParaView 3.4, there's also a macro, ADD_PARAVIEW_VIEW_OPTIONS() which allows adding options pages for the custom view, accessible from Edit -> View Settings. The example in ParaView3/Examples/Plugins/GUIView demonstrates this (until more information is put here).