Begära information från R

I vissa fall kan man vilja hämta ytterligare information från R, som ska presenteras i insticksprogrammets användargränssnitt. Man kanske exempelvis vill erbjuda ett urval av nivåer för en faktor som användaren har valt att analysera. Från version 0.6.2 av RKWard är det möjligt att göra det. Innan vi börjar är det viktigt att du är medveten om några förbehåll:

R-kod som körs inne i insticksprogrammets logik för användargränssnittet utvärderas i R:s händelsesnurra, vilket betyder att de kan köras medan andra beräkningar pågår. Det görs för att se till att insticksprogrammets användargränssnitt är användbart även när R är upptaget med att göra andra saker. Dock gör detta att det är mycket viktigt att koden inte har några sidoeffekter. I synnerhet:

  • Gör inte några tilldelningar i .GlobalEnv eller några andra icke-lokala omgivningar.

  • Skriv inte ut någonting i utdatafilen.

  • Rita inte någonting på skärmen.

  • I allmänhet, gör ingenting som får sidoeffekter. Koden kan läsa in information, inte "göra" någonting.

Med det i åtanke, här är det allmänna mönstret. Det används inne i en sektion med skriptbaserad logik för användargränssnittet:

<script><![CDATA[
                                last_command_id = -1;
                                gui.addChangeCommand ("variable", "update ()");
                                update = function () {
                                        gui.setValue ("selector.enabled", 0);
                                        variable = gui.getValue ("variable");
                                        if (variable == "") return;

                                        last_command_id = doRCommand ('levels (' + variable + ')', "commandFinished");
                                }

                                commandFinished = function (result, id) {
                                        if (id != last_command_id) return;  // ett annat resultat är på väg att anlända
                                        if (typeof (result) == "undefined") {
                                                gui.setListValue ("selector.available", Array ("ERROR"));
                                                return;
                                        }
                                        gui.setValue ("selector.enabled", 1);
                                        gui.setListValue ("selector.available", result);
                                }
                ]]></script>
        

Här är variable en egenskap som innehåller ett objektnamn (t.ex. inne i en <varslot>). Så snart den ändras, vill man uppdatera visningen av nivåer inne i <valueselector>, benämnd selector. Nyckelfunktionen här är doRCommand(), som har kommandosträngen att köra som första parameter, och namnet på en funktion att anropa när kommandot är klart som andra parameter. Observera att kommandot körs asynkront, och det gör saker och ting lite mer komplicerat. Man vill åtminstone försäkra sig om att <valueselector> förblir inaktiverad medan den inte innehåller aktuell information. En annan sak är att man potentiellt kan ha köat mer än ett kommando innan det första resultatet levereras. Det är därför varje kommando ges en "id", som vi lagrar i ast_command_id för senare referens.

När kommandot är klart, anropas det angivna återanropet (commandFinished i det här fallet) med två parametrar: Själva resultatet, och id för motsvarande kommando. Resultatet har en typ som liknar representationen i R, dvs. ett numeriskt fält, om resultatet är numeriskt, etc. Det kan till och med vara en list() i R, men i det här fallet representeras det som en Array() i JS utan namn.

Observera att till och med det här exemplet är något förenklat. I verkligheten bör man vidta ytterligare försiktighetsåtgärder, för att t.ex. undvika att lägga till ett extremt antal nivåer i väljaren. De goda nyheterna är att du troligtvis inte behöver göra allt själv. Exemplet ovan kommer från insticksprogrammet rkward::level_select, som du helt enkelt kan inbädda i ditt eget insticksprogram. Det låter dig till och med ange ett annat uttryck att köra istället för levels().