16
$\begingroup$

The serious flaw of Dynamic is a combination of facts:

  • it is triggered/refreshed by any kind of mutation of a symbol,

  • careful metaprogramming/construction of symbols is really cumbersome

An example is worth a thousand words so:

{ Dynamic@x[[1]], Dynamic@x[[2]]}

You can't trigger one without other one being affected even if the value didn't change. And writing:

{ Dynamic @ x1, Dynamic @ x2 }

will be tough to automatically scale/maintain for regular users. A real world example can be found here:

Allow multiple GUI elements to react dynamically to interaction with a single element

The code in the answer is not something you'd love to write on daily basis:

DynamicModule[{}, Graphics[{
   ( ToExpression[
      "{sA:=state" <> ToString[#] <> ", sB:=state" <> ToString[#2] <> "}",
      StandardForm, 
      Hold
    ] /. Hold[spec_] :> With[spec, 
       {  Dynamic @ If[TrueQ[sA || sB], Red, Black], 
          Line[{pts[#1], pts[#2]}]
       }
    ]
   ) & @@@ edges
   ,
   PointSize[0.025],
   (
    ToExpression[
      "{sA:=state" <> ToString[#] <> "}", 
      StandardForm, 
      Hold
    ] /. Hold[spec_] :> With[spec, 
       { Dynamic @ If[TrueQ[sA], Red, Black], 
         EventHandler[ Point @ pts[#], 
           {"MouseEntered" :> (sA = True), "MouseExited" :> (sA = False)}
         ]
       }
    ]
   ) & /@ names
  }, 
  ImageSize -> Large]
 ]

Very often a smart, specific solution can be applied but it would be nice to be able to do mindless things and not be limited by syntax/design issues but real limitations of how much FrontEnd can handle.

What can we do to make programming of idioms presented in the linked question more approachable?


Related topic: How to track Part of Symbol or How to symbolize Parts of Symbol without copying data

$\endgroup$
15
$\begingroup$

Among features that will come, DynamicObjects` package provides FrontEndSymbol/FrontEndModule utilities which can be used to solve this problem.

It is not a golden hammer and needs understanding of what is going on under the hood but can be used in powerful ways. So I strongly recommend reading wiki before creating own examples:

https://github.com/kubaPod/DynamicObjects/wiki/FrontEndModule-and-FrontEndSymbol

TL;DR;

Follow installation steps from https://github.com/kubaPod/DynamicObjects

Then the code from the linked topic can be rewritten to:

Needs @ "DynamicObjects`";

n = 120;
names = Range[n];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], 250];

FrontEndModule[ 
  Graphics[
    { 
      { 
        Dynamic @ If[ 
          TrueQ[ FrontEndSymbol["state",#1] || FrontEndSymbol["state", #2] ]
        , Red
        , Black
        ]
      , Line[{pts[#1],pts[#2]}]
      }& @@@ edges    
    , {
        AbsolutePointSize@7
      , Dynamic @ If[ TrueQ[FrontEndSymbol["state",#1]], Red, Black]
      , EventHandler[Point@pts[#]
        , { "MouseEntered":>(FrontEndSymbol["state",#1]=True)
          , "MouseExited":>(FrontEndSymbol["state",#1]=False)
          }
        ]
      }& /@ names
    }
  , ImageSize -> Medium
  ]
]

enter image description here

Much cleaner, isn't it?

$\endgroup$
  • $\begingroup$ Very nice! Getting Dynamic to scale well has always been a mild nightmare $\endgroup$ – b3m2a1 Jun 6 '18 at 21:09
  • 1
    $\begingroup$ @b3m2a1 Thanks, a nightmare indeed. I was thinking about using it for your BlockBuilder because it gets slow quite quickly (not your fault, as any other similar app) but is otherwise a great tool. But currently it requires to implicitly declare all FrontEndSymbol at the beginning so I need to add something for easier creation/deletion of them. Probably based on local cell context or something rather than DynamicModules. $\endgroup$ – Kuba Jun 6 '18 at 21:14
  • $\begingroup$ @Kuba Nice work! Does this speed-up work in cloud notebooks too? $\endgroup$ – M.R. Jun 7 '18 at 15:46
  • 2
    $\begingroup$ @M.R. Unfortunately it does not, Cloud FrontEnd is completely different and even more closed. That is why I don't even try with what they offer: mathematica.stackexchange.com/search?q=user%3A5478+MVue $\endgroup$ – Kuba Jun 7 '18 at 16:31
5
$\begingroup$

DynamicObjects` were not flexible enough for my daily problems. Here is an alternative approach:

PreciseTracking` package (prototype stage) does not create intermediate symbols to manage binding with Dynamic but instead uses the same mechanism Mathematica uses for Dynamic updates. (how exactly it works is a subject for a longer post in future, an introduction can be found in https://mathematica.stackexchange.com/a/200571/5478).

You can install it from https://github.com/kubaPod/PreciseTracking

Check[
  << MPM`
, Import["https://raw.githubusercontent.com/kubapod/mpm/master/install.m"]
; << MPM`
]    

MPMInstall["kubapod", "PreciseTracking"]

And give it a try:

<< PreciseTracking`

asso = <|"a" -> 1, "b" -> 1|>; (*only 'flat' associations are handled atm*)

ToTrackedAssociation @ asso;  
  (*now asso[key] = value will try to update associated dynamics *)

Slider@Dynamic@asso["a"]

PreciseDynamic[Print@"a"; asso["a"], asso["a"]] 
PreciseDynamic[Print@"b"; asso["b"], asso["b"]]
  (*the second argument serves as a 'TrackedTarget' specification*)

enter image description here

It is in a prototype form and I am yet to test / extend it but I appreciate feedback.

And finally a solution for the original problem:

n = 100;
names = Range[n];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], 1500];

states = AssociationThread[names -> False];

ToTrackedAssociation@states;

Graphics[
{
  {
     PreciseDynamic[
      If[TrueQ[states[#1] || states[#2]], Red, Black],
      {states[#1], states[#2]}
     ], 
     Opacity@.5, Line[{pts[#1], pts[#2]}]
  } & @@@ edges,
  {
     AbsolutePointSize@12, 
     PreciseDynamic[If[TrueQ[states[#1]], Red, Black], states[#]], 
     EventHandler[
      Point@pts[#], 
      {"MouseEntered" :> (states[#1] = True), "MouseExited" :> (states[#1] = False)}
     ]
  } & /@ names
}, ImageSize -> Large]
$\endgroup$
  • $\begingroup$ Do you mean you’re calling into "ValueTrack`" and such manually? That’d be very impressive. $\endgroup$ – b3m2a1 Jun 18 at 0:37
  • $\begingroup$ @b3m2a1 I have my own trigger-id bin and I only capture box ids to put there and send UpdateDynamicObjects if needed. I could not control/adapt SetValueTrack etc so I did my own. You can check the package, it is 5 lines of code plus error handling. $\endgroup$ – Kuba Jun 18 at 4:07

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.