Important API changes

Since Tulip 4.9

Special syntax for automatic creation of graph properties through type inference

For convenient use, in particular when importing graph data, it is now possible to create graph properties on the fly without prior calls to the methods dedicated to these tasks (e.g. tlp.Graph.getBooleanProperty(), tlp.Graph.getIntegerProperty(), tlp.Graph.getDoubleProperty(), ...). It allows to save a consequent lines of code but also to work faster using the tulip Python API.

For instance, consider the following task : importing nodes data from a JSON file. As an example, we will use the following file containing data about cars with different types : string, float and integer, and will create one node for each car and one graph property (correctly typed) for each data field.

[
  {
    "acceleration": 12.0,
    "car": "Chevrolet Chevelle Malibu",
    "cylinders": 8,
    "displacement": 307.0,
    "horsepower": 130.0,
    "id": 0,
    "model": 70,
    "mpg": 18.0,
    "origin": "US",
    "weight": 3504.0
  },
  {
    "acceleration": 11.5,
    "car": "Buick Skylark 320",
    "cylinders": 8,
    "displacement": 350.0,
    "horsepower": 165.0,
    "id": 1,
    "model": 70,
    "mpg": 15.0,
    "origin": "US",
    "weight": 3693.0
  },
  ...
]

To correctly import those data into Tulip graph nodes, the sample script below is sufficient:

cars = json.loads(open('cars.json').read())

for car in cars:
  n = graph.addNode()
  for k, v in car.items():
    graph[k][n] = v

Before Tulip 4.9, it was necessary to create the graph properties first by calling the following methods:

graph.getDoubleProperty('acceleration')
graph.getStringProperty('car')
graph.getIntegerProperty('cylinders')
graph.getDoubleProperty('displacement')
graph.getDoubleProperty('horsepower')
graph.getIntegerProperty('id')
graph.getIntegerProperty('model')
graph.getDoubleProperty('mpg')
graph.getStringProperty('origin')
graph.getDoubleProperty('weight')

Improvements regarding the declaration and transmission of file / directory parameters for plugins

When implementing Tulip plugins in Python (see Writing Tulip plugins in Python), it can be usefull to declare a file / directory parameter to perform a variety of tasks during the plugin execution: reading / writing graph data to a file, logging messages to a file, ...

Prior to the 4.9 release, it was necessary to declare a file parameter in the plugin constructor the way below:

self.addStringParameter('file::filename', 'the path to an existing file')

The “file::” prefix acts as a hint for the Tulip GUI to create a dialog in order to easily pick a file from the filesystem.

To retrieve the path of the file selected by the user, the following instruction had to be used in the plugin main method (tlp.ImportModule.importGraph(), tlp.ExportModule.exportGraph(), tlp.Algorithm.run()):

filename = self.dataSet['file::filename']

That way to proceed is not really intuitive so Tulip 4.9 introduces a more user friendly mechanism to work with file / directory parameters : two new methods have been added in order to easily declare file / directory parameters (tlp.WithParameter.addFileParameter(), tlp.WithParameter.addDirectoryParameter()) and it is no more needed to explicitely write the “file::” prefix.

So the recommended way to declare a file parameter in the plugin constructor is now the one below:

self.addFileParameter('filename', True, 'the path to an existing file')

And to get the path of the file selected by the user, you can now simply write in the plugin main method:

filename = self.dataSet['filename']

In the same manner, when transmitting a file parameter to a plugin trough a dictionnary (see Applying an algorithm on a graph), the “file::” prefix is no more required to be written.

Nevertheless for backward compatibility, the old mechanism can still be used.

Since Tulip 4.8.1

New methods for getting / setting graph properties values for nodes and edges added

Convenient methods that rely on the use of a dictionnary for setting and getting properties values for nodes and edges have been added to the tlp.Graph class :

For instance, the sample code below sets multiple graph view properties values for each node of a graph:

def getRandomFontAwesomeIcon():
  iconKeys = vars(tlp.TulipFontAwesome).keys()
  while 1:
    attName = random.choice(list(iconKeys))
    attr = getattr(tlp.TulipFontAwesome, attName)
    if not attName.startswith('_') and type(attr) == str:
      return attr

def getRandomColor():
  r = int(random.random()*255)
  g = int(random.random()*255)
  b = int(random.random()*255)
  return tlp.Color(r, g, b)

def getRandomSize(minSize, maxSize):
  return minSize + random.random() * (maxSize - minSize)

for n in graph.getNodes():
    values = {'viewShape': tlp.NodeShape.FontAwesomeIcon,
              'viewColor' : getRandomColor(),
              'viewSize' : getRandomSize(tlp.Size(0.1), tlp.Size(1)),
              'viewFontAwesomeIcon' : getRandomFontAwesomeIcon()}
    graph.setNodePropertiesValues(n, values)

Since Tulip 4.8

Deprecation of the direct use of the tlp.DataSet class

Formerly, the class tlp.DataSet was used to transmit parameters to the algorithms that can be executed on an instance of a tlp.Graph class (see Applying an algorithm on a graph).

For commodity of use in the Python world, that class is now internally mapped to a dictionnary indexed by string keys (parameters names). To get a dictionnary filled with default parameters for an algorithm, you can use the tlp.getDefaultPluginParameters() function.

Nevertheless for backward compatibilty, it is still possible to create instance of that class.

Deprecation of the direct use of the tlp.StringCollection class

The tlp.StringCollection class represents a list of selectable string entries that can be used as plugin parameter. Formerly, to select the string to transmit to a plugin, the following code has to be used:

# get defaut parameters for the 'FM^3 (OGDF)' layout plugin
params = tlp.getDefaultPluginParameters('FM^3 (OGDF)')
# set 'Page Format' as 'Landscape'
params['Page Format'].setCurrent('Landscape')

For syntactic sugar, the tlp.StringCollection class does not need to be instantiated anymore to transmit the string to the algorithm. The creation of the string collection is handled internally and you can now simply write:

# get defaut parameters for the 'FM^3 (OGDF)' layout plugin
params = tlp.getDefaultPluginParameters('FM^3 (OGDF)')
# set 'Page Format' as 'Landscape'
params['Page Format'] = 'Landscape'

If the provided string is not contained in the string collection associated to a plugin parameter, an exception will be thrown when trying to execute the plugin trough dedicated methods/functions.

Nevertheless for backward compatibilty, it is still possible to create instance of that class.