Creating Plugins

The standard library has a few nodes (import nodes, export nodes and calculator nodes) that make use of plugins. Plugins can be added to third-party libraries to extend the functionality of these nodes in some intended ways.

Plugins are installed by adding python files inside the Library folder of a library (<some-library>/Library/<some-folder>). The filename for the plugin should match plugin_*.py. These python files needs to contain a class which is a subclass of the plugin class specified by the node. Installed plugins are then automatcally loaded and made ready for use in nodes.

Import node plugins

The specific base classes used for implementing plugins for import nodes inherit the following base:

class sympathy.api.importers.IDataImporter(fq_infilename, parameters)[source]

Interface for a DataImporter. It’s important to set IMPORTER_NAME to a unique name otherwise errors will occur.

classmethod display_name()[source]
Returns:Display name for importer.
Return type:six.text_type
classmethod identifier()[source]
Returns:Unique identifier for importer.
Return type:six.text_type
import_data(out_file, parameters=None, progress=None)[source]

Fill out_file with data.

is_type()[source]
Returns:True if self._fq_infilename points to a native “sydata file.
Return type:bool
classmethod name()[source]
Returns:Name for importer.
Return type:six.text_type
parameter_view(**kwargs)
Returns:GUI widget for importer.
Return type:QtWidgets.QWidget
valid_for_file()[source]
Returns:True if plugin handles self._fq_infilename.
Return type:bool

It serves as a common point for standardization and documentation but should not be subclassed directly. Instead subclass the base class specified by the node.

Example

This example will serve to illustrate the fundamentals of creating import plugins with custom parameters and GUI.

To extend Table with support for a json subformat of table-like structure, for example, the following:

[{"a": 1, "b": 2, "c": 3},
 {"a": 4, "b": 5, "c": 6},
 {"a": 7, "b": 8, "c": 9}]

Create a new file called plugin_json_table_importer.py with the following code:

import os
import json
import numpy as np
from sympathy.api import qt as qt_compat2
from sympathy.api import importers
QtWidgets = qt_compat.import_module('QtWidgets')

class JsonTable(importers.TableDataImporterBase):
    IMPORTER_NAME = 'JSON-TABLE'

    def __init__(self, fq_infilename, parameters):
        super(JsonTable, self).__init__(fq_infilename, parameters)
        if parameters is not None:
            if 'set_name' not in self._parameters:
                parameters.set_boolean('set_name', value=True,
                                       label='Set table name from filename')

    def valid_for_file(self):
        # Inefficient for large files, it would be better
        # to check some part of the content or perhaps even the file
        # extension. Additionally, this accepts all valid json files.
        try:
            with open(self._fq_infilename, 'rb') as f:
                json.load(f)
                return True
        except Exception:
            return False

    def import_data(self, out_table, parameters=None, progress=None):
        cols = {}
        with open(self._fq_infilename, 'rb') as f:
            for row in json.load(f):
                for col_name, cell in row.items():
                    cols.setdefault(col_name, []).append(cell)
        for col_name, col_data in cols.items():
            out_table[col_name] = np.array(col_data)

        if parameters['set_name']:
            out_table.set_name(os.path.basename(self._fq_infilename))

    def parameter_view(self, parameters):
        # For importers without custom parameters, this need not
        # be implemented.
        if not self.valid_for_file():
            return QtGui.QWidget()
        return parameters['set_name'].gui()

Note that we are subclassing TableDataImporterBase which is the base class for plugins specified in Table.

Create a new library and move the file into Library/<Library name>/

To try it out, create a new text file with data following the structure example above, called table.json.

Configure Table to import using JSON-TABLE and select table.json using the datasource node.

Now we have created a custom importer plugin for Table. Plugins for ADAF, Text, etc. should follow the same structure and should be straight-forward to implement so long as you can handle the source format (possibly by using a third-party library) and know the sympathy datatype.

Export node plugins

The specific base classes used for implementing plugins for export nodes inherit the following base:

class sympathy.api.exporters.IDataExporter(parameters)[source]

Interface for a DataExporter. It’s important to set EXPORTER_NAME to a unique name otherwise errors will occur.

cardinality()[source]

Relation between input elements and output elements created.

Returns:Cardinality enum. IDataExporter.one_to_one, IDataExporter.one_to_many IDataExporter.many_to_one.
Return type:int
create_filenames(data, filename, ext=True)[source]
Parameters:
  • data (list of sydata) – Items to export.
  • filename (six.text_type) – Base filename without extension.
  • ext (bool or six.text_type) – Extensions string or True to add default extension, False for no extension.
Returns:

relative filenames with extension.

Return type:

list of six.text_type

classmethod display_name()[source]
Returns:Display name for return.
Return type:six.text_type
static file_based()[source]
Returns:True if exporter is file based (creates files on disk) instead.
Return type:bool
static hide_filename()[source]
Returns:True if filename preview should be hidden from view.
Return type:bool
classmethod identifier()[source]
Returns:Unique identifier for importer.
Return type:six.text_type
parameter_view(*args)[source]
Returns:GUI widget for exporter.
Return type:QtWidgets.QWidget

It serves as a common point for standardization and documentation but should not be subclassed directly. Instead subclass the base class specified by the node.

Calculator node plugins

All calculator nodes use the following base class for implementing plugins (it can subclassed directly):

class sylib.calculator.plugins.ICalcPlugin[source]

Interface for calculator plugins.

static globals_dict()[source]

Return a dictionary that will be added to the globals dictionary when executing calculations.

static gui_dict()[source]

Return a dictionary with functions that will be shown in the configuration gui for the calculator node.

Each dictionary in the globals_dict represents another level of subcategories in the tree view. The keys of the dict is used as labels for the subcategories. A list represents a list of functions.

Each function in the list should be a tuple of three elements: label, code, tooltip/documentation.

For example:

{‘My functions’: [
(‘My function 1’, ‘myplugin.func1()’,
“My function 1 is awesome…”),
(‘My function 2’, ‘myplugin.func2()’,
“My function 1 is also awesome…”)]}

This should result in a top level category called “My functions” containing the two functions “My function 1” and “My function 2”.

static hidden_items()[source]

Reimplement this to hide some elements from other plugins.

The hidden functions will still be available, but won’t show up in the list of common functions in the calculator gui.

The returned value should be a list of tuples with the “paths” in the gui_dict that should be hidden. E.g. [("Event detection",)] will hide the entire event detection subtree, while [("Event detection", "Changed")] will hide the function called “Changed” under “Event detection”.

static imports()[source]

Using this method is deprecated and will no longer have an effect from Sympathy 1.7.0.