Git commit d079f909c6a845e1fd8de5b9e01cade4ff20a2c0 by Thomas Friedrichsmeier.
Committed on 08/05/2025 at 18:31.
Pushed by tfry into branch 'master'.

Allow addChangeCommand to operate on arrays, and return command parameter, for 
convenience.

M  +1    -1    ChangeLog
M  +14   -6    doc/rkwardplugins/index.docbook
M  +2    -5    rkward/plugins/data/level_select.xml
M  +5    -6    rkward/plugins/data/limit_vector_length.xml
M  +4    -8    rkward/plugins/data/one_var_tabulation.xml
M  +2    -4    rkward/plugins/plots/barplot.xml
M  +2    -4    rkward/plugins/plots/dotchart.xml
M  +2    -4    rkward/plugins/plots/pareto.xml
M  +2    -4    rkward/plugins/plots/piechart.xml
M  +15   -12   rkward/scriptbackends/rkcomponentscripting.cpp
M  +1    -1    rkward/scriptbackends/rkcomponentscripting.h
M  +7    -2    rkward/scriptbackends/rkcomponentscripting.js

https://invent.kde.org/education/rkward/-/commit/d079f909c6a845e1fd8de5b9e01cade4ff20a2c0

diff --git a/ChangeLog b/ChangeLog
index 613c02f98..b62a1734d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,5 @@
 --- Version 0.8.2 - UNRELEASED
-- Added: Plugins: Enhance addChangeCommand() to accept callable objects, 
directly
+- Added: Plugins: Enhance addChangeCommand() to allow writing more readable 
GUI logic code
 - Added: Plugins: Simplify running R commands inside GUI logic code
 - Fixed: Crash on script errors in plugins
 - Fixed: Printing of captured R messages/warnings in plugins was broken
diff --git a/doc/rkwardplugins/index.docbook b/doc/rkwardplugins/index.docbook
index 7b1fa3041..240577508 100644
--- a/doc/rkwardplugins/index.docbook
+++ b/doc/rkwardplugins/index.docbook
@@ -942,8 +942,16 @@ R code.
[suppressed due to size limit]
        </para>
        <note><para>
-               Alternatively, <command>gui.addChangeCommmand()</command> 
accepts a string to be evaluated on changes, e.g. 
<replaceable>"my_update_function()"</replaceable>. This can be useful, if you 
want to call the same function for changes in several different UI elements.
-       </para></note>
+               If the same function should be invoked for changes in several 
elements, you can also pass an array of the respective 
<parameter>id=</parameter>s to <command>gui.addChangeCommmand()</command>. 
Further, for convenience, <command>gui.addChangeCommmand()</command> returns 
its second parameter, which
+               is useful, if you want to refer to this function elsewhere 
(e.g. to call it once during initialization). E.g.:
+       </para>
+       <programlisting>
+       let update = gui.addChangeCommand(["mode.string", "y.available"], 
function() {
+               // do something on each change of mode.string or y.available
+       });
+       update(); // do the same once during intialization
+       </programlisting>
+       </note>
        <para>
                The scripted approach to &GUI; logic becomes particularly 
useful when you want to change the available option according to the type of 
object that the user has selected. See <link linkend="guilogic_functions">the 
reference</link> for available functions.
        </para>
@@ -1660,8 +1668,7 @@ This chapter contains information on some topics that are 
useful only to certain
        <para>With this in mind, here is the general pattern. You will use this 
inside a <link linkend="logic_scripted">scripted UI logic</link> section:</para>
        <programlisting>
                &lt;script&gt;&lt;![CDATA[
-                               gui.addChangeCommand ("variable", "update ()");
-                               update = function () {
+                               let update = gui.addChangeCommand ("variable", 
function () {
                                        gui.setValue ("selector.enabled", 0);
                                        variable = gui.getValue ("variable");
                                        if (variable == "") return;
@@ -1675,7 +1682,7 @@ This chapter contains information on some topics that are 
useful only to certain
                                                // if the command failed e.g.:
                                                gui.setListValue 
("selector.available", Array ("ERROR:", msg));
                                        });
-                               }
+                               });
                ]]&gt;&lt;/script&gt;
        </programlisting>
        <para>Here, <parameter>variable</parameter> is a property holding an 
object name (&eg; inside a <command>&lt;varslot&gt;</command>). Whenever that 
changes, you will want to update the display
@@ -4483,7 +4490,8 @@ different types, using modifiers may lead to errors. For 
<replaceable>fixed_valu
        <varlistentry><term>getList(id)</term><listitem><para>Returns the value 
of the given child property as an array of strings (if possible). Returns the 
value of this property, if ID is omitted.</para></listitem></varlistentry>
        <varlistentry><term>setValue(id, value)</term><listitem><para>Set the 
value of the given child property to 
<emphasis>value</emphasis>.</para></listitem></varlistentry>
        <varlistentry><term>getChild(id)</term><listitem><para>Return an 
instance of the child-property with the given 
<emphasis>id</emphasis>.</para></listitem></varlistentry>
-       <varlistentry><term>addChangeCommand(id, 
command)</term><listitem><para>Execute <emphasis>command</emphasis> whenever 
the child property given by <emphasis>id</emphasis> changes. Command can either 
be a string to be evaluated, or callable value (usually a 
function).</para></listitem></varlistentry>
[suppressed due to size limit]
+       <para>The function returns the parameter 
<replaceable>command</replaceable>, for convenience (so you can e.g. assign it 
to a variable and/or call it during 
initialization).</para></listitem></varlistentry>
        </variablelist></para></listitem>
 </varlistentry>
 <varlistentry><term>Class "RObject"</term>
diff --git a/rkward/plugins/data/level_select.xml 
b/rkward/plugins/data/level_select.xml
index 6741cb4d2..d95e994c2 100644
--- a/rkward/plugins/data/level_select.xml
+++ b/rkward/plugins/data/level_select.xml
@@ -21,10 +21,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                <script><![CDATA[
                                gui.setValue ("limitnote.visible", false);
 
-                               gui.addChangeCommand ("variable", "update ()");
-                               gui.addChangeCommand ("limit", "update ()");
-                               gui.addChangeCommand ("custom_expression", 
"update ()");
-                               update = function () {
+                               gui.addChangeCommand(["variable", "limit", 
"custom_expression"], function() {
                                        gui.setValue ("selector.available", "");
                                        gui.setValue ("selector.enabled", 0);
 
@@ -56,7 +53,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                                .catch(err => {
                                                        
gui.setListValue("selector.available", Array("ERROR:", err));
                                                });
-                               }
+                               });
                ]]></script>
        </logic>
        <dialog label="Level selector">
diff --git a/rkward/plugins/data/limit_vector_length.xml 
b/rkward/plugins/data/limit_vector_length.xml
index cff8fac8c..a582fd5fd 100644
--- a/rkward/plugins/data/limit_vector_length.xml
+++ b/rkward/plugins/data/limit_vector_length.xml
@@ -16,12 +16,11 @@ SPDX-License-Identifier: GPL-2.0-or-later
                <connect client="custom_stat.enabled" 
governor="is_custom_stat"/>
 
                <script><![CDATA[
-                               gui.addChangeCommand ("sorting.string", 
"updateDescription ()");
-                               gui.addChangeCommand ("cutoff.int", 
"updateDescription ()");
-                               updateDescription = function () {
-                                       gui.setValue ("parameters", '"Limit"="' 
+ gui.getValue ("cutoff.int") + ' ' + gui.getValue ("sorting.string") + ' 
values"');
-                               }
-                               updateDescription ();
+                               updateDescription = function() {
+                                       gui.setValue("parameters", 
`"Limit"="${gui.getValue("cutoff.int")} ${gui.getValue("sorting.string")} 
values"`);
+                               };
+                               gui.addChangeCommand(["sorting.string", 
"cutoff.int"], updateDescription);
+                               updateDescription();
                ]]></script>
        </logic>
        <dialog label="Limit Vector Length">
diff --git a/rkward/plugins/data/one_var_tabulation.xml 
b/rkward/plugins/data/one_var_tabulation.xml
index d78320a30..98ca983a1 100644
--- a/rkward/plugins/data/one_var_tabulation.xml
+++ b/rkward/plugins/data/one_var_tabulation.xml
@@ -21,10 +21,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                <connect client="custom_stat.enabled" governor="is_custom"/>
 
                <script><![CDATA[
-                       gui.addChangeCommand ("stat.string", "updateFunLabel 
()");
-                       gui.addChangeCommand ("custom_stat.text", 
"updateFunLabel ()");
-                       gui.addChangeCommand ("outcome.available", 
"updateFunLabel ()");
-                       updateFunLabel = function () {
+                       updateFunLabel = gui.addChangeCommand(["stat.string", 
"custom_stat.text", "outcome.available"], function() {
                                var stat = gui.getValue ("stat.string");
                                var label;
                                if (stat == "freq") {
@@ -39,12 +36,11 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                }
                                gui.setValue ("fun_label", label);
                                updateDescription ();
-                       }
+                       });
 
-                       gui.addChangeCommand ("groups.available", 
"updateDescription ()");
-                       updateDescription = function () {
+                       updateDescription = 
gui.addChangeCommand("groups.available", function() {
                                gui.setValue ("parameters", i18n ("Tabulation 
groups") + '=paste (names (groups), collapse=' + i18nc ("Tabulate X by Y [by Z 
[...]]", " by ") + '), ' + i18n ("Tabulation statistic") + '=' + gui.getValue 
("fun_label"));
-                       }
+                       });
                        updateFunLabel ();
                ]]></script>
        </logic>
diff --git a/rkward/plugins/plots/barplot.xml b/rkward/plugins/plots/barplot.xml
index 8218c4dba..9f8075779 100644
--- a/rkward/plugins/plots/barplot.xml
+++ b/rkward/plugins/plots/barplot.xml
@@ -19,9 +19,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                        <connect client="x.enabled" 
governor="tabulate.checked.not"/>
                        <connect client="tabulate_options.varsource.selected" 
governor="vars.selected" />
                        <script><![CDATA[
-                               gui.addChangeCommand ("tabulate.checked", 
"updateFunLabel ()");
-                               gui.addChangeCommand 
("tabulate_options.fun_label", "updateFunLabel ()");
-                               updateFunLabel = function () {
+                               updateFunLabel = 
gui.addChangeCommand(["tabulate.checked", "tabulate_options.fun_label"], 
function() {
                                        if (gui.getValue ("tabulate.checked")) {
                                                gui.setValue 
("barplot_embed.plotoptions.default_ylab", gui.getValue 
("tabulate_options.fun_label"));
                                                gui.setValue 
("barplot_embed.plotoptions.default_xlab", "title");
@@ -29,7 +27,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                                gui.setValue 
("barplot_embed.plotoptions.default_ylab", "");
                                                gui.setValue 
("barplot_embed.plotoptions.default_xlab", "");
                                        }
-                               }
+                               });
                                updateFunLabel ();
                        ]]></script>
                </logic>
diff --git a/rkward/plugins/plots/dotchart.xml 
b/rkward/plugins/plots/dotchart.xml
index 4e4f05479..b648eb9c7 100644
--- a/rkward/plugins/plots/dotchart.xml
+++ b/rkward/plugins/plots/dotchart.xml
@@ -22,9 +22,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                <connect client="tabulate_options.varsource.selected" 
governor="vars.selected" />
 
                <script><![CDATA[
-                       gui.addChangeCommand ("tabulate.checked", 
"updateFunLabel ()");
-                       gui.addChangeCommand ("tabulate_options.fun_label", 
"updateFunLabel ()");
-                       updateFunLabel = function () {
+                       updateFunLabel = 
gui.addChangeCommand(["tabulate.checked", "tabulate_options.fun_label"], 
function() {
                                if (gui.getValue ("tabulate.checked")) {
                                        gui.setValue 
("plotoptions.default_xlab", gui.getValue ("tabulate_options.fun_label"));
                                        gui.setValue 
("plotoptions.default_ylab", "title");
@@ -32,7 +30,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                        gui.setValue 
("plotoptions.default_xlab", "");
                                        gui.setValue 
("plotoptions.default_ylab", "");
                                }
-                       }
+                       });
                        updateFunLabel ();
                ]]></script>
        </logic>
diff --git a/rkward/plugins/plots/pareto.xml b/rkward/plugins/plots/pareto.xml
index dc3271e59..b6b7118b5 100644
--- a/rkward/plugins/plots/pareto.xml
+++ b/rkward/plugins/plots/pareto.xml
@@ -16,15 +16,13 @@ SPDX-License-Identifier: GPL-2.0-or-later
                <connect client="x.enabled" governor="tabulate.checked.not"/>
                <connect client="tabulate_options.varsource.selected" 
governor="vars.selected" />
                <script><![CDATA[
-                       gui.addChangeCommand ("tabulate.checked", 
"updateFunLabel ()");
-                       gui.addChangeCommand ("tabulate_options.fun_label", 
"updateFunLabel ()");
-                       updateFunLabel = function () {
+                       updateFunLabel = 
gui.addChangeCommand(["tabulate.checked", "tabulate_options.fun_label"], 
function() {
                                if (gui.getValue ("tabulate.checked")) {
                                        gui.setValue 
("plotoptions.default_ylab", gui.getValue ("tabulate_options.fun_label"));
                                } else {
                                        gui.setValue 
("plotoptions.default_ylab", quote ("Frequency"));
                                }
-                       }
+                       });
                        updateFunLabel ();
                ]]></script>
        </logic>
diff --git a/rkward/plugins/plots/piechart.xml 
b/rkward/plugins/plots/piechart.xml
index 4fae6856c..17a21f19f 100644
--- a/rkward/plugins/plots/piechart.xml
+++ b/rkward/plugins/plots/piechart.xml
@@ -27,9 +27,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                <connect client="angle_inc.enabled" governor="density_0.not"/>
 
                <script><![CDATA[
-                       gui.addChangeCommand ("tabulate.checked", 
"updateFunLabel ()");
-                       gui.addChangeCommand ("tabulate_options.fun_label", 
"updateFunLabel ()");
-                       updateFunLabel = function () {
+                       updateFunLabel = 
gui.addChangeCommand(["tabulate.checked", "tabulate_options.fun_label"], 
function() {
                                if (gui.getValue ("tabulate.checked")) {
                                        gui.setValue 
("plotoptions.default_main", "title");
                                        gui.setValue 
("plotoptions.default_sub", gui.getValue ("tabulate_options.fun_label"));
@@ -37,7 +35,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                        gui.setValue 
("plotoptions.default_main", "");
                                        gui.setValue 
("plotoptions.default_sub", "");
                                }
-                       }
+                       });
                        updateFunLabel ();
                ]]></script>
        </logic>
diff --git a/rkward/scriptbackends/rkcomponentscripting.cpp 
b/rkward/scriptbackends/rkcomponentscripting.cpp
index a65642d4a..57af6dd3b 100644
--- a/rkward/scriptbackends/rkcomponentscripting.cpp
+++ b/rkward/scriptbackends/rkcomponentscripting.cpp
@@ -92,16 +92,9 @@ void RKComponentScriptingProxy::evaluate(const QString 
&code, const QString &fil
        handleScriptError(result, filename);
 }
 
-void RKComponentScriptingProxy::addChangeCommand(const QString &changed_id, 
const QJSValue &command) {
+void RKComponentScriptingProxy::addChangeCommand(const QStringList 
&changed_ids, const QJSValue &command) {
        RK_TRACE(PHP);
 
-       QString remainder;
-       RKComponentBase *base = component->lookupComponent(changed_id, 
&remainder);
-       if (!remainder.isEmpty()) {
-               evaluate(QStringLiteral("error ('No such property %1 (failed 
portion was %2)');\n").arg(changed_id, remainder));
-               return;
-       }
-
        auto callback = [this, command](RKComponentBase *) {
                if (command.isCallable()) {
                        auto res = command.call();
@@ -111,10 +104,20 @@ void RKComponentScriptingProxy::addChangeCommand(const 
QString &changed_id, cons
                }
        };
 
-       if (base->isComponent()) {
-               connect(static_cast<RKComponent *>(base), 
&RKComponent::componentChanged, this, callback);
-       } else {
-               connect(static_cast<RKComponentPropertyBase *>(base), 
&RKComponentPropertyBase::valueChanged, this, callback);
+       for (const QString &changed_id : changed_ids) {
+               QString remainder;
+               RKComponentBase *base = component->lookupComponent(changed_id, 
&remainder);
+               if (!remainder.isEmpty()) {
+                       evaluate(QStringLiteral("error ('No such property %1 
(failed portion was %2)');\n").arg(changed_id, remainder));
+                       return;
+               }
+
+
+               if (base->isComponent()) {
+                       connect(static_cast<RKComponent *>(base), 
&RKComponent::componentChanged, this, callback);
+               } else {
+                       connect(static_cast<RKComponentPropertyBase *>(base), 
&RKComponentPropertyBase::valueChanged, this, callback);
+               }
        }
 }
 
diff --git a/rkward/scriptbackends/rkcomponentscripting.h 
b/rkward/scriptbackends/rkcomponentscripting.h
index ba38739db..10b8ac5fd 100644
--- a/rkward/scriptbackends/rkcomponentscripting.h
+++ b/rkward/scriptbackends/rkcomponentscripting.h
@@ -36,7 +36,7 @@ class RKComponentScriptingProxy : public QObject {
   public:
        // these are meant to be called from the script
        Q_INVOKABLE void include(const QString &filename);
-       Q_INVOKABLE void addChangeCommand(const QString &changed_id, const 
QJSValue &command);
+       Q_INVOKABLE void addChangeCommand(const QStringList &changed_ids, const 
QJSValue &command);
        /** @returns id of the command issued. */
        Q_INVOKABLE QVariant doRCommand(const QString &command, const QString 
&callback);
        Q_INVOKABLE void doRCommand2(const QString &command, const QString &id, 
const QJSValue resolve, const QJSValue reject);
diff --git a/rkward/scriptbackends/rkcomponentscripting.js 
b/rkward/scriptbackends/rkcomponentscripting.js
index 720c63f83..92e42f069 100644
--- a/rkward/scriptbackends/rkcomponentscripting.js
+++ b/rkward/scriptbackends/rkcomponentscripting.js
@@ -60,8 +60,13 @@ function Component(id) {
                return (new Component(this.absoluteId(id)));
        };
 
-       this.addChangeCommand = function(id, command) {
-               _rkward.addChangeCommand(this.absoluteId(id), command);
+       this.addChangeCommand = function(ids, command) {
+               if (Array.isArray(ids)) {
+                       _rkward.addChangeCommand(ids.map((id) => 
this.absoluteId(id)), command);
+               } else {
+                       _rkward.addChangeCommand(Array(this.absoluteId(ids)), 
command);
+               }
+               return command;
        };
 };
 

Reply via email to