Workspace 6.21.5
More Advanced Widget Use


Note
As this Overview's name suggests, this section covers somewhat "Advanced" topics. This overview is more intended to show you what can be done - rather than necessarily exactly how to do it. Fell free to skip if the topics are too complex on first reading.
In case you get stuck, a sample workflow has been provided for you.

Prerequisites

Namespaces for Global Names

Consider the following scenario. The user builds up a workflow to perform a certain task (we will refer to this as "Task" in the following discussion). They create a user interface for their workflow and set up global names to allow various inputs and outputs to be connected to widgets in their UI. The user then realises that they might want to re-use this workflow as part of some larger workflow defined in workflow "ManyTasks". Indeed, they might want to include multiple copies of their "Task" workflow with each instance receiving a different set of input values. So they create the "ManyTasks" workflow and then add multiple "Task" instances as externally referenced nested workflows. This allows them to keep working on "Task" and have any changes they make picked up automatically by "ManyTasks". A simple version of such a workflow may look like this:

The ManyTasks workflow with three nested Task workflows

This works fine until the user wants to load up a UI for the "ManyTasks" workflow. Because there are multiple instances of "Task", there will now be duplicates of all its global names in the global name table. When a UI is loaded, it won't know which instance of "Task" it should connect to.

Global Name Table with repeated names

In order to handle this situation, namespaces can be used. In the properties of any nested workflow, a namespace can be specified. This acts somewhat like a subfolder for global names, much like how a directory does for files on your computer's file system.

Entering a namespace for a nested workflow

When a widget is connected to an input or output, Workspace walks up the workflow hierarchy and records all namespaces it finds along the way.

Global Name Table with namespaces

This information is then saved as a full path that the widget should use when it needs to find the input/output (ie when the .ui file is loaded and needs to be connected to a workflow). This full path is referred to as the fully scoped global name and it will consist of all the namespaces starting at the root workflow followed by the global name of the input/output.

Connecting the value from Task1 to the UI

The namespaces and the global name are separated by a forward slash, so the fully scoped global name really does look like a file system path.

Fully scoped global name

As another example, consider a deeply nested workflow hierarchy with four levels of nesting. The top/root level is "A", "B" sits within "A", "C" sits within "B" and "D" sits within "C". Now let's assume that "B" has been given a namespace of "Beans" and "D" has been given a namespace of "Delicious". Let's say that we want to find the fully scoped global name of an input for an operation in "D" where that input has the global name "Flavour". In this scenario, the fully scoped global name of that input will be "Beans/Delicious/Flavour". Both "A" and "C" have no namespace specified, so they don't add anything to the fully scoped global name - nested workflows are not required to have a namespace. If they have no namespace, they are essentially absorbed into their parent when working out fully scoped global names. Note also that there is no leading forward slash in the fully scoped global name.

Widgets That Do Not Cause Updates

After working with Workspace and becoming comfortable with creating UI's and connecting them to workflows, a situation usually arises where you want to have a widget show the state of some input or output on a branch of workflow which might or might not otherwise need to execute. For example, consider a workflow where one of two different branches should execute based on some condition. This situation is shown in the following image:

Example: Switching between two workflow branches

Now assume you want to attach a widget to the output of the operation labelled "Branch A" and another widget to the output of operation "Branch B". However, only one of the two branches is considered "active", based on the value provided by the "Switch" operation. The widget attached to the inactive branch doesn't need to be showing anything. In fact, the inactive widget could potentially be completely hidden in the UI (widgets can be made invisible). The problem here is that once a widget is attached to an input or output, by default it will ask the Workspace to keep that input/output up to date. This will result in both branches always being executed, even though only one is actually needed. If executing a branch is an expensive exercise, this could be a serious problem.

What is needed is a way to mark a widget to only reflect changes to the underlying data without asking Workspace to explicitly update it. If the data changes as a result of normal workflow execution, then the widget should reflect that change. However, if Workspace does not update that data, the widget will not force it to be updated. Such a facility is available when creating a UI as a .ui file within Qt Designer. When the user drags from an input/output from a workflow and drops it on the name of a widget in Qt Designer, a property called wsDataPath is created for that widget which tells Workspace where to find the input/output to connect that widget to. It also creates a second property with the name triggerUpdates which has the value true by default (Qt Designer shows it as a checked checkbox). When this second property is checked/enabled, the widget triggers Workspace updates as you would normally expect. If, however, this property is unchecked/disabled, the widget will not force the Workspace to keep that input/output up to date. Instead, it will give the more relaxed behaviour described just above. Thus, by unchecking this property, it is possible to make that widget show the state of an input or output without forcing it to become part of the execution path of the underlying workflow.

A slightly more complicated but just as interesting feature of having triggerUpdates unchecked is that the widget is still able to modify the value at the associated input/output, even if that input/output is not currently up to date. This is because widgets can force an input/output to at least have data which the widget can then modify, even if the workflow does not yet consider it up to date. Quite often, the result is that the data is modified to have the desired value, then the workflow execution comes along later and simply marks it as up to date without changing that value. In more complex situations, the data might actually be shared with operations further upstream, in which case the widget is actually modifying upstream data, but still in a way which is safe (the Workspace ensures this).

The ability for widgets to modify data held by inputs or outputs even when they are not up to date may seem odd at first. There are, however, some widgets which specifically rely on this feature, notably a combo box connected to an input or output with data type StringSelection. In this case, the widget will modify the data but it will actually cause something upstream of that input/output to change. Generally, users do not need to concern themselves with how these situations work.

For .ui files created before the triggerUpdates functionality was added to Workspace (which was version 2.18.0), widgets with a wsDataPath property will not have a triggerUpdates property. To have the triggerUpdates property added, either repeat making the connection from the workflow to the widget in Qt Designer (recommended) or if you know what you are doing, you can also manually add the property directly within Designer. If the triggerUpdatesproperty is missing, the widget will behave as though the property was set to true, so older .ui files will continue to behave as they always have in the past.