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 plugins folder of a library. See Libraries for information about where to put plugins in your library. 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 auto_priority() int[source]

Return integer number, 0-99 representing how early this plugin should be attempted when used in auto configuration. Higher values before lower values.

classmethod cardinalities()[source]

Return list of options for cardinality. In order of preference, first choice is the preferred.

cardinality()[source]

Relation between input elements and output elements created. The result is the first out of any items from cardinalities() that is also part of the cardinalites set as supported using set_supported_cardinalities().

Returns:

Cardinality enum. IDataImporter.one_to_one, IDataImporter.one_to_many IDataImporter.many_to_one.

Return type:

int

classmethod display_name()[source]
Returns:

Display name for importer.

Return type:

str

classmethod identifier()[source]
Returns:

Unique identifier for importer.

Return type:

str

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:

str

node()[source]

Return the current node or None if set_node was not called.

parameter_view(**kwargs)
Returns:

GUI widget for importer.

Return type:

QtWidgets.QWidget

set_supported_cardinalities(options)[source]

Set relation between input elements and output elements created that is supported for the importer.

Parameters:

options (list of Cardinality enum) – List of supported cardinalities for importer.

classmethod supports_datasource() bool[source]
Returns:

True if importer supports datasource input as self._fq_infilename.

Return type:

bool

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 qt2
from sympathy.api import importers
QtWidgets = qt2.import_module('QtWidgets')

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

    def __init__(self, fq_infilename, parameters):
        super().__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 QtWidgets.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 the plugins folder.

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 (str) – Base filename without extension.

  • ext (bool or str) – Extensions string or True to add default extension, False for no extension.

Returns:

relative filenames with extension.

Return type:

list of str

classmethod display_name()[source]
Returns:

Display name for return.

Return type:

str

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:

str

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”.