Table Element (VectorHash)

Known as TABLE_ELEMENT in the bound API, VectorHash allows users to specify custom entries, in the form of a table, that are, programmatically, available later in the form of an iterable.

Like other karabo properties, VectorHash is initialized by displayedName, description, defaultValue, and accessMode. As well, it has a rows field that describes what each row in the table contains.

This rows field expects a class that inherits from Configurable.

class RowSchema(Configurable):
    deviceId = String(
            displayedName="DeviceId",
            defaultValue="")
    instanceCount = Int(
            displayedName="Count")

This class can have as many parameters as desired, and these will be represented as columns in the table.

With RowSchema, the definition of the VectorHash is as follows:

class MyMLDevice(Device):
    userConfig = VectorHash(
                   rows=RowSchema,
                   displayedName="Hot Initialisation",
                   defaultValue=[],
                   minSize=1, maxSize=4)

The user will now be presented with an editable table:

../_images/VectorHash.png

Note that it is possible to provide the user with predefined entries, such as default values or reading a configuration file, by providing a populated array in the defaultValue option. The minSize and maxSize arguments can limit the table’s size if needed.

class MyMLDevice(Device):
    userConfig = VectorHash(
                   rows=RowSchema,
                   displayedName="Default Value Example",
                   defaultValue=[Hash("deviceId", "XHQ_EG_DG/MOTOR/1",
                                      "instanceCount", 1),
                                 Hash("deviceId", "XHQ_EG_DG/MOTOR/2",
                                      "instanceCount", 2)],
                   minSize=1, maxSize=4)

Customization

The Table Element can be a bit customized with the displayType attribute. Specifying for example

class RowSchema(Configurable):
    deviceId = String(
            displayedName="DeviceId",
            defaultValue="")
    state = String(
            defaultValue="UNKNOWN",
            displayType="State",
            displayedName="State")

will have a table schema that describes the state column with a displayType State. The graphical user interface can then color the column according to state color.

With Karabo 2.14.X, the table offers more customization, e.g.

class RowSchema(Configurable):
    progress = Double(
            displayedName="ProgressBar",
            displayType="TableProgressBar"
            defaultValue=0.0,
            minInc=0.0,
            maxInc=100.0)

    stringColor = String(
            defaultValue="anystring",
            displayType="TableColor|default=white&xfel=orange&desy=blue",
            displayedName="stringColor")

    numberColor = Int32(
            displayedName="Number Color",
            displayType="TableColor|0=red&1=orange&2=blue"
            defaultValue=0)

Hence, for different displayTypes more options are available.

  • A progressbar can be declared with TableProgressBar on a number descriptor.

  • Background coloring can be provided for strings and numbers with the TableColor displayType. The coloring is then appended in an URI scheme (separator &) which is append to the displayType after |. Declaration of a default background brush can be set with the default setting.

class RowSchema(Configurable):
    progress = Bool(
            displayedName="Bool Button",
            displayType="TableBoolButton",
            defaultValue=True)

For read only table element a button can be declared via TableBoolButton. The button is enabled depending on the boolean setting.

Clicking the button will send a Hash to the device via the slot requestAction.

The hash contains keys with data:

action: TableButton
path: the property key
table: the table data

The table data itself is a Hash with:

data = Hash(
    "rowData", h,
    "row", row,
    "column", column,
    "header", header)

The rowData is a Hash of the row of the table button. The elements row and column provide the indexes of the button and the header the column string.

Another option (since Karabo 2.15.X) for a button can be the TableStringButton. Besides access level considerations this button is always enabled to provide an action on a deviceScene or url.

class RowSchema(Configurable):

    description = String(
            displayedName="Description",
            defaultValue="")

    view = String(
            displayedName="View",
            displayType="TableStringButton",
            defaultValue="")

The value for both protocols are strings and an example to set a table

device_scene = "deviceScene|deviceId=YOURDEVICE&name=YOURSCENENAME"
open_url = "url|www.xfel.eu"

self.table = [Hash("description", "Important device", "view", device_scene),
              Hash("description", "Important device doc", "view", open_url)]

Vector handling in tables is significantly increased in Karabo 2.16.X. Specifiy a button with TableVectorButton to launch a list edit dialog via a button in the table element.

class RowSchema(Configurable):

    devices = VectorString(
                displayedName="View",
                displayType="TableVectorButton",
                defaultValue=[])

Using Entries

Once the VectorHash has been populated, it is possible to iterate through its rows, which are themselves internally stored as a TableValue, which itself encapsulates a numpy array. From Karabo 2.14.0 onwards it is possible to convert the np.array value to a list of Hashes with

table = self.userConfig.to_hashlist()

Moreover, iterating over the encapsulated numpy array can be done like

@Slot(displayedName="Do something with table")
async def doSomethingTable(self):
    # This loops over the array (.value)
    for row in self.userConfig.value:
        # do something ..., e.g. check the first column
        first_column = row[0]

Action on Update

If an action is required on VectorHash update, e.g. a row is added or removed, then the VectorHash should be defined within a decorator:

@VectorHash(rows=RowSchema,
            displayedName="Hot Initialisation",
            defaultValue=[])
async def tableUpdate(self, updatedTable):
    self.userConfig = updatedTable
    # This loops over the array (.value)
    for row in updatedTable.value:
        # do something ...