Creating richer interactive interfaces that are generated by a DynamicModule invariably requires more complex data structures and in particular, a larger argument space as specified by DynamicModule's local variables. Managing the extra functionality within DynamicModule's body then inevitably involves the need for ongoing code modularization which in turn requires repeated passing down of this argument space.

Here's an example for a 2-element argument space.

Component1[x_,y_]:=Button["Increase x",x++];
Component2[x_,y_]:=Button["Decrease y",y--];


This is not a problem for only 2 arguments but becomes a serious issue for larger argument spaces both for reading and writing code. For writing not only does the whole argument space need to be re-typed each time a new piece of modularization or functionality is added, but any changes to this argument space (as part of ongoing design changes for example) then requires changes throughout the code base. The reading issue revolves around a function's arguments clogging valuable space while distracting from the function's logic.

Ideally what is needed is to replicate the "magic" of OptionsPattern[] but for local DynamicModule variables in which these variables can be modified in a single place and have the effects automatically cascade (a bonus would be for the syntax colouring to be maintained in the body of any function accessing variables in this space).

Roughly - with obj denoting the variable space - something like:

(* not correct syntax *)

Component1[obj] := Button["Increase x", x++];
Component2[obj] := Button["Decrease y", y--];
Component3[obj] := {Dynamic@x, Dynamic@y};

obj = {x = 1, y = 2};

Column[{Component1[obj], Component2[obj], Component3[obj]}]]
  • $\begingroup$ Why do you want to pass every variable to all functions? This seems quite at odds with the idea of having a function in the first place. $\endgroup$ – jVincent Sep 14 '12 at 13:24
  • $\begingroup$ You might find the following toolkit helpful if you are interested in doing serious GUI development from within Mathematica: Bailey's Super Widget Package $\endgroup$ – StackExchanger Sep 15 '12 at 7:51
  • $\begingroup$ I think I've invested too much in the UI within Mma although I wonder if David will one day introduce functionality to automatically translate Mma UIs into Java equivalents? - probably asking a bit much! $\endgroup$ – Ronald Monson Sep 17 '12 at 19:58

I think this is a case for using (delayed) rules. In particular, I suggest the following:

ClearAll[Component1, Component2, Component3];
Component1[obj_] := Button["Increase x", Increment["x"]] /. obj;
Component2[obj_] := Button["Decrease y", Decrement["y"]] /. obj;
Component3[obj_] := Unevaluated[{Dynamic["x"], Dynamic["y"]}] /. obj;

where obj is supposed to be a rule or, generally, list of rules. Then:

DynamicModule[{obj, x = 1, y = 2},
    obj = {"x" :> x, "y" :> y}; 
    Column[{Component1[obj], Component2[obj], Component3[obj]}]]

will do what you want. Since rules are flexible, you can add more elements without affecting the existing code. Rules are used as a device of concise injection of unevaluated code, state in this case.

In some sense, what is happening here is non-trivial, since all components are actually functioning like tiny macros, with the full code being "assembled" at run-time. I generally think that the best way to construct complex UI-s in Mathematica is through code-generation.

  • $\begingroup$ "I generally think that the best way to construct complex UI-s in Mathematica is through code-generation." Are their use-cases you can envisage for which this code-generation idiom has advantages over jVincent's answer? $\endgroup$ – Ronald Monson Sep 14 '12 at 19:45
  • $\begingroup$ @RonaldMonson Yes, there are. Have a look e.g. here to see what code generation can buy you. Basically, there are "theoretical" advantages of it: any comples UI in mma is a (large) Mathematica expression with often complex embedded state, and usually not easily amenable to breaking into smaller chunks. I know a bunch of cases where code generation allows to dramatically simplify life. $\endgroup$ – Leonid Shifrin Sep 14 '12 at 19:54
  • $\begingroup$ @RonaldMonson With more complex / larger UI, a lack of proper encapsulation (global variables used in many places without control) can make life miserable. Code generation may allow one to combine widgets from smaller ones, and test those in isolation. Using usual functions is often not an option since lots of expressions are held, and one then need complicated code injection mechanisms. $\endgroup$ – Leonid Shifrin Sep 14 '12 at 19:57
  • $\begingroup$ I see the similarity in the code-generation idiom with the linked example but that seems to be solving a different problem associated more with code-refactoring whereas this seems to be more of a scoping issue. The advantage of this code-generation idiom to "dramatically simplify life" I agree with and use it quite a bit myself (but not in the exact way you showed). Having said this, sometimes beyond a certain threshold there can be a trap to move more and more logic into this generic code-generation template as it increasingly strains to accommodate different situations. $\endgroup$ – Ronald Monson Sep 17 '12 at 18:39
  • $\begingroup$ ... Sometimes I find it easier to just do a copy and paste of a basic template and modify this with the specific logic. I guess it depends on how common the "common factor" is. Anyway, what is aimed for in this post is to address the "not easily amenable to breaking into smaller chunks." of complex UI. This idiom does allow the variable definitions to be done in the one place as requested but I don't quite see any advantages over the "no argument" solution since effectively the same code ends up getting generated but with more involved syntax. $\endgroup$ – Ronald Monson Sep 17 '12 at 18:40

If you want each component to have access to the entire scope, rather then pass it through as an argument to a function, you can also just not have any function at all:

 Component1 := Button["Increase x", x++];
 Component2 := Button["Decrease y", y--];
 Component3 := {Dynamic@x, Dynamic@y};

 DynamicModule[{x = 1, y = 2},
 Row[{Component1, Component2, Component3}]
  • $\begingroup$ This seems to be the "obvious" (obviously not to myself!) solution and goes to your comment about the need to have a function at all. I guess the bundle of a function's advantages -collecting up repeatable transformation into a single callable unit, only changing what is necessary and the clarity gained from modularization-instilled an imperative to always want to provide arguments. When only the last advantage is needed this imperative disappears. I think in the back of my mind is also Ajasja's comment "Usually using global (or in this case semi-global) variables is frowned upon" ... $\endgroup$ – Ronald Monson Sep 14 '12 at 19:34
  • $\begingroup$ … I assume he is alluding to such a practice causing unexpected changes in the state with code chunks telling you what is to be modified- both of which perhaps are not so much an issue in this context? (bearing in mind that deeper transformations likely to be repeatable can always be lazily functionalized and used with these semi-global variables) $\endgroup$ – Ronald Monson Sep 14 '12 at 19:37

Declaring the components inside the DynamicModule would allow you to have access to all Dynamic Module variables.

DynamicModule[{Component1, Component2, Component3, x = 1, y = 2},
   Component1[] := Button["Increase x", x++];
   Component2[] := Button["Decrease y", y--];
   Component3[] := {Dynamic@x, Dynamic@y};
 Row[{Component1[], Component2[], Component3[]}]]

Usually using global (or in this case semi-global) variables is frowned upon, but here it might do the trick.

As an added bonus, the syntax colouring of variables in the functions is correct.

  • $\begingroup$ Similar to jVincent's answer except with the use of "[]" makes the SetAttributes calls redundant (is it really a function to justify their inclusion?). As written the syntax colouring will be preserved but usually the components are more complex and need to be defined in other notebook cells so this colouring won't be preserved in general. $\endgroup$ – Ronald Monson Sep 14 '12 at 19:44
  • $\begingroup$ @RonaldMonson Yes, his answer is indeed similar to mine:) You're right about the SetAttribues, I was in a hurry. $\endgroup$ – Ajasja Sep 15 '12 at 7:41
  • $\begingroup$ +1 Indeed, going by own absorbed chronology :) And yes the form of the generality wasn't mentioned in the question so can't complain about it not appearing in an answer. I think the principle behind the colouring is an interesting feature that can/will be extended in many different directions which I will mention when I get some time and so want to avoid losing it if possible. $\endgroup$ – Ronald Monson Sep 15 '12 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.