12
$\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$
  • $\begingroup$ When you leave out the option TrackedSymbols in the definition of h, both examples work. So why is TrackedSymbols necessary? $\endgroup$ – Fred Simons Oct 28 '14 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 Oct 28 '14 at 15:02
  • $\begingroup$ Is there any reason why you cannot define test3 := test2[[1]] and then use h[test3]? $\endgroup$ – WReach Oct 28 '14 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 Oct 28 '14 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$ – István Zachar Oct 29 '18 at 16:26
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$
  • $\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 Mar 27 '15 at 22:40
4
$\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$
  • $\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 Oct 28 '14 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$ – Rolf Mertig Oct 28 '14 at 23:28
3
$\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$

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.