In some cases, you may want to fetch further information from R, to be presented in your plugin's UI. For instance, you may want to offer a selection of the levels of a factor that the user has selected for analysis. Since version 0.6.2 of RKWard it is possible to do so. Before we start, it is important that you are aware of some caveats:
R Code run from inside the plugin's UI logic is evaluated in R's event loop, meaning they can be run while other computations are running. This is to make sure your plugin's UI will be usable, even while R is busy doing other things. However, this makes it really important, that your code does not have side effects. In particular:
Do not make any assignments in .GlobalEnv or any other non-local environment.
Do not print anything to the output file.
Do not plot anything on-screen.
In general, do not do anything that has side-effects. Your code may read in information, not "do" anything.
With this in mind, here is the general pattern. You will use this inside a scripted UI logic section:
<script><![CDATA[
let update = gui.addChangeCommand ("variable", function () {
gui.setValue ("selector.enabled", 0);
variable = gui.getValue ("variable");
if (variable == "") return;
new RCommand('levels (' + variable + ')', "myid").then(result => {
gui.setValue ("selector.enabled", 1);
gui.setListValue ("selector.available", result);
}).catch(msg => {
if (msg === "outdated") return; // command was canceled, because new one is about to arrive -> benign
// possibly other error handling, msg carries the warnings and error messages produced,
// if the command failed e.g.:
gui.setListValue ("selector.available", Array ("ERROR:", msg));
});
});
]]></script>
Here, variable is a property holding an object name (e.g. inside a <varslot>). Whenever that changes, you will want to update the display
of levels inside the <valueselector>, named selector. The key function here is the constructor statement new RCommand(), taking as first parameter
the command string to run. Note that the command is running asynchronously, and this makes things
a bit more complex. For one thing you want to make sure, that your <valueselector> remains disabled, while it does not contain up-to-date information.
Secondly, as the user may make changes, quickly, more than one command may have been generated, before we receive any result. Thus, you'll need to make sure to act on the most recent
command, only.
To deal with asynchronousicty, what is returned, here, is a Promise object. More info on this powerful javascript feature can be found on the internet. The important thing to know, here, is that appending a .then() statement allows you to specify what will happen when the command has completed, and a .catch() statement can be used to handle any errors. Again, keep in mind, that the .then() block does not get executed right away. To understand the implications of this, it may be helpful, during development, to insert a Sys.sleep(1); into your R command, to see what happens, when a command does not complete, immediately.
Finally, to deal with multiple commands being generated, you can specify a second argument to new RCommand(), ("myid", in this example). Any commands with the same (freely chosen) identifier will be understood to belong to the same queue. RKWard will then make sure that only the latest command will actually trigger the .then() block, while any obsoleted commands will arrive in the .catch() block. Here, obsoleted commands can be identified, as the string "outdated" is passed as their value, while for any other possible errors, the warnings, and error messages are passed along.
Note that this example is somewhat simplified. In reality you should take additional precautions, e.g. to avoid putting an extreme amount of levels into the selector. The good news is that probably you do not have to do all this yourself. The above example is taken from the rkward::level_select plugin, for instance, which you can simply embed in your own plugin. This even allows you to specify a different expression to run in place of levels().
Note
In earlier versions of RKWard R commands were run using a somewhat more complex doRCommand() function. You may still see that in some plugins, but it is not recommended to use it in new code.