18

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

Kuba
  • 136,707
  • 13
  • 279
  • 740

2 Answers2

15

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?

István Zachar
  • 47,032
  • 20
  • 143
  • 291
Kuba
  • 136,707
  • 13
  • 279
  • 740
  • Very nice! Getting Dynamic to scale well has always been a mild nightmare – b3m2a1 Jun 06 '18 at 21:09
  • 1
    @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. – Kuba Jun 06 '18 at 21:14
  • @Kuba Nice work! Does this speed-up work in cloud notebooks too? – M.R. Jun 07 '18 at 15:46
  • 2
    @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: https://mathematica.stackexchange.com/search?q=user%3A5478+MVue – Kuba Jun 07 '18 at 16:31
6

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]
Kuba
  • 136,707
  • 13
  • 279
  • 740
  • Do you mean you’re calling into "ValueTrack`" and such manually? That’d be very impressive. – b3m2a1 Jun 18 '19 at 00:37
  • @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. – Kuba Jun 18 '19 at 04:07