18
$\begingroup$

Let's create simple function:

SetAttributes[h, HoldFirst];
h[data_] := Dynamic[Grid[data], TrackedSymbols :> {data}]

test = RandomReal[1, {2, 2}];
h[test]

Now you can evaluate somewhere else:

 test = RandomReal[1, {2, 2}];

And our grid will be updated. However if it is only a part of huge structure: (here not so huge ofc)

 test2 = RandomReal[1, {2, 2, 2}];
 h[ test2[[1]] ]

Evaluating test2 = RandomReal[1, {2, 2, 2}] will not change anything because what's inside TrackedSymbols is not a Symbol.

Anticipating questions:

  • Yes, TrackedSymbols is necessary, code above is only a minimal example.

  • No I don't want to track whole test2.

  • No I don't want to split my test2 before passing it to h into pieces that are meant to be tracked later. I need the referrence to be clear, if I do something inside with part of test[[1]] then test will be updated. If I pass those new symbols then I will have to care about updating test each time. Or maybe there is a way of dealing with this?

Do I want too much? :)

$\endgroup$
10
  • $\begingroup$ When you leave out the option TrackedSymbols in the definition of h, both examples work. So why is TrackedSymbols necessary? $\endgroup$ Commented Oct 28, 2014 at 14:28
  • $\begingroup$ @FredSimons because Grid may be heavy layout that I want to recalculate only when necessary and here it would be even if you change part of test2 that is not used in Grid. $\endgroup$
    – Kuba
    Commented Oct 28, 2014 at 15:02
  • $\begingroup$ Is there any reason why you cannot define test3 := test2[[1]] and then use h[test3]? $\endgroup$
    – WReach
    Commented Oct 28, 2014 at 18:18
  • $\begingroup$ @WReach because inside h there may be a procedure to change data so the oryginal table will not be updated. Moreover, your example will not be updated because test3 is not changing unless evaluated. $\endgroup$
    – Kuba
    Commented Oct 28, 2014 at 18:30
  • 2
    $\begingroup$ A nicely presented problem, I missed this one before, +1. I don't know if there is any better solution than introducing as many independent symbols as parts you want to update -- and I know this won't qualify as a good answer. Just for the reference, a realistic use case is presented here: dynamic minesweeper, where such a part-tracking mechanism would be really useful. $\endgroup$ Commented Oct 29, 2018 at 16:26

3 Answers 3

6
$\begingroup$

Summing up:

  • There is no built-in way to track part of a symbol.

  • Albert's answer uses additional symbol for each part we track. It is useful to digest that code as this idiom can help in variety of cases (e.g. listening to Length[a], etc). Unfortunately it does not scale when you want to track hundreds of indices or you don't know how many you will need.

  • Alternative but limited solution is proposed in Scaling Dynamic widgets up. It constructs a DynamicModule with autogenerated symbols based on desired expression structure. That solution was not flexible enough for my daily use cases

Finally, I think I have something that fits my needs, it is presented in another answer in:

Scaling Dynamic widgets up and https://github.com/kubaPod/PreciseTracking

It is designed to work with associations because they are handy anyway and using lists encourages working with ranges of indices / spans etc and that will be tough to handle.

To not repeat linked examples I will briefly explain here how Dynamics work, how that solution works and how we could adapt it for List/Part if needed.

How Dynamic works

Actors: FE-FrontEnd, K-Kernel:

  • FE->K : user input "Dynamic[x]"
  • K : "Dynamic[x]" -> Dynamic[Global`x] -> DynamicBox[ToBoxes[x, StandardForm]]
  • K->FE : here is the result, a DynamicBox
  • FE : nice, let me create a 'node' in an object that represents the notebook state
  • FE->K : could you please run ToBoxes[x, StandardForm] this for me? I need to display something. Also, please 'track' that expression and let me know if it changes, here is an ID of a 'node' which cares about it.
  • K : ok, it seems that next time x changes then I need to notify FE with ID.
  • K->FE : ToBoxes[x, StandardForm] -> "1"

[coffee break]

  • user : x=2 (I am skipping in/out communication for that call)
  • K : ok, x mutated! let me inform the FE and I can finally remove it from tracking list.
  • K->FE : you better update ID!
  • FE : is ID visible at all? If not I will add it to a queue for later. If it is:
  • FE->K : ok, gimme ToBoxes[x, StandardForm] and please track it again.

[the story repeats]

To take control over Dynamic we have to:

  1. Suppress default tracking (TrackedSymbols :> {})
  2. Capture box ID (ValueTrackGetTrackingState[]`)
  3. Establish 'tracking' for desired expressions
  4. Handle changes of tracked expressions
  5. Tell FE to update given ID.(FrontEnd`UpdateDynamicObjects)
  6. Keep the loop alive if needed

Points 1, 2 and 5 are easy (if you know them). Unfortunately SetValueTrack and friends are internal functions optimized for symbols tracking so we can't use them and don't really want to. The idea is to handle tracking manually:

  1. Dynamic[Track[asso[key]]; whatever, TrackedSymbols :> {}]

  2. V-G trick to call 5. when asso[key] = val is executed

  3. by keeping Track[...] in Dynamic the loop is self sustaining.

Implementation can be found in https://github.com/kubaPod/PreciseTracking/blob/master/PreciseTracking/PreciseTracking.m and is very short. Once it grows I will bring here a small example for clarity.

$\endgroup$
2
  • $\begingroup$ Hello! Are you still working on this? I'm having trouble getting Dynamic[Print[a]; asso[a]] and Dynamic[Print[b]; asso[b]] to actually print anything. It's just behaving like Dynamic[asso[a]] and Dynamic[asso[b]] (version 12.2 and 13.1) $\endgroup$
    – QuantumDot
    Commented Aug 9, 2022 at 4:06
  • $\begingroup$ @QuantumDot I was not but I am now working on an update to make it even more user friendly. As for your issue, are you sure? check messages window, it works like it used to for me on Win 10 MMA 13.* $\endgroup$
    – Kuba
    Commented Aug 9, 2022 at 7:55
5
$\begingroup$

I understand that you are looking for sort of a conditional updating: only when the displayed portion of the variable test changes. My feeling is that it is unavoidable that the displayed portion of test has to be recomputed at each change of test. But that can be done outside Dynamic. Here is an smaller example, showing the sort of solution I am thinking of:

displaypart[]:=(a=test[[1]];);
test={1,2,3,4,5};
displaypart[];
Dynamic[{RandomReal[], a}]

(* {0.228823,1} *)

I added a RandomReal[] so that it can be seen whether there was an updating or not. Now two experiments:

test[[5]]=12;displaypart[]

Nothing happens with the Dynamic output.

test[[1]]=12;displaypart[]

The dynamic output now shows a different real and the integer 12.

I am not quite sure that this is what you are looking for.

$\endgroup$
2
  • $\begingroup$ Unfortunately no, it is in contradiction with third bullet-point. Take a look at extended example of your way: f = Function[var, Dynamic[{RandomReal[], var, Button["+", var++]}], HoldFirst] now use f[a] and use button, refference is lost, test is not updated. $\endgroup$
    – Kuba
    Commented Oct 28, 2014 at 18:43
  • 1
    $\begingroup$ f=Function[var,DynamicModule[{var=1},Dynamic[{RandomReal[],var,Button["+",var++]},TrackedSymbols:>{var}]],HoldFirst] and then f[a] does update. $\endgroup$ Commented Oct 28, 2014 at 23:28
4
$\begingroup$

I'm not sure whether the following will fulfill all of your requirements as it does make a copy of the part which is to be shown. But that copy is only used to control when updates are needed and only within a purly local variable. This should not be a problem concerning the updating of the original symbol, but in case you are concerned about the memory consumption of the copy the following will not fulfill the "no copy" requirement (if the part is still a large amount of data one probably could store just a hash value of it instead of the full data and use that for the comparation...). The whole approach is not very elegant but I think should at least come very close to what you want:

SetAttributes[h,HoldFirst];
h[data_Symbol[[partspec___]]]:=DynamicModule[{
        current=data[[partspec]],trigger=0,doupdate=True
    },
    DynamicWrapper[
        Dynamic[
            trigger;
            Column[{
                DateString[],
                InputField[Dynamic[
                    data[[partspec]],
                    (doupdate=False;current=data[[partspec]]=#;FinishDynamic[];doupdate=True)&
                ],Number]
            }],
            TrackedSymbols:>{trigger}
        ],
        If[And[TrueQ[doupdate],current!=data[[partspec]]],trigger=AbsoluteTime[]],
        TrackedSymbols:>{data}
    ]
]


test = RandomReal[1, {2, 2}];
h[test[[1,1]]]

you should now see that the output is updated when you do:

test[[1, 1]] = RandomReal[];

but not when you do:

test[[2, 1]] = RandomReal[];

if you edit the input field, test[[1,1]] will be updated as required.

I think that the same can probably be achieved with only using two additional dynamic variables (or one?) but my own tries to achieve that did all suffer from the problem that the InputField is then recreated when the change is made from within it and loose the focus in the process, which I find quite annoying.

$\endgroup$
1
  • $\begingroup$ Thanks for your time, it seems to do the job for that minimal example. I'm not sure how useful it can be in real world complex cases, will pin you when I'm back to the project where this issue originates. $\endgroup$
    – Kuba
    Commented Mar 27, 2015 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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