Module parameters - arklumpus/TreeViewer GitHub Wiki

Transformer modules, Further transformation modules, Coordinates module and Plot action modules can define a list of module parameters that can be changed by the user to affect how the module behaves. For example these can be the colour of the labels on the tree, or the thickness of the branches. These parameters are defined in the GetParameters method, and can be changed by the user in the panel at the left of the main window.

The value of the parameters can be retrieved from within the other methods of the module that have a Dictionary<string, object> parameterValues argument, which stores the current value for each parameter. The key to be used corresponds to the name of the parameter (i.e. the text of the control label), but the parameter value (which is returned by the dictionary as an object) needs to be cast back to the correct type.

public static List<(string, string)> GetParameters(TreeNode tree)
{
    return new List<(string, string)>()
    {
        ("Thickness:", "NumericUpDown:1[\"0\",\"Infinity\"]")
    };
}

// ...

public static Point[] PlotAction(TreeNode tree, Dictionary<string, object> parameterValues, Dictionary<string, Point> coordinates, Graphics graphics)
{
    // Retrieve the value from the dictionary (as an object) and cast it to the correct type.
    double thickness = (double)parameterValues["Thickness:"];
}

Note that the name of the parameter must be unique (i.e. the module cannot define two parameters called "Thickness:"); however, in some situations it may be necessary or useful to define two different parameters with the same name (e.g. because the parameters refer to the thickness of two different things). In this case, the "trick" is to add one or more whitespace characters after the name of the character (e.g. "Thickness:" and "Thickness: "), which will not have any effect on the graphical presentation of the control label, but will make it possible to distinguish between the two parameters in the dictionary.

The following list describes all the parameter types that can be returned by the GetParameters method, as well as the correct type to which the value should be cast.

Button

Syntax

"Button:"

Value type

bool

A single button is specified by the Button keyword; it has no default value and no additional settings are available. Note that the colon (:) after the Button keyword is still required.

When the button is clicked, the OnParameterChange method of the module is invoked, with the value corresponding to the button in the currentParameterValues dictionary set to true. The OnParameterChange method can react to this and should always make sure to set this parameter back to false in the parametersToChange dictionary.

Example

("Test button", "Button:")


Buttons

Syntax

"Buttons:[<button 0>,<button 1>,...]"

Value type

int

A set of multiple buttons is specified by the Buttons keyword; they have no default value and the additional settings is a JSON string array containing the text for the buttons.

When one of the buttons is clicked, the OnParameterChange method of the module is invoked, with the value corresponding to the button set in the currentParameterValues dictionary set to the index of the button that was clicked. The OnParameterChange method can react to this and should always make sure to set this parameter back to -1 in the parametersToChange dictionary.

Example

("This is not displayed", "Buttons:[\"0\",\"1\",\"2\",\"3\"]")


Checkbox

Syntax

"CheckBox:<default>"

Value type

bool

Checkboxes are specified by the CheckBox keyword; the default value can be either true or false. No additional settings are available.

Example

("Test checkbox", "CheckBox:true")


String formatter

Syntax

"Formatter:[<attribute type reference>,<default source code>,<is default formatter>]"
"Formatter:[<attribute type reference>,<digits type>,<digits count>,<maximum>,<minimum>,<maximum enabled>,<minimum enabled>,<default source code>,<is default formatter>]"

Value type

TreeViewer.FormatterOptions

String formatters are specified by the Formatter keyword; they have no default value. The additional settings is a JSON array whose contents depend on whether the attribute to be formatter is a String or a Number.

The first additional setting (<attribute type reference>) should always be the name of another parameter of type AttributeType that contains the type of the attribute that needs to be formatted.

If this is String, only two other settings are expected:

  • <default source code> is the source code of the attribute formatter. The default source code for a string formatter can be obtained using Modules.DefaultAttributeConverters[0]. Note that to include this in the JSON array, you may have to serialize it using System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConverters[0]).
  • <is default formatter> should be set to "true" if <default source code> is equal to Modules.DefaultAttributeConverters[0], or "false" otherwise.

If the attribute type is Number, the following settings are expected:

  • <digits type> can be either "0" for significant digits or "1" for decimal places.
  • <digits count> corresponds to the number of significant or decimal digits to keep when formatting the number. Note that this must be specified as a string (e.g. "2", not 2).
  • <maximum>: if the maximum is enabled and the attribute value is greater than this value, the formatter will return null. Note that this must be specified as a string (e.g. "1.5", not 1.5).
  • <minimum>: if the minimum is enabled and the attribute value is smaller than this value, the formatter will return null. Note that this must be specified as a string (e.g. "0.01", not 0.01).
  • <maximum enabled>: if this is "true", the maximum is enabled. If it is "false", the maximum is disabled.
  • <minimum enabled>: if this is "true", the minimum is enabled. If it is "false", the minimum is disabled.
  • <default source code> is the source code of the attribute formatter. The default source code for a number-to-string formatter can be obtained using Modules.DefaultAttributeConverters[1]. Note that to include this in the JSON array, you may have to serialize it using System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConverters[1]).
  • <is default formatter> should be set to "true" if <default source code> is equal to Modules.DefaultAttributeConverters[1], or "false" otherwise.

This parameter returns a FormatterOptions object, whose Formatter property is a method that accepts a single object argument and returns a string. This method can be invoked within the module to convert the value of an attribute to a string. For example:

public static Point[] PlotAction(TreeNode tree, Dictionary<string, object> parameterValues, Dictionary<string, Point> coordinates, Graphics graphics)
{
    // Retrieve the formatter function.
    Func<object, string> formatter = ((FormatterOptions)parameterValues["Attribute format..."]).Formatter;

    // ...

    foreach (TreeNode node in tree.GetChildrenRecursiveLazy())
    {
        // Initialize the variable that will contain the value of the formatted attribute.
        string formattedAttribute = null;

        // Initialize the variable that will contain the value of the attribute as it is stored on the tree.
        object attributeValue;

        // Try to retrieve the attribute value from the node.
        if (node.Attributes.TryGetValue("MyAttribute", out attributeValue))
        {
            // The node had the attribute; now we can format it using the formatter.
            formattedAttribute = formatter(attributeValue);
        }

        if (!string.IsNullOrEmpty(formattedAttribute))
        {
            // The attribute was present in the node and has been correctly formatted.

            // Do something with the formattedAttribute.
        }
        else
        {
            // The attribute was not present in the node or it was of the wrong type.
        }
    }

    // ...
}

Example

("Test attribute type 1:", "AttributeType:String"),
("Test attribute format 1", "Formatter:[\"Test attribute type 1:\"," + System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConverters[0]) + ",\"true\"]")


("Test attribute type 2:", "AttributeType:Number"),
("Test attribute format 2", "Formatter:[\"Test attribute type 2:\",\"0\",\"2\",\"0\",\"0\",\"false\",\"false\"," + System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConverters[1]) + ",\"true\"]")


Label

Syntax

"Label:"
"Label:[<alignment>]"
"Label:[<alignment>,<font style>]"
"Label:[<alignment>,<font style>,<colour>]"

Labels are specified by the Label keyword. They are not actually parameters, in the sense that they are not added to the parameterValues dictionary; they are just visual elements. The additional settings control the alignment, style and colour of the label.

  • <alignment> can be one of "Left" (the default), "Right" or "Center".

  • <font style> can be one of "Normal" (the default), "Bold", "Italic" or "BoldItalic".

  • <colour> can be:

    • A "#RRGGBB" hex colour string.
    • An "#AARRGGBB" hex colour string.
    • The name of a "known" colour (e.g. "red").

    The default is to have the label in black.

Example

See below the examples for the Group type.


Group

Syntax

"Group:<count>"

Groups are specified by the Group keyword. They are not actually parameters, in the sense that they are not added to the parameterValues dictionary; they are just visual elements. The value after the colon corresponds to the number of controls that belong in the group.

Example

("Test group", "Group:3"),
("Test label 1", "Label:"),
("Test label 2", "Label:[\"Center\",\"Bold\"]"),
("Test label 3", "Label:[\"Right\",\"Italic\",\"#0072B2\"]"),
("Long label which will be outside of the group.", "Label:[\"Left\",\"BoldItalic\"]")


Drop-down list (combo box)

Syntax

"ComboBox:<default index>[<element1>,<element2>,...]"

Value type

int

Drop-down lists (sometimes known as combo boxes) are specified by the ComboBox keyword; the default index should be the index of the item that is initially selected in the box. The additional parameters are a list of all the items in the box.

Example

("Test drop-down:", "ComboBox:1[\"Item 0\",\"Item 1\",\"Item 2\",\"Item 3\"]")


Text box

Syntax

"TextBox:<default>"

Value type

string

Text boxes are specified by the TextBox keyword; the default value is the initial text of the text box. No additional settings are available.

Example

("Test text box:", "TextBox:Initial text")


Attribute selector

Syntax

"AttributeSelector:<default attribute>"

Value type

string

Attribute selectors are specified by the AttributeSelector keyword; the default attribute should be the name of the attribute that is selected by default (e.g. Name). No additional settings are available; the list of attributes is populated automatically. You should make sure that the default attribute is present in the tree, either by using one of the attributes that are always defined (Name, Length and Support) or by manually checking whether the default attribute you want to use is present in the tree.

The value of the parameter will be the name of the attribute that is selected.

Example

("Test attribute selector:", "AttributeSelector:Length")


Attribute type selector

Syntax

"AttributeType:<default attribute type>"

Value type

string

Attribute type selectors are specified by the AttributeType keyword; the default type should be either String or Number. No additional settings are available; the list of attribute types is populated automatically.

The value of the parameter will be either "String" or "Number", depending on the attribute type selected by the user.

Example

("Test attribute type:", "AttributeType:String")


Attachment selector

Syntax

"Attachment:"

Value type

TreeViewer.Attachment

Attachment selectors are specified by the Attachment keyword. No default value and additional settings are available; the list of attachments is populated automatically.

The value of the parameter will either be null (if the user has not selected an attachment, or if there are no attachments in the tree) or an Attachment. Interesting methods of the Attachment class are GetBytes and GetLines, which return the contents of the file as a byte array and as an array of strings, respectively. Attachments can be used to store additional data together with the tree.

Example

("Test attachment:", "Attachment:")


Node selector

Syntax

"Node:[<taxon 1>]"
"Node:[<taxon 1>,<taxon 2>,...]"

Value type

string[]

Node selectors are specified by the Node keyword. The additional settings are a JSON string array containing the names of a set of taxa whose last common ancestor is the selected node. A good default value would be to select the root node of the tree, by picking the first and last leaf name.

The value of the parameter will be a string[] array containing the list of taxa whose last common ancestor has been selected by the user.

The actual node from the tree can be obtained using code similar to the following:

public static List<(string, string)> GetParameters(TreeNode tree)
{
    // Get all leaf names.
    List<string> leafNames = tree.GetLeafNames();

    return new List<(string, string)>()
    {
        // Due to how the leaves are ordered, the LCA of the first and last leaf of a node should always
        // be the node itself (pathological cases aside). Here we are choosing the first and last leaf
        // of the whole tree, thus the LCA should be the root node.
        ( "Node:", "Node:[\"" + leafNames[0] +"\",\"" + leafNames[^1] + "\"]" )
    };
}

// ...

public static void Transform(ref TreeNode tree, Dictionary<string, object> parameterValues)
{
    // Get the list of taxa selected by the user.
    string[] nodeElements = (string[])parameterValues["Node:"];

    // Get the node as the LCA of the selected taxa.
    TreeNode node = tree.GetLastCommonAncestor(nodeElements);

    // Always check whether the node actually exists - modules that change the tree topology can make
    // previously valid selections invalid.
    if (node == null)
    {
        // When an exception is thrown within a Transformer, Further transformation, Coordinates or
        // Plot action module, a warning sign is shown in the interface next to the offending module
        // and the exception message is shown when the user hovers over it.
        throw new Exception("Could not find the requested node! If you have changed the Name of some nodes, please select the node again!");
    }
}

Example

List<string> leafNames = tree.GetLeafNames();

// ...

("Test node:", "Node:[\"" + leafNames[0] +"\",\"" + leafNames[^1] + "\"]")


Number spin box

Syntax

"NumericUpDown:<default>[<minimum>,<maximum>]"
"NumericUpDown:<default>[<minimum>,<maximum>,<increment>]"
"NumericUpDown:<default>[<minimum>,<maximum>,<increment>,<format>]"

Value type

double

Numeric spin boxes are specified by the NumericUpDown keyword; the default value is the initial value of the box. Additional settings are:

  • <minimum>: the minimum value (required).
  • <maximum>: the maximum value (required).
  • <increment>: the amount by which the selected value is increased or decreased when the user clicks on the spin up/down buttons.
  • <format>: a string used to format the value of the box (see Standard numeric format strings and Custom numeric format strings).

Note that all these values (including the minimum and maximum) must be supplied as strings in the JSON array. If the value entered in the box should have no minimum or maximum bound, a maximum or minimum of "-Infinity" or "Infinity" are accepted.

If the <increment> is omitted, the default value is computed as 1% of the <maximum> - <minimum> difference. If this is not a finite number, an increment of 1 is used.

The <format> string can be used to alter the appearance of the value in the spin box, e.g. changing the number of decimal digits, showing it as a percentage, or including a measurement unit. If the format string is not provided, it is determined automatically based on the increment, using the following criteria:

  • If the increment is 0, it is not finite, or its absolute value is greater than 1, the format string is "0" (i.e. show the value rounded to the next integer).
  • Otherwise, the order of magnitude of the value is determined as and the format string is "0." followed by additional zeroes.

For example, an increment of 100.123 would result in a default format string of "0", while an increment of 0.0000123 would result in a default format string of "0.00000".

Examples

("Test numeric box 1:", "NumericUpDown:1[\"0\",\"Infinity\"]")


("Test numeric box 2:", "NumericUpDown:0.5[\"0\",\"1\",\"0.01\",\"{0:P0}\"]")


("Test numeric box 3:", "NumericUpDown:90[\"0\",\"360\",\"1\",\"{0}°\"]")


Number spin box (by node)

Syntax

"NumericUpDownByNode:<default>[<minimum>,<maximum>,<default source code>,<attribute name>,<attribute type>,<is default formatter>]"

Value type

TreeViewer.NumberFormatterOptions

The NumericUpDownByNode keyword identifies a numeric spin box that provides a default value that can be overridden by a specific attribute on the nodes. The user can change the settings of the formatter by clicking on the wrench button next to the box.

The default value is the initial value of the box. Additional settings are:

  • <minimum>: the minimum value.
  • <maximum>: the maximum value.
  • <default source code>: the source code of the attribute formatter. The default source code for a string-to-number formatter can be obtained using Modules.DefaultAttributeConvertersToDouble[0] and the code for a number-to-number formatter can be obtained using Modules.DefaultAttributeConvertersToDouble[1]. Note that to include this in the JSON array, you may have to serialize it using System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConvertersToDouble[0]).
  • <attribute name>: the name of the attribute which will be used to override the default value.
  • <attribute type>: the type of the attribute (either "String" or "Number").
  • <is default formatter> should be set to "true" if <attribute type> is equal to "String" and <default source code> is equal to Modules.DefaultAttributeConvertersToDouble[0], or if <attribute type> is equal to "Number" and <default source code> is equal to Modules.DefaultAttributeConvertersToDouble[1]; it should be set to "false" otherwise.

Note that all these values (including the minimum and maximum) must be supplied as strings in the JSON array. If the value entered in the box should have no minimum or maximum bound, a maximum or minimum of "-Infinity" or "Infinity" are accepted.

In principle, this control works similarly to the Formatter control, with the exception that the attribute name and type are stored within this same control. The value of the parameter is a NumberFormatterOptions object, whose Formatter property is a method that accepts a single object argument and returns a double?. This method can be invoked within the module to convert the value of an attribute to a double?. The DefaultValue property of the NumberFormatterOptions corresponds to the default value (selected by the user). Here is an example of how to use this object:

public static List<(string, string)> GetParameters(TreeNode tree)
{
    return new List<(string, string)>()
    {
        // Define the parameter, with default value 1, attribute name "Thickness", and attribute type "Number".
        ( "Line weight:", "NumericUpDownByNode:1[\"0\",\"Infinity\"," + System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConvertersToDouble[1]) + ",\"Thickness\",\"Number\",\"true\"]" ),
    };
}

// ...

public static Point[] PlotAction(TreeNode tree, Dictionary<string, object> parameterValues, Dictionary<string, Point> coordinates, Graphics graphics)
{
    // Retrieve the value of the parameter as a NumberFormatterOptions object.
    NumberFormatterOptions WeightFO = (NumberFormatterOptions)parameterValues["Line weight:"];

    // Get the default value.
    double defaultWeight = WeightFO.DefaultValue;

    // Get the function that converts an attribute into a double?.
    Func<object, double?> weightFormatter = WeightFO.Formatter;

    // Enumerate through all the nodes in the tree.
    foreach (TreeNode node in tree.GetChildrenRecursiveLazy())
    {
        // Initialise with the default value.
        double weight = defaultWeight;

        // Declare variable to store the value of the attribute.
        object weightAttributeObject;

        // Check whether the attribute is actually present on the node and retrieve it.
        if (node.Attributes.TryGetValue(WeightFO.AttributeName, out weightAttributeObject))
        {
            // Check that the attribute is actually defined.
            if (weightAttributeObject != null)
            {
                // Format the attribute using the formatter. The formatter returns double?, i.e. either a
                // double or a null. The null-coalescing operator ?? is used here to return either the
                // formatted value (if it is not null), or the default value (if the formatted value is null).
                weight = weightFormatter(weightAttributeObject) ?? defaultWeight;
            }
        }

        // ...
    }
}

Examples

( "Number by node:", "NumericUpDownByNode:1[\"0\",\"Infinity\"," + System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConvertersToDouble[1]) + ",\"Thickness\",\"Number\",\"true\"]" )


Slider

Syntax

"Slider:<default>[<minimum>,<maximum>]"
"Slider:<default>[<minimum>,<maximum>,<format>]"

Value type

double

Sliders are specified by the Slider keyword; the default value is the initial value of the slider. Additional settings are:

Note that all these values (including the minimum and maximum) must be supplied as strings in the JSON array.

The slider is shown together with a numeric spin box that shows the current value of the slider and can be used to change it with greater precision.

The increment is computed as 1% of the <maximum> - <minimum> difference. If this is not a finite number, an increment of 1 is used. The <format> string can be used to alter the appearance of the value in the spin box, e.g. changing the number of decimal digits, showing it as a percentage, or including a measurement unit. If the format string is not provided, it is determined automatically based on the increment, using the following criteria:

  • If the increment is 0, it is not finite, or its absolute value is greater than 1, the format string is "0" (i.e. show the value rounded to the next integer).
  • Otherwise, the order of magnitude of the increment is determined as and the format string is "0." followed by additional zeroes.

For example, an increment of 100.123 would result in a default format string of "0", while an increment of 0.0000123 would result in a default format string of "0.00000".

Examples

("Test slider 1:", "Slider:1[\"0\",\"100\"]")


("Test slider 2:", "Slider:0.5[\"0\",\"1\",\"{0:P0}\"]")


("Test slider 3:", "Slider:90[\"0\",\"360\",\"{0}°\"]")


Font

Syntax

"Font:[<font family>,<font size>]"

Value type

VectSharp.Font

Font selection buttons are specified by the Font keyword; the default value is provided as a JSON string array containing the name of the font family and the size in points of the font.

The font family should be one of the 12 standard PDF font families:

  • Helvetica
  • Helvetica-Bold
  • Helvetica-Oblique
  • Helvetica-BoldOblique
  • Times-Roman
  • Times-Bold
  • Times-Italic
  • Times-BoldItalic
  • Courier
  • Courier-Bold
  • Courier-Oblique
  • Courier-BoldOblique
  • Symbol
  • ZapfDingbats

Note that the default font size should also be specified as a string.

Example

("Test font:", "Font:[\"Times-Bold\",\"18\"]")


Point

Syntax

"Point:[<X>,<Y>]"

Value type

VectSharp.Point

Points are specified by the Point keyword; the default value is provided as a JSON number array containing the X and Y coordinates of the point.

Example

("Test point:", "Point:[10.5,15.6]")


Colour

Syntax

"Colour:[<R>,<G>,<B>,<A>]"

Value type

VectSharp.Colour

Colour picker buttons are specified by the Colour keyword; the default value is provided as a JSON array of integer numbers containing the red (<R>), green (<G>), blue (<B>) and alpha (<A>) components of the colour. Each component can range from 0 to 255 (inclusive). The alpha component must always be provided, even when the colour is completely opaque (in which case it should be equal to 255).

Example

("Test colour:", "Colour:[0,162,232,255]")


Colour (by node)

Syntax

"ColourByNode:[<default source code>,<attribute name>,\"String\",<R>,<G>,<B>,<A>,<is default formatter>]"
"ColourByNode:[<default source code>,<attribute name>,\"Number\",<R>,<G>,<B>,<A>,<minimum>,<maximum>,<gradient name>,<is default formatter>]"

Value type

VectSharp.Colour

The ColourByNode keyword specifies a colour control that provides a default value that can be overridden by a specific attribute on the nodes. The user can change the settings of the formatter by clicking on the wrench button next to the box.

The additional settings are:

  • <default source code>: the source code of the attribute formatter. The default source code for a string-to-colour formatter can be obtained using Modules.DefaultAttributeConvertersToColour[0] and the code for a number-to-colour formatter can be obtained using Modules.DefaultAttributeConvertersToColour[1]. Note that to include this in the JSON array, you may have to serialize it using System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConvertersToColour[0]).
  • <attribute name>: the name of the attribute which will be used to override the default value.
  • "String" or "Number" represent the attribute type.
  • <R>, <G>, <B>, <A> represent the red, green, blue and alpha components of the default colour.
  • If the attribute type is "Number", <minimum> and <maximum> represent the minimum and maximum of the number range that is mapped on the gradient (the minimum is mapped to the start of the gradient, the maximum to the end, and everything in between is linearly interpolated). Note that you can switch the minimum and maximum to reverse the gradient.
  • If the attribute type is "Number", <gradient name> should be the name of one of the default gradients that will be used by the default formatter. The available values are:
    • "TransparentToBlack": a gradient that goes from a transparent black to a fully opaque black.
    • "WhiteToBlack": a gradient from fully opaque white to fully opaque black.
    • "RedToGreen": a gradient from fully opaque red to fully opaque green.
    • "Rainbow": a rainbow gradient.
    • "Viridis": the Viridis colour scale.
  • <is default formatter> should be set to "true" if the attribute type is "String" and <default source code> is equal to Modules.DefaultAttributeConvertersToColour[0], or if the attribute type is "Number" and <default source code> is equal to Modules.DefaultAttributeConvertersToColour[1]; it should be set to "false" otherwise.

Note that all these values (including the minimum and maximum) must be supplied as strings in the JSON array.

This control works similarly to the NumericUpDownByNode control. The value of the parameter is a ColourFormatterOptions object, whose Formatter property is a method that accepts a single object argument and returns a Colour?. This method can be invoked within the module to convert the value of an attribute to a Colour?. The DefaultValue property of the ColourFormatterOptions corresponds to the default value (selected by the user). Here is an example of how to use this object:

public static List<(string, string)> GetParameters(TreeNode tree)
{
    return new List<(string, string)>()
    {
        // Define the parameter, with default colour black, attribute name "Color", and attribute type "String".
         ( "Text colour:", "ColourByNode:[" + System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConvertersToColour[0]) + ",\"Color\",\"String\",\"0\",\"0\",\"0\",\"255\",\"true\"]" )
    };
}

// ...

public static Point[] PlotAction(TreeNode tree, Dictionary<string, object> parameterValues, Dictionary<string, Point> coordinates, Graphics graphics)
{
    // Retrieve the value of the parameter as a NumberFormatterOptions object.
    ColourFormatterOptions Fill = (ColourFormatterOptions)parameterValues["Text colour:"];

    // Get the default value.
    Colour defaultFill = Fill.DefaultColour;

    // Get the function that converts an attribute into a Colour?.
    Func<object, Colour?> fillFormatter = Fill.Formatter;

    // Enumerate through all the nodes in the tree.
    foreach (TreeNode node in tree.GetChildrenRecursiveLazy())
    {
        // Initialise with the default value.
        Colour fillColour = defaultFill;

        // Declare variable to store the value of the attribute.
        object fillAttributeObject;

        // Check whether the attribute is actually present on the node and retrieve it.
        if (node.Attributes.TryGetValue(Fill.AttributeName, out fillAttributeObject))
        {
            // Check that the attribute is actually defined.
            if (fillAttributeObject!= null)
            {
                // Format the attribute using the formatter. The formatter returns Colour?, i.e. either a
                // Colour or a null. The null-coalescing operator ?? is used here to return either the
                // formatted value (if it is not null), or the default value (if the formatted value is null).
                fillColour = fillFormatter(fillAttributeObject) ?? defaultFill;
            }
        }

        // ...
    }
}

Example

("Test colour by node:", "ColourByNode:[" + System.Text.Json.JsonSerializer.Serialize(Modules.DefaultAttributeConvertersToColour[0]) + ",\"Color\",\"String\",\"0\",\"0\",\"0\",\"255\",\"true\"]" )


Source code

Syntax

"SourceCode:<default source code>"

Value type

TreeViewer.CompiledCode

Source code is specified by the SourceCode keyword; the default value is the default source code to use. The control is shown as a button on which the user can click to open a source editor window.

The value of the parameter is a CompiledCode object, whose CompiledAssembly property can be used to obtain an Assembly object corresponding to the code that has been compiled. Standard C# reflection methods can then be used to instantiate types and invoke methods declared in the assembly.

For example, this is how the Custom script Further transformation module uses a source code control:

public static List<(string, string)> GetParameters(TreeNode tree)
{
    // Create the default source code.
    StringBuilder defaultSourceCode = new StringBuilder();

    defaultSourceCode.AppendLine("using PhyloTree;");
    defaultSourceCode.AppendLine("using System.Collections.Generic;");
    defaultSourceCode.AppendLine("using TreeViewer;");
    defaultSourceCode.AppendLine();
    defaultSourceCode.AppendLine("namespace a" + Guid.NewGuid().ToString("N"));
    defaultSourceCode.AppendLine("{");
    defaultSourceCode.AppendLine("\t//Do not change class name");
    defaultSourceCode.AppendLine("\tpublic static class CustomCode");
    defaultSourceCode.AppendLine("\t{");
    defaultSourceCode.AppendLine("\t\t//Do not change method signature");
    defaultSourceCode.AppendLine("\t\tpublic static void PerformAction(ref TreeNode tree, TreeCollection trees, InstanceStateData stateData)");
    defaultSourceCode.AppendLine("\t\t{");
    defaultSourceCode.AppendLine("\t\t\t//TODO: do something with the tree");
    defaultSourceCode.AppendLine("\t\t}");
    defaultSourceCode.AppendLine("\t}");
    defaultSourceCode.AppendLine("}");

    return new List<(string, string)>()
    {
        // Declare the source code parameter. Note that the default source code does not need to be
        // serialized.
        ( "Source code:", "SourceCode:" + defaultSourceCode.ToString() ),
    };
}

// ...

public static void Transform(ref TreeNode tree, Dictionary<string, object> parameterValues)
{
    // Get the compiled code from the parameter values.
    CompiledCode compiledCode = (CompiledCode)parameterValues["Source code:"];

    // Get the compiled assembly.
    Assembly assembly = compiledCode.CompiledAssembly;

    // Initialise the arguments for method invocation.
    object[] args = new object[] { /* ... */ };

    // Get the Type corresponding to class "CustomCode" from the assembly. We use the utility function
    // ModuleMetadata.GetTypeFromAssembly, which takes care of going through all the namespaces
    // in the assembly until it founds the requested type.
    Type customCode = ModuleMetadata.GetTypeFromAssembly(assembly, "CustomCode");

    // Invoke the static method "PerformAction" of the class "CustomCode".
    customCode.InvokeMember("PerformAction", BindingFlags.Default | BindingFlags.InvokeMethod, null, null, args);

    // ...
}

Example

("Test source code:", "SourceCode:" + CompiledCode.EmptyCode)


Markdown

Syntax

"Markdown:<default Markdown source>"

Value type

string

Markdown code is specified by the Markdown keyword; the default value is the code to use. The control is shown as a button on which the user can click to open a Markdown source editor window.

The Markdown source code that has been entered by the user is returned as a string. It can then be rendered using a VectSharp.Markdown.MarkdownRenderer object; this should be obtained by invoking the MarkdownUtils.GetMarkdownRenderer(InstanceStateData stateData) method, as this will ensure that the renderer is properly initialised. Furthermore, a MarkdownRenderer obtained in this way will also be able to parse "special" image addresses (e.g. circle:// or attachment://); see the readme for the Legend module for more information about these special images.

This is how the Legend Plot action module renders the Markdown code entered by the user.

public static List<(string, string)> GetParameters(TreeNode tree)
{
    return new List<(string, string)>()
    {
        // Request an InstanceStateData object (this will be necessary to obtain the MarkdownRenderer).
        ( "StateData", "InstanceStateData:" ),

        // Declare the Markdown code parameter. Note that the default source code does not need to be
        // serialized.
        ( "Markdown source:", "Markdown:### **Legend**\n\n* ![](circle://8,#CC79A7) Red circle\n* ![](square://8,#0072B2) Blue square\n* ![](star://8,#009E73) Green star" ),

        // ...
    };
}

// ...

public static Point[] PlotAction(TreeNode tree, Dictionary<string, object> parameterValues, Dictionary<string, Point> coordinates, Graphics graphics)
{
    // Get the InstanceStateData object.
    InstanceStateData stateData = (InstanceStateData)parameterValues["StateData"];

    // Get the Markdown source that the user has entered.
    string text = (string)parameterValues["Markdown source:"];

    // Get the MarkdownRenderer object.
    MarkdownRenderer renderer = MarkdownUtils.GetMarkdownRenderer(stateData);

    // Render the Markdown source to a Page object.
    Page pag = renderer.RenderSinglePage(text, width, out _);

    // ...

    // Draw the rendered Markdown page on the tree plot.
    graphics.DrawGraphics(x, y, pag.Graphics);

    // ...
}

Example

("Test Markdown:", "Markdown:# Markdown header\n\nMarkdown text")


Dash

Syntax

"Dash:[<units on>,<units off>,<phase>]"

Value type

VectSharp.Dash

Dash style buttons are specified by the Dash keyword; the default value is provided as a JSON number array containing number of units in which the dash is "on", the number of units in which it is "off", and the phase (i.e. the position along the dash pattern of the starting point).

Example

("Test dash:", "Dash:[10,10,0]")


Special parameters

In addition to the parameter types described above, there are additional "special" parameters, which provide access to underlying functionality in TreeViewer. These are not shown as controls on the window, thus their value cannot be changed by the user.


TreeCollection

Syntax

"TreeCollection:"

Value type

PhyloTree.TreeCollection

The TreeCollection keyword identifies a parameter that returns the collection of trees that have been loaded from the tree file. No additional settings are available.

Example

("Trees", "TreeCollection:")

Window

Syntax

"Window:"

Value type

TreeViewer.MainWindow

The Window keyword identifies a parameter that returns the window that contains the module. No additional settings are available. This will return null if the program is running in command-line mode.

Example

("Main window", "Window:")

InstanceStateData

Syntax

"InstanceStateData:"

Value type

TreeViewer.InstanceStateData

The InstanceStateData keyword identifies a parameter that returns the InstanceStateData object corresponding to the window that contains the module. No additional settings are available.

The properties of InstanceStateData objects are described in the section about Custom scripts.

Example

("State data", "InstanceStateData:")

⚠️ **GitHub.com Fallback** ⚠️