ParaView/Plugin HowTo: Difference between revisions
Line 321: | Line 321: | ||
<Hints> <!-- examine the above server manger xml to determine where in the file this goes --> | <Hints> <!-- examine the above server manger xml to determine where in the file this goes --> | ||
<!-- possibly other hints --> | <!-- possibly other hints --> | ||
< | <ShowInMenu icon=":/MyIcons/MyElevationIcon.png" /> | ||
</Hints> | </Hints> | ||
<!-- skip end of file --> | <!-- skip end of file --> |
Revision as of 14:51, 8 January 2015
Introduction
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 four 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.
- The Plugin Manager remembers all loaded plugins, so next time to load the plugin, simply locate it in the list and click "Load Selected" button.
- You can set up ParaView to automatically load the plugin at startup (in case of client-side plugins) or on connecting to the server (in case of server-side plugins) by checking the "Auto Load" checkbox on a loaded plugin.
- Using environment variable (Auto-loading plugins)
- 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 environment variable needs to be set on both the client node to load local plugins as well as the remote server to load remote plugins. Note that plugins in PV_PLUGIN_PATH are always auto-loaded irrespective of the status of the "Auto Load" checkbox in the Plugin Manager.
- Using the plugin file .plugins(Make plugins available and possibly Auto-load plugins)
- Plugins that are listed in the .plugins file on the client computer and server cluster will automatically be listed in the Plugin Manager, and optionally can be auto loaded. The .plugins file is automatically created at ParaView build time and includes all plugins that ParaView built. The .plugins file should be in the same directory as pvserver. An example .plugins file, auto loading H5PartReader, looks like this:
<source lang="xml"> <?xml version="1.0"?> <Plugins>
<Plugin name="Moments" auto_load="0"/> <Plugin name="PrismPlugin" auto_load="0"/> <Plugin name="PointSprite_Plugin" auto_load="0"/> <Plugin name="pvblot" auto_load="0"/> <Plugin name="SierraPlotTools" auto_load="0"/> <Plugin name="H5PartReader" auto_load="1"/>
</Plugins> </source>
- Placing the plugins in a recognized location. Recognized locations are:
- A plugins subdirectory beneath the directory containing the paraview client or server executables. This can be a system-wide location if installed as such.
- A Plugins subdirectory in the user's home area. On Unix/Linux/Mac, $HOME/.config/ParaView/ParaView<version>/Plugins. On Windows %APPDATA$\ParaView\ParaView<version>\Plugins.
Debugging Plugins
If plugin loading failed, try setting the PV_PLUGIN_DEBUG environment variable for all processes that you were trying to load the plugin on. ParaView will then try to print verbose information about each step and causes for failure, as show below.
<source lang="python">
Attempting to load /home/utkarsh/Kitware/ParaView3/ParaView3Bin/bin/libSurfaceLIC.so Loaded shared library successfully. Now trying to validate that it's a ParaView plugin. Plugin's signature: paraviewplugin|GNU|3.7 Plugin signature verification successful. This is definitely a ParaView plugin compiled with correct compiler for correct ParaView version. Updating Shared Library Paths: /home/utkarsh/Kitware/ParaView3/ParaView3Bin/bin Plugin instance located successfully. Now loading components from the plugin instance based on the interfaces it implements.
Plugin Information:
Name : SurfaceLIC Version : 1.0 ReqOnServer : 1 ReqOnClient : 1 ReqPlugins : ServerManager Plugin : Yes Python Plugin : No
</source>
Plugin debug information is not available for ParaView 3.6 or earlier
Writing Plugins
This section covers writing and compiling different types of Plugins. To create a plugin, one must have their own build of ParaView3. 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.
Adding a Filter
In this plugin, we want to add a new filter to ParaView. The filter has to be a VTK-based algorithm, written as following the standard procedures for writing VTK algorithms. Generally for such cases where we are adding a new VTK class to ParaView (be it a filter, reader or a writer), we need to do the following tasks:
- Write a Server Manager Configuration XML which describes the Proxy interface for the new VTK class. Basically, this defines the interface for the client to create and modify instances of the new class on the server side. Please refer to the ParaView Guide for details about writing these server-manager xmls.
- Write a configuration XML for the GUI to make ParaView GUI aware of this new class, if applicable. For filters, this is optional, since ParaView automatically recognizes filters added through plugins and lists them in the Alphabetical sub-menu. One may use the GUI configuration xml to add the new filter to a specific category in the Filters menu, or add a new category etc. For readers and writers, this is required since ParaView GUI needs to know what extensions your reader/writer supports etc.
Enabling an existing VTK filter
Sometimes, the filter that one wants to add to ParaView is already available in VTK, it's just not exposed through the ParaView GUI. This is the easiest type of plugin to create. There are two options: 1) setup the plugin using only an XML file and 2) actually compile the plugin into a shared library. The first option is the easiest, but the second option will prepare you for creating a custom filter in the future as the process is nearly identical.
XML Only
If you have not built Paraview from source, using an xml plugin is your only option.
We need to write the server manager configuration xml for the filter describing its API. The GUI xml to add the filter to any specific category is optional.
For example, let's say we simply want to expose the vtkCellDerivatives in VTK. Then first, we'll write the server manager configuration XML (call it CellDerivatives.xml), similar to what we would have done for adding a new filter.
<source lang="xml">
<ServerManagerConfiguration>
<ProxyGroup name="filters"> <SourceProxy name="MyCellDerivatives" class="vtkCellDerivatives" label="My Cell Derivatives"> <Documentation long_help="Create point attribute array by projecting points onto an elevation vector." short_help="Create a point array representing elevation."> </Documentation> <InputProperty name="Input" command="SetInputConnection"> <ProxyGroupDomain name="groups"> <Group name="sources"/> <Group name="filters"/> </ProxyGroupDomain> <DataTypeDomain name="input_type"> <DataType value="vtkDataSet"/> </DataTypeDomain> </InputProperty>
</SourceProxy> </ProxyGroup>
</ServerManagerConfiguration> </source>
At this point, we can stop and use the plugin in Paraview by loading the XML file directly into the plugin manager.
Please note that if you are writing the XML for a filter that takes just one input, you *must* set the "name" attribute for the InputProperty XML element to "Input". If you do not, then the filter will not be displayed properly in ParaView's pipeline browser.
If you have built Paraview from source, it is possible to compile the plugin into into a shared library. To do this, we can use the following CMakeLists.txt
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(CellDerivatives "1.0" SERVER_MANAGER_XML CellDerivatives.xml)
We can now load the plugin through the plugin manager by selecting the .so file.
Similarly compiled Qt resources (*.bqrc) can be loaded at runtime. *.bqrc is a binary file containing resources which can include icons, the GUI configuration xmls for adding catergories etc. A .bqrc can be made from a .qrc by running the rcc utility provided by Qt: <source lang="text"> rcc -binary -o myfile.bqrc myfile.qrc. </source>
Adding a new VTK filter
For this example, refer to Examples/Plugins/Filter in the ParaView source. Let's say we have written a new vtkMyElevationFilter (vtkMyElevationFilter.h|cxx), which extends the functionality of the vtkElevationFilter and we want to package that as a plugin for ParaView. For starters, we simply want to use this filter in ParaView (not doing anything fancy with Filters menu categories etc.). As described, we need to write the server manager configuration XML (MyElevationFilter.xml). Once that's done, we write a CMakeLists.txt file to package this into a plugin.
This CMakeLists.txt simply needs to include the following lines:
# Locate ParaView build and then import CMake configuration, # macros etc. from it. FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) # Use the ADD_PARAVIEW_PLUGIN macro to build a plugin ADD_PARAVIEW_PLUGIN( MyElevation #<--Name for the plugin "1.0" #<--Version string SERVER_MANAGER_XML MyElevationFilter.xml #<-- server manager xml SERVER_MANAGER_SOURCES vtkMyElevationFilter.cxx #<-- source files for the new classes )
Then using cmake and a build system, one can build a plugin for this new filter. Once this plugin is loaded the filter will appear under the "Alphabetical" list in the Filters menu.
Filters with Multiple Input Ports
If your filter requires multiple input ports, you have two options - 1) You can create helper functions in the VTK filter such as SetYourInputName which deal with addressing the VTK pipeline in the c++ code. 2) Address/access the input connection by number in the XML. The port_index property specifies which input connection the particular input will be connected to. The SetInputConnection function is the command that will actually be called with this port_index to setup the pipeline.
An example XML file for a filter with multiple inputs is below. The filter takes three vtkPolyData's as input.
<source lang="xml">
<ServerManagerConfiguration>
<ProxyGroup name="filters"> <SourceProxy name="LandmarkTransformFilter" class="vtkLandmarkTransformFilter" label="LandmarkTransformFilter"> <Documentation long_help="Align two point sets using vtkLandmarkTransform to compute the best transformation between the two point sets." short_help="vtkLandmarkTransformFilter."> </Documentation>
<InputProperty name="SourceLandmarks"
port_index="0"
command="SetInputConnection"> <ProxyGroupDomain name="groups"> <Group name="sources"/> <Group name="filters"/> </ProxyGroupDomain> <DataTypeDomain name="input_type"> <DataType value="vtkPolyData"/> </DataTypeDomain> <Documentation> Set the source data set. This data set that will move towards the target data set. </Documentation> </InputProperty>
<InputProperty name="TargetLandmarks"
port_index="1"
command="SetInputConnection"> <ProxyGroupDomain name="groups"> <Group name="sources"/> <Group name="filters"/> </ProxyGroupDomain> <DataTypeDomain name="input_type"> <DataType value="vtkPolyData"/> </DataTypeDomain> <Documentation> Set the target data set. This data set will stay stationary. </Documentation> </InputProperty>
<InputProperty name="SourceDataSet"
port_index="2"
command="SetInputConnection"> <ProxyGroupDomain name="groups"> <Group name="sources"/> <Group name="filters"/> </ProxyGroupDomain> <DataTypeDomain name="input_type"> <DataType value="vtkPolyData"/> </DataTypeDomain> <Documentation> Set the source data set landmark points. </Documentation> </InputProperty>
<Hints> </Hints>
</SourceProxy> </ProxyGroup>
</ServerManagerConfiguration> </source>
To set the inputs in Paraview, simply select one of the inputs in the Pipeline Browser and then select the filter from the Filters menu. This will open a dialog box which will allow you to specify which object to connect to each input port.
Adding Categories to the Filters Menu
Now suppose we want to add a new category to the Filters menu, called "Extensions" and then show this filter in that submenu. In that case we need to add a hint to the XML file that tells ParaView what category to display this filter in.
<source lang="xml">
<Hints> <ShowInMenu category="Extensions" /> </Hints>
</source>
Prior to ParaView 4.0, we need a GUI configuration xml to tell the ParaView GUI to create the category. However, as of ParaView 4.3 the GUI configuration xml does nothing and the above method must be followed. This GUI configuration xml will look as such:
<source lang="xml">
<ParaViewFilters> <Category name="Extensions" menu_label="&Extensions"> <Filter name="MyElevationFilter" /> </Category> </ParaViewFilters>
</source>
If the name of the category is same as an already existsing category eg. Data Analysis, then the filter gets added to the existing category.
The CMakeLists.txt must change to include this new xml (let's call it MyElevationGUI.xml) as follows:
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(MyElevation "1.0" SERVER_MANAGER_XML MyElevationFilter.xml SERVER_MANAGER_SOURCES vtkMyElevationFilter.cxx GUI_RESOURCE_FILES MyElevationGUI.xml)
Again, the GUI configuration xml is removed and will do nothing as of ParaView 4.3. If the option is specified in the CMakeLists.txt file, CMake will warn about its use.
Adding Icons
You can see that some filters in the Filters menu (eg. Clip) have icons associated with them. It's possible for the plugin to add icons for filters it adds as well. For that you need to write a Qt resource file (say MyElevation.qrc) as follows:
<source lang="xml">
<RCC> <qresource prefix="/MyIcons" > <file>MyElevationIcon.png</file> </qresource> </RCC>
</source>
To use the icon for a filter in the pipeline add the following hint to the server manager xml. <source lang="xml">
<Hints> <ShowInMenu icon=":/MyIcons/MyElevationIcon.png" /> </Hints>
</source>
Prior to ParaView 4.0, the GUI configuration xml now refers to the icon provided by this resource as follows: <source lang="xml">
<ParaViewFilters> <Category name="Extensions" menu_label="&Extensions"> <Filter name="MyElevationFilter" icon=":/MyIcons/MyElevationIcon.png" /> </Category> </ParaViewFilters>
</source>
Finally, the CMakeLists.txt file much change to include our MyElevation.qrc file as follows:
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(MyElevation "1.0" SERVER_MANAGER_XML MyElevationFilter.xml SERVER_MANAGER_SOURCES vtkMyElevationFilter.cxx GUI_RESOURCES MyElevation.qrc)
Adding GUI Parameters
Simply add these in the server manager xml to expose parameters of the filter to the paraview user.
Integer property
This property appears as a text box. <source lang="xml">
<IntVectorProperty name="bStartByMatchingCentroids" command="SetbStartByMatchingCentroids" number_of_elements="1" default_values="1"> </IntVectorProperty>
</source>
Boolean property
This property appears as a check box control. A boolean property uses the IntVectorProperty with an extra line (BooleanDomain...) indicating this should be a check box rather than a text field. <source lang="xml">
<IntVectorProperty name="bStartByMatchingCentroids" command="SetbStartByMatchingCentroids" number_of_elements="1" default_values="1"> <BooleanDomain name="bool"/> </IntVectorProperty>
</source>
String property
This property appears as a text box. <source lang="xml">
<StringVectorProperty name="YourStringVariable" command="SetYourStringVariable" number_of_elements="1" default_values="1"> </StringVectorProperty>
</source>
Double property
This property appears as a text box. <source lang="xml">
<DoubleVectorProperty name="YourDoubleVariable" command="SetYourDoubleVariable" number_of_elements="1" default_values="1"> </DoubleVectorProperty>
</source>
Multi-Value Double property
This property appears as a text box. <source lang="xml">
<DoubleVectorProperty name="YourDoubleVectorVariable" command="SetYourDoubleVectorVariable" number_of_elements="3" default_values="1.0 0.0 0.0"> </DoubleVectorProperty>
</source>
Double property slider
This creates a slider that ranges from 0.0 to 1.0 <source lang="xml">
<DoubleVectorProperty name="PercentToRemove" command="SetPercentToRemove" number_of_elements="1" default_values="0.1"> <DoubleRangeDomain name="range" min="0.0" max="1.0" /> </DoubleVectorProperty>
</source>
Drop down list
This creates a drop down list with 3 choices. The values associated with the choices are specified. <source lang="xml">
<IntVectorProperty name="TransformMode" command="SetTransformMode" number_of_elements="1" default_values="1"> <EnumerationDomain name="enum"> <Entry value="6" text="RigidBody"/> <Entry value="7" text="Similarity"/> <Entry value="12" text="Affine"/> </EnumerationDomain> <Documentation> This property indicates which transform mode will be used. </Documentation> </IntVectorProperty>
</source>
Drop down list with values from input arrays
This creates a list that lets you choose among the input arrays of the input of a ProgrammableFilter:
<source lang="xml"> <StringVectorProperty name="SelectInputScalars"
label="Array" command="SetInputArrayToProcess" number_of_elements="5" element_types="0 0 0 0 2" animateable="0"> <ArrayListDomain name="array_list" attribute_type="Scalars" input_domain_name="inputs_array"> <RequiredProperties> <Property name="Input" function="Input" /> </RequiredProperties> </ArrayListDomain> <FieldDataDomain name="field_list"> <RequiredProperties> <Property name="Input" function="Input" /> </RequiredProperties> </FieldDataDomain> </StringVectorProperty>
</source>
This will look like the following image:
Drop down list with values from input file
If you need to populate a list with values from a file and be able to select/deselect list entries (e.g., to pick which variables are loaded from the file), use a XML similar to this:
<source lang="xml">
<StringVectorProperty information_only="1" name="CellArrayInfo"> <ArraySelectionInformationHelper attribute_name="Cell" /> </StringVectorProperty> <StringVectorProperty command="SetCellArrayStatus" element_types="2 0" information_property="CellArrayInfo" label="Cell Arrays" name="CellArrayStatus" number_of_elements="0" number_of_elements_per_command="2" repeat_command="1"> <ArraySelectionDomain name="array_list"> <RequiredProperties> <Property function="ArrayList" name="CellArrayInfo" /> </RequiredProperties> </ArraySelectionDomain> <Documentation>This property lists which cell-centered arrays to read.</Documentation> </StringVectorProperty> <StringVectorProperty information_only="1" name="PointArrayInfo"> <ArraySelectionInformationHelper attribute_name="Point" /> </StringVectorProperty>
</source>
You can see an example in use in the following file:
ParaView/ParaViewCore/ServerManager/SMApplication/Resources/readers.xml
You can also do it in the following manner:
<source lang="xml">
<StringVectorProperty command="SetCellArrayStatus" element_types="2 0" information_property="CellArrayInfo" label="Cell Arrays" name="CellArrayStatus" number_of_elements="0" number_of_elements_per_command="2" repeat_command="1"> <ArrayListDomain name="array_list" attribute_type="Scalars" input_domain_name="inputs_array"> <RequiredProperties> <Property name="Input" function="Input" /> </RequiredProperties> </ArrayListDomain> </StringVectorProperty>
</source>
In which case the result will look like this:
Tutorials for creating filters
Go to this page for the main article for the tutorials: Python Filters Tutorials
Adding a Reader
Adding a new reader through a plugin is similar to adding a filter. The only difference is that we do not need to specify what category the reader should be added to in the GUI. For the latest version of ParaView we do not need to specify anything special for the GUI as all of the details of the reader are available in the xml proxy definition of the reader. For ParaView version 4.0.1 and earlier we need the xml to define what file extensions this reader can handle. This xml (MyReaderGUI.xml) looks like this:
<source lang="xml">
<ParaViewReaders> <Reader name="MyPNGReader" extensions="png" file_description="My PNG Files"> </Reader> </ParaViewReaders>
</source>
An example MyPNGReader.xml is shown below. In almost all cases you must have a SetFileName function property. You are free to have other properties as well, as with a standard (non-reader) filter. Also, the Hints section is needed in order to associate the file extension with the reader on the client. In ParaView 4.3 and later, the Hints section ReaderFactory hint is what the client uses to identify readers from sources.
<source lang="cmake"> <ServerManagerConfiguration>
<ProxyGroup name="sources"> <SourceProxy name="MyPNGReader" class="vtkMyPNGReader" label="PNGReader"> <Documentation long_help="Read a PNG file." short_help="Read a PNG file."> </Documentation> <StringVectorProperty name="FileName" animateable="0" command="SetFileName" number_of_elements="1"> <FileListDomain name="files"/> <Documentation> This property specifies the file name for the PNG reader. </Documentation> </StringVectorProperty> <Hints> <ReaderFactory extensions="png" file_description="PNG File Format" /> </Hints> </SourceProxy> </ProxyGroup>
</ServerManagerConfiguration>
</source>
And the CMakeLists.txt looks as follows where vtkMyPNGReader.cxx is the source for the reader and MyPNGReader.xml is the server manager configuration xml:
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(MyReader "1.0" SERVER_MANAGER_XML MyPNGReader.xml SERVER_MANAGER_SOURCES vtkMyPNGReader.cxx REQUIRED_ON_SERVER)
Note that this is for the latest version of ParaView. For ParaView 4.0.1 and earlier the CMakeLists.txt file needs to include the GUI xml to associate the reader with the file name extension. This looks like:
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(MyReader "1.0" SERVER_MANAGER_XML MyPNGReader.xml SERVER_MANAGER_SOURCES vtkMyPNGReader.cxx GUI_RESOURCE_FILES MyReaderGUI.xml)
If you want your reader to work correctly with a file series, please refer to file series animation for details.
Once you generate the project using CMake and compile the project, in ParaView go to "Tools->Manage Plugins/Extensions". Under "Local Plugins", click "Load New" and browse for the shared library file you just created. You should now see your new file type in the "Files of type" list in the "Open file" dialog.
Adding a Writer
Similar to a reader plugin, for a writer plugin we need to tell ParaView what extensions this writer supports. For the current version of ParaView this is done in the Hints section of the server manager xml definition as follows:
<source lang="xml">
<Hints> <WriterFactory extensions="tif" file_description="My Tiff Files" /> </Hints>
</source>
For ParaView version 4.0.1 and earlier this is done in the GUI xml as follows:
<source lang="xml">
<ParaViewWriters> <Writer name="MyTIFFWriter" extensions="tif" file_description="My Tiff Files"> </Writer> </ParaViewWriters>
</source>
Adding Customizations for Properties Panel
* new in 4.0
Properties Panel is the primary panel in ParaView used to change the parameters for visualization modules and displays. Plugins can provide new types of pqPropertyWidget subclasses that can be used to control properties/property groups on this Properties panel.
To register a new pqPropertyWidget subclass to be associated with a particular widget type for a property (vtkSMProperty), use the following:
add_paraview_property_widget(IFACES IFACE_SRCS TYPE "string-type-name" CLASS_NAME "class-name")
The CLASS_NAME must refer to a pqPropertyWidget subclass with a constructor with the following prototype:
<source lang="cpp">
ClassName(vtkSMProxy *smproxy, vtkSMProperty *smproperty, QWidget *parentObject=0)
</source>
The TYPE specifies the string that will be used in the ServerManager XML as the value for the panel_widget attribute to request creation of this widget for a vtkSMProperty subclass.
To register a new pqPropertyWidget subclass to be associated with a particular widget type for a property group (vtkSMPropertyGroup), use the following:
add_paraview_property_group_widget(IFACES IFACE_SRCS TYPE "string-type-name" CLASS_NAME "class-name")
The CLASS_NAME must refer to a pqPropertyWidget subclass with a constructor with the following prototype:
<source lang="cpp">
ClassName(vtkSMProxy *smproxy, vtkSMPropertyGroup *smgroup, QWidget *parentObject=0);
</source>
As before, the TYPE specifies the string that will be used in the ServerManager XML as the value for the panel_widget attribute on a <PropertyGroup/> element to request creation of this widget for that group.
Another mechanism for adding customizations for Properties panel is to provide pqPropertyWidgetDecorator subclasses to add custom control logic for widgets on the panel.
Decorators can be registered as follows:
add_paraview_property_widget_decorator(IFACES IFACE_SRCS TYPE "string-type-name" CLASS_NAME "class-name")
The CLASS_NAME must point to a pqPropertyWidgetDecorator subclass and the TYPE is the string name used to request the creation of the decorator in the ServerManager XML as described here.
An example for customizing the Properties panel can be found in the ParaView source under Examples/Plugins/PropertyWidgets.
Adding Documentation for Plugins
Starting with ParaView 3.14, developers can provide documentation for plugins that is shown in the ParaView Help Window. There are two mechanisms for adding documentation from plugins.
- And SERVER_MANAGER_XML files added to the ADD_PARAVIEW_PLUGIN macro are automatically parsed to process <Documentation /> elements. HTML pages summarizing the proxy and properties are automatically generated. This ensures that when the user click "?" for a filter/source added via the plugin, the help window shows appropriate help pages.
- Using DOCUMENTATION_DIR command in the call to ADD_PARAVIEW_PLUGIN() to specify a directory containing html pages and/or images that gets added a the documentation for the plugin (in addition to the documentation generated using the SERVER_MANAGER_XML files e.g.
ADD_PARAVIEW_PLUGIN(SurfaceLIC "1.0" DOCUMENTATION_DIR "${CMAKE_CURRENT_SOURCE_DIR}/doc" SERVER_MANAGER_XML ${SM_XMLS} ...)
This results in adding documentation to the "ParaView Online Help" when the plugin is loaded, as shown below.
Adding a Toolbar
Filters, reader and writers are by far the most common ways for extending ParaView. However, ParaView plugin functionality goes far beyond that. The following sections cover some of these advanced plugins that can be written.
Applications use toolbars to provide easy access to commonly used functionality. It is possible to have plugins that add new toolbars to ParaView. The plugin developer implements his own C++ code to handle the callback for each button on the toolbar. Hence one can do virtually any operation using the toolbar plugin with some understanding of the ParaView Server Manager framework and the ParaView GUI components.
Please refer to Examples/Plugins/SourceToolbar for this section. There we are adding a toolbar with two buttons to create a sphere and a cylinder source. For adding a toolbar, one needs to implement a subclass for QActionGroup which adds the QActions for each of the toolbar button and then implements the handler for the callback when the user clicks any of the buttons. In the example SourceToobarActions.h|cxx is the QActionGroup subclass that adds the two tool buttons.
To build the plugin, the CMakeLists.txt file is:
# We need to wrap for Qt stuff such as signals/slots etc. to work correctly. QT4_WRAP_CPP(MOC_SRCS SourceToolbarActions.h) # This is a macro for adding QActionGroup subclasses automatically as toolbars. ADD_PARAVIEW_ACTION_GROUP(IFACES IFACE_SRCS CLASS_NAME SourceToolbarActions GROUP_NAME "ToolBar/SourceToolbar") # Now create a plugin for the toolbar. Here we pass IFACES and IFACE_SRCS # which are filled up by the above macro with relevant entries ADD_PARAVIEW_PLUGIN(SourceToolbar "1.0" GUI_INTERFACES ${IFACES} SOURCES ${MOC_SRCS} ${IFACE_SRCS} SourceToolbarActions.cxx)
For the GROUP_NAME, we are using ToolBar/SourceToolbar; here ToolBar is a keyword which implies that the action group is a toolbar (and shows up under View | Toolbars menu) with the name SourceToolbar. When the plugin is loaded, this toolbar will show up with two buttons.
Adding a Menu
Adding a menu to the menu bar of the main window is almost identical to #Adding a Toolbar. The only difference is that you use the keyword MenuBar in lieu of ToolBar in the GROUP_NAME of the action group. So if you change the ADD_PARAVIEW_ACTION_GROUP command above to the following, the plugin will add a menu titled MyActions to the menu bar.
ADD_PARAVIEW_ACTION_GROUP(IFACES IFACE_SRCS CLASS_NAME SourceToolbarActions GROUP_NAME "MenuBar/MyActions")
If you give the name of an existing menu, then the commands will be added to that menu rather than create a new one. So, for example, if the GROUP_NAME is MenuBar/File, the commands will be added to the bottom of the File menu.
Adding Custom Property Widgets
Autostart Plugins
This refers to a plugin which needs to be notified when ParaView starts up or the plugin is loaded which ever happens later and then notified when ParaView quits. Example is in Examples/Plugins/Autostart in the ParaView source. For such a plugin, we need to provide a QObject subclass (pqMyApplicationStarter) with methods that need to be called on startup and shutdown.
<source lang="cpp"> ... class pqMyApplicationStarter : public QObject { ... public:
// Callback for startup. // This cannot take any arguments void onStartup();
// Callback for shutdown. // This cannot take any arguments void onShutdown();
... }; </source>
The CMakeLists.txt looks as follows:
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE})
QT4_WRAP_CPP(MOC_SRCS pqMyApplicationStarter.h) # Macro for auto-start plugins. We specify the class name # and the methods to call on startup and shutdown on an instance of that class. # It fills IFACES and IFACE_SRCS with proper values as needed by ADD_PARAVIEW_PLUGIN macro. ADD_PARAVIEW_AUTO_START(IFACES IFACE_SRCS CLASS_NAME pqMyApplicationStarter # the class name for our class STARTUP onStartup # specify the method to call on startup SHUTDOWN onShutdown # specify the method to call on shutdown ) # Create a plugin for this starter ADD_PARAVIEW_PLUGIN(Autostart "1.0" GUI_INTERFACES ${IFACES} SOURCES pqMyApplicationStarter.cxx ${MOC_SRCS} ${IFACE_SRCS})
Adding a custom view * obsolete *
Although the general procedure remains the same, the source code in this section is obsolete. See the Examples/Plugins/GUIView and Plugins/MantaView/ParaView directories of the ParaView source code for more up-to-date examples. Also, this e-mail thread discusses some issues of interest.
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).
Adding new Representations for 3D View using Plugins * new in version 3.7
ParaView’s 3D view the most commonly used view for showing polygonal or volumetric data. By default, ParaView provides representation-types for showing the dataset as surface, wireframe, points etc. It’s possible to add representations using plugins that extends this set of available representation-types.
Before we start looking at how to write such a plugin, we need to gain some understanding of the 3D view and its representations. The 3D view uses 3 basic representation proxies for rendering all types of data:
- (representations, UnstructuredGridRepresentation) – for vtkUnstructuredGrid or a composite dataset consisting of vtkUnstructuredGrid.
- (representations, UniformGridRepresentation) – for vtkImageData or a composite dataset consisting of vtkImageData
- (representations, GeometryRepresentation) – for all other data types.
Each of these representation proxies are basically composite-representation proxies that use other representation proxies to do the actual rendering e.g. GeometryRepresentation uses SurfaceRepresentation for rendering the data as wireframe, points, surface and surface-with-edges and OutlineRepresentation for rendering an outline for the data. Subsequently, the 3 composite-representation proxies provide a property named Representation which allows the user to pick the representation type he wants to see the data as. The composite-representation proxy has logic to enable one of its internal representations based on the type chosen by the user.
These 3-composite representation types are fixed and cannot be changed by plugins. What plugins can do is add more internal representations to any of these 3 composite representations to support new representations types, that the user can choose using the representation-type combo box on the display tab or in the toolbar.
Using a new Mapper
In this example, we see how to integrate a special poly-data mapper written in VTK into ParaView. Let’s say the mapper is called vtkMySpecialPolyDataMapper which is simply a subclass of vtkPainterPolyDataMapper. In practice, vtkMySpecialPolyDataMapper can internally use different painters to do perform special rendering tasks.
To integrate this mapper into ParaView first we need to create a vtkSMRepresentationProxy subclass for that uses this mapper. In this example, since the mapper is a simple replacement for the standard vtkPainterPolyDataMapper, we can define our representation proxy as a specialization of the “SurfaceRepresentation” as follows:
<source lang="xml"> <ServerManagerConfiguration>
<ProxyGroup name="representations"> <RepresentationProxy name="MySpecialRepresentation" class="vtkMySpecialRepresentation" processes="client|renderserver|dataserver" base_proxygroup="representations" base_proxyname="SurfaceRepresentation"> <Documentation> This is the new representation type we are adding. This is identical to the SurfaceRepresentation except that we are overriding the mapper with our mapper. </Documentation> </RepresentationProxy> </ProxyGroup>
</ServerManagerConfiguration> </source>
vtkMySpecialRepresentation is a subclass of vtkGeometryRepresentationWithFaces where in the constructor we simply override the mappers as follows:
<source lang="cpp"> //---------------------------------------------------------------------------- vtkMySpecialRepresentation::vtkMySpecialRepresentation() {
// Replace the mappers created by the superclass. this->Mapper->Delete(); this->LODMapper->Delete();
this->Mapper = vtkMySpecialPolyDataMapper::New(); this->LODMapper = vtkMySpecialPolyDataMapper::New();
// Since we replaced the mappers, we need to call SetupDefaults() to ensure // the pipelines are setup correctly. this->SetupDefaults();
} </source>
Next we need to register this new type with the any (or all) of the 3 standard composite representations so that it will become available to the user to choose in the representation type combo-box.
To decide which of the 3 composite representations we want to add our representation to, think of the input data types our representation supports. If it can support any type of data set, then we can add our representation all the 3 representations (as is the case with this example). However if we are adding a representation for volume rendering of vtkUnstructuredGrid then we will add it only to the UnstructuredGridRepresentation. This is done by using the Extension xml tag. It simply means that we are extending the original XML for the proxy definition with the specified additions. Now to make this representation available as a type to the user, we use the <RepresentationType /> element , with “text” used as the text shown for the type in the combo-box, “subproxy” specifies the name of representation –subproxy to activate when the user chooses the specified type. Optionally one can also specify the “subtype” attribute, which if present is the value set on a property named “Representation” for the subproxy when the type is chosen. This allows for the subproxy to provide more than one representation type.
<source lang="xml"> <ServerManagerConfiguration>
<ProxyGroup name="representations"> <Extension name="GeometryRepresentation"> <Documentation> Extends standard GeometryRepresentation by adding MySpecialRepresentation as a new type of representation. </Documentation>
<RepresentationType subproxy="MySpecialRepresentation" text="Special Mapper" subtype="1" />
<SubProxy> <Proxy name="MySpecialRepresentation" proxygroup="representations" proxyname="MySpecialRepresentation"> </Proxy> <ShareProperties subproxy="SurfaceRepresentation"> <Exception name="Input" /> <Exception name="Visibility" /> <Exception name="Representation" /> </ShareProperties> </SubProxy> </Extension>
</ProxyGroup>
</ServerManagerConfiguration> </source>
The CMakeLists.txt file is not much different from what it would be like for adding a simple filter or a reader.
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) ADD_PARAVIEW_PLUGIN(Representation "1.0" SERVER_MANAGER_XML Representation.xml SERVER_MANAGER_SOURCES vtkMySpecialPolyDataMapper.cxx vtkMySpecialRepresentation.cxx )
Source code for this example is available under Examples/Plugins/Representation in the ParaView source directory.
Using Hardware Shaders
One common use-case for adding new representations is to employ specialized hardware shaders written using shading languages such as GLSL or Cg to perform specialized rendering. Such special rendering algorithms can be encapsulated in a special mapper or a vtkPainter subclass and then making a special mapper that uses the painter.
In this example, we have a new vtkPainter subclasses vtkVisibleLinePainter that uses shaders to prune hidden lines from a wireframe rendering. Following is the CMakeLists.txt
FIND_PACKAGE(ParaView REQUIRED) INCLUDE(${PARAVIEW_USE_FILE}) # Compile-in all GLSL files are strings. # const char* with the names same as that of the file then become available for # use. encode_files_as_strings(ENCODED_STRING_FILES vtkPVLightingHelper_s.glsl vtkPVColorMaterialHelper_vs.glsl vtkVisibleLinesPainter_fs.glsl vtkVisibleLinesPainter_vs.glsl ) add_paraview_plugin( HiddenLinesRemoval "1.0" SERVER_MANAGER_XML HiddenLinesRemovalPlugin.xml SERVER_MANAGER_SOURCES vtkVisibleLinesPolyDataMapper.cxx SOURCES vtkPVColorMaterialHelper.cxx vtkPVLightingHelper.cxx vtkVisibleLinesPainter.cxx ${ENCODED_STRING_FILES} )
vtkVisibleLinesPolyDataMapper is simply a vtkPainterPolyDataMapper subclass, like the previous example, which inserts the vtkVisibleLinesPainter at the appropriate location in the painter chain. The server manager configuration xml doesn’t look much different from the Using a new Mapper example except that we replace the mapper to be vtkVisibleLinesPolyDataMapper.
Source code for this example is available under Examples/Plugins/HiddenLineRemoval in the ParaView source directory.
Embedding Python Source as Modules
Embedding Python source was first available in ParaView 3.6. Also be aware that you need Python 2.3 or greater to be able to load a plugin with embedded Python source.
It is possible to take a Python module written in Python source code and embed it into a ParaView plugin. Once the plugin is loaded, the Python interpreter within the ParaView client (or pvpython or pvbatch) can access your module using the Python import command. Of course, Python has its own way of distributing modules; however, if your Python source relies on, say, a filter defined in a plugin or something else in a plugin, like a toolbar, relies on executing your Python module, then it can be more convenient to distribute and load everything if they are all wrapped into a single plugin.
Let us say that you have a file named helloworld.py with the following contents.
<source lang="python"> def hello():
print "Hello world"
</source>
You can add this to a plugin by simply listing the file in the PYTHON_MODULES option of ADD_PARAVIEW_PLUGIN. Note that the file must be located in the same directory as the CMakeLists.txt file (more on that later).
ADD_PARAVIEW_PLUGIN(MyPythonModules "1.0" PYTHON_MODULES helloworld.py )
Once you load this plugin into ParaView (no matter how you do it), you can then access this source code by importing the helloworld module.
<source lang="python"> >>> paraview.servermanager.LoadPlugin('libPythonTest.dylib') >>> import helloworld >>> helloworld.hello() Hello world </source>
Note that if you are using the ParaView client GUI, you can load the plugin through the GUI's Plugin Manager or by autoloading the plugin (as described in #Using Plugins) instead of using the LoadPlugin Python command. You do, however, need the import command.
It is also possible to have multiple modules and to embed packages with their own submodules (with an arbitrary depth of packages). You can set this up by simply arranging your Python source in directories representing the packages in the same way you set them up if you were loading them directly from Python (in fact, that might simplify debugging your Python code). If you have a file named __init__.py, that file is taken to be the implementation of the package represented by the directory it is contained in. This is the same behavior as Python itself.
ADD_PARAVIEW_PLUGIN(MyPythonModules "1.0" PYTHON_MODULES helloworld.py # Becomes module helloworld hello/__init__.py # Becomes package hello hello/world.py # Becomes module hello.world )
Note that when Python imports a module, it first imports all packages in which it is contained. The upshot is that if you define a module in a package within your plugin, you must also make sure that the package is also defined somewhere. In the example above, if you removed the hello/__init__.py source file, you would not be able to load the hello/world.py file. Thus, it is best to include a __init__.py in every package directory you make, even if it is empty.
Examples
The ParaView CVS repository contains many examples in the Plugins directory. Additional examples are available on this wiki at the Plugin Examples entry.
Adding plugins to ParaView source
There are several plugins that are included in ParaView source itself and are built as part of ParaView's build process. To add such a plugin to the ParaView build there are two options:
- Place the source for the plugin in a directory under ParaView/Plugins.
- Add the source directory to the CMake variable EXTRA_EXTERNAL_PLUGIN_DIRS when building ParaView.
Both approaches result in identical results.
In general users should simply build their plugins separately, outside the ParaView source. However, when building ParaView statically, adding the plugin to be built as part of ParaView ensures that the static executables load the plugin, otherwise there is no mechanism for loading a plugin in statically built executables.
In your plugin source directory, ParaView searches for a file name "plugin.cmake" which provides ParaView with information about the plugin. This file should contain the following code:
# Contents of a typical plugin.cmake file pv_plugin(<PluginName> # Provide brief description for the plugin used as documentation for # the PARAVIEW_BUILD_PLUGIN_<PluginName> cmake option provided to the user. DESCRIPTION "<text>" # If you want the plugin to be auto-loaded when ParaView starts, specify this option. # Users can manually mark any plugin to be auto-loaded using the Plugin Manager dialog. # This option is ignore for static-builds. All enabled plugins are auto-loaded in static # builds. AUTOLOAD # Specify this option if PARAVIEW_BUILD_PLUGIN_<PluginName> option should default to TRUE. # If not specified, it defaults to FALSE and the user must turn it ON to build this plugin. # Note the user can always turn PARAVIEW_BUILD_PLUGIN_<PluginName> off using cmake. DEFAULT_ENABLED # If providing more than 1 plugin or plugin is named differently (in add_paraview_plugin call) # than the <PluginName> specified, # you can use this option to notify ParaView of the plugin library names. ParaView uses these # names to locate the plugin at run time. PLUGIN_NAMES Name1 Name2 )
If now the plugin is enabled (by the user or by default) by turning ON the PARAVIEW_BUILD_PLUGIN_<PluginName> cmake option, then CMake will look for a CMakeLists.txt file next to the plugin.cmake. This file contains the calls to build the plugin including the add_paraview_plugin(...) call, and building of any other libraries that the plugin needs.
A good place to start would be look at examples under ParaView/Plugins directory.
Plugins in Static Applications
This functionality is new in ParaView 3.12
It is possible to import plugins into a ParaView-based application at compile time. When building ParaView-based applications statically, this is the only option to bring in components from plugins. When built statically (i.e. with BUILD_SHARED_LIBS set to false), ParaView will automatically link and load plugins that were enabled via CMake by inserting the necessary PV_PLUGIN_IMPORT_INIT and PV_PLUGIN_IMPORT macros.
The code below shows how the PV_PLUGIN macros would be used to statically load plugins in custom applications:
<source lang="cpp">
- include "vtkPVPlugin.h"
// Adds required forward declarations. PV_PLUGIN_IMPORT_INIT(MyFilterPlugin) PV_PLUGIN_IMPORT_INIT(MyReaderPlugin)
class MyMainWindow : public QMainWindow { // .... };
MyMainWindow::MyMainWindow(...) { // ... after initialization ...
// Calls relevant callbacks to load the plugins and update the // GUI/Server-Manager PV_PLUGIN_IMPORT(MyFilterPlugin); PV_PLUGIN_IMPORT(MyReaderPlugin);
} </source>
Pitfalls
Tools->Manage Plugins is not visible!
Plugins can only be loaded dynamically when ParaView is built with shared libraries. You must recompile Paraview with BUILD_SHARED_LIBS ON.
SYNTAX ERROR found in parsing the header file
When writing a VTK reader, filter, or writer for use with Paraview, any variable declaration in header files involving VTK classes or your own derived data type has to be wrapped in a "//BTX" "//ETX" pair of comments to tell the parser (Paraview's vtkWrapClientServer) to ignore these lines. The following is an example based on ParaView/Examples/Plugins/Filter/vtkMyElevationFilter.h: <source lang="cpp">
class VTK_EXPORT vtkMyElevationFilter : public vtkElevationFilter { private: vtkMyElevationFilter(const vtkMyElevationFilter&); void operator=(const vtkMyElevationFilter&);
//BTX vtkSmartPointer<vtkPolyData> Source; vtkSmartPointer<vtkPolyData> Target; //ETX };
</source>
If these tags are omitted, building the plugin will fail with an error message like the following: <source lang="text">
- SYNTAX ERROR found in parsing the header file <something>.h before line <line number> ***
</source>
Compile error "invalid conversion from ‘vtkYourFiltersSuperClass*’ to ‘vtkYourFilter*’"
Any VTK object that needs to be treated as a filter or source has to be a vtkAlgorithm subclass. The particular superclass a filter is derived from has to be given not only in the standard C++ way <source lang="cpp"> class VTK_EXPORT vtkMyElevationFilter : public vtkElevationFilter </source>
but additionally declared with help of the "vtkTypeRevisionMacro". For the example given above <source lang="cpp">
class VTK_EXPORT vtkMyElevationFilter : public vtkElevationFilter { public: vtkTypeRevisionMacro(vtkMyElevationFilter, vtkElevationFilter); }
</source>
Otherwise, compiling the filter will fail with a variety of error messages (depending on superclass) like <source lang="cpp">
vtkMyElevationFilter.cxx:19: error: no 'void vtkMyElevationFilter::CollectRevisions(std::ostream&)' member function declared in class 'vtkMyElevationFilter'
</source> or <source lang="cpp">
vtkMyElevationFilterClientServer.cxx:97: error: invalid conversion from ‘vtkPolyDataAlgorithm*’ to ‘vtkICPFilter*’
</source>
Plugin loaded, but invalid ELF header
What would cause this???
Undefined symbol _ZTV12vtkYourFilter
When you load your plugin, if you see a yellow ! warning triangle that says "undefined symbol....", you need to add <source lang="cpp"> vtkCxxRevisionMacro(vtkYourFilter, "$Revision$"); </source> to your header file and recompile the plugin.
Mysterious Segmentation Faults in plugins that use custom VTK classes
This primarily concerns plugins that make calls to your own custom "vtkMy"(or whatever you called it) library of VTK extensions.
Symptoms:
- The plugin will load, but causes a segfault when you try to use it.
- If you use a debugger you may notice that in some cases when your code calls vtkClassA.MethodB, what actually gets called is vtkClassC.MethodD, where MethodB is a virtual member function. This is occurs because of different vtable entries in the Paraview-internal versions of the VTK libraries.
The solution is to make sure that your vtkMy library is compiled against Paraview's internal VTK libraries. Even if you compiled VTK and Paraview using the same VTK sources, you *must not* link against the external VTK libraries. (The linker won't complain, because it will find all the symbols it needs, but this leads to unexpected behaviour.)
To be explicit, when compiling your vtkMy library, you must set the cmake variable VTK_DIR to point to the 'VTK' subdirectory in the directory in which you built Paraview. (On my system, cmake automatically finds vtk at /usr/lib/vtk-5.2, and I must change VTK_DIR to ~/source/ParaView3/build/VTK .)
"Is not a valid Qt plugin" in Windows
Make sure that all the DLLs that your plugin depends on are on the PATH. If in doubt, try placing your plugin and all its dependent DLLs in the bin dir of your build and load it from there.
The system cannot find the path specified. error MSB6006: "cmd.exe" exited with code 3.
You may get an error like this when trying to build your plugin with VisualStudio:
1> CS Wrapping - generating vtkMyElevationFilterClientServer.cxx 1> The system cannot find the path specified. 1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(151,5): error MSB6006: "cmd.exe" exited with code 3. 1>Done executing task "CustomBuild" -- FAILED.
This is caused for a mismatch between the configuration you used when building ParaView (e.g. Debug, Release, etc.) and the configuration currently chosen for building your plugin. So ensure those match.
The problem is caused because inside the Linker properties there are references to the *.lib files, including the name of the directory that matches the configuration type, which may look something like this:
C:\Users\MyUser\ParaView-v4.2.0-build\lib\Release\vtkPVAnimation-pv4.2.lib
Legacy/Deprecated Components
Adding an object panel
Deprecated since 3.98. Use Properties Panel customizations instead.
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 components to Display Panel (decorating display panels)
Deprecated since 3.98. Use Properties Panel customizations instead.
Display panel is the panel shown on the Display tab in the Object Inspector. It is possible to add GUI components to existing display panels.
In this example we want to add a GUI element to the display panel shown for the spread sheet view to size of data that is fetched to the client at one time referred to as the Block Size.
For that we write the implementation in QObject subclass (say MySpreadsheetDecorator) with a constructor that takes in the pqDisplayPanel which is to be decorated.
<source lang="cpp"> ... class MySpreadsheetDecorator : public QObject { ... public:
MySpreadsheetDecorator(pqDisplayPanel* panel); virtual ~MySpreadsheetDecorator();
... }; </source>
In the constructor, we have access to the panel, hence we can get the layout from it and add custom widgets to it. In this case, it would be a spin-box or a line edit to enter the block size. pqDisplayPanel::getRepresentation() provides access to the representation being shown on the panel. We can use pqPropertyLinks to link the "BlockSize" property on the representation with the spin-box for the block size so that when the widget is changed by the user, the property changes and vice-versa.
Now the CMakeLists.txt to package this plugin looks like follows:
QT4_WRAP_CPP(MOC_SRCS MySpreadsheetDecorator.h) # This is the macro to add a display panel decorator. # It needs the class name, and the panel types we are decorating. It fills up # IFACES and IFACE_SRCS with proper values as needed by ADD_PARAVIEW_PLUGIN macro. ADD_PARAVIEW_DISPLAY_PANEL_DECORATOR( IFACES IFACE_SRCS CLASS_NAME MySpreadsheetDecorator PANEL_TYPES pqSpreadSheetDisplayEditor # <-- This identifies the panel type(s) to decorate # Our decorator will only be instantiated for the panel types indicated here ) # create a plugin ADD_PARAVIEW_PLUGIN(MySpreadsheetDecorator "1.0" GUI_INTERFACES ${IFACES} SOURCES MySpreadsheetDecorator.cxx ${MOC_SRCS} ${IFACE_SRCS})
An example panel decorator is available under Examples/Plugins/DisplayPanelDecorator in the ParaView source.