Specify local Dynamic in Grid
Asked Answered
U

2

7

I would like to update specific parts of a Grid dynamically in different ways. Consider the following toy example: I have two rows: one must be updated one-by-one (a, b, c), as these symbols depend on different triggers; the second row depends on one single trigger (show) that allows displaying/hiding some data.

Now I know that I can wrap the whole Grid structure into Dynamic, and even specify which symbols to track, thus this example does what I want:

Checkbox[Dynamic[show]]
test = {0, 0};
Dynamic[Grid[{{Dynamic@a, Dynamic@b, Dynamic@c}, 
   If[show, Prepend[test, "test:"], {}]}, Frame -> All],
 TrackedSymbols :> {show}]

Mathematica graphics

Though for certain reasons I would like to have a locally specified Dynamic, that is only applied to the second row of the Grid.

For those who are wondering what ungodly situation would it be, just imagine the followings: show is used in any of a, b or c, and these I do NOT want to update when show is changing, their changes depend on other triggers. Why not remove then show from the symbols of the first row? Imagine, I can't, as show is present in a function that is used in a, b or c, and this function I cannot access easily.

Of course wrapping the first argument of If into Dynamic won't help here, as the Grid itself or any of its cells won't become dynamic:

Grid[{
  {Dynamic@a, Dynamic@b, Dynamic@c},
  If[Dynamic@show, Prepend[test, "test:"], {}]
  }, Frame -> All]

Furthermore, wrapping a row into Dynamic makes the given row invalid, as it does not have head List anymore:

Grid[{
  {Dynamic@a, Dynamic@b, Dynamic@c},
  Dynamic@If[show, Prepend[test, "test:"], {}]
  }, Frame -> All]

Mapping Dynamic over the row does not work either because show is not updated dynamically:

Grid[{
  {Dynamic@a, Dynamic@b, Dynamic@c},
  Dynamic /@ If[show, Prepend[test, "test:"], {}]
  }, Frame -> All]

Also, wrapping Dynamic[If[...]] around list members work, but now I have to evaluate If 3 times instead of just 1.

Grid[{
  {Dynamic@a, Dynamic@b, Dynamic@c},
  Dynamic[If[show, #, ""]] & /@ Prepend[test, "test:"]
  }, Frame -> All]

Would like to know if there is any solution to overcome this particular problem by locally applying a Dynamic wrapper on a row.

Unlay answered 10/1, 2012 at 16:11 Comment(5)
So you need a solution where 1. there's a Grid with two rows 2. both rows depend on show 3. but only the second row must be updated when show changes. Did I get it right?Helot
Can you explain why is three evaluations of the If undesirable? Is the test in If expensive? We might come up with something that evaluates the test a single time and stores the result into a variable which then controls the three Ifs. I believe if you wrap the whole thing then you can't avoid updating of a,b,c, but I'm not sure!Helot
Yes, you are right about the requirements, and no, of course the test in If is not too expensive, and at the moment the code runs like that (mapping If). Even more problematic though is another thing, which I am trying to pinpoint just right now.Sherysherye
is there a cut-and-paste error in your first example? It does not give the output of your last example.Nino
kguler: You're right, I forgot to update the code when copypasted. Thanks, see edit.Sherysherye
S
3

Here is a solution using the Experimental ValueFunction

show = True;
test = {0, 0};
Checkbox[Dynamic[show]]

Now write your own little Dynamic update function on the side

Needs["Experimental`"];
row = {};
updateRow[x_, v_] := row = If[v, Prepend[test, "test:"], {}];
ValueFunction[show] = updateRow;

Now make the Grid, and now can use Dynamic on EACH row, not around the whole Grid, which is what you wanted:

Grid[{
  {Dynamic@a, Dynamic@b, Dynamic@c},
  {Dynamic@row}
  },
 Frame -> All
 ]

enter image description here

ps. I just read a post here by telefunkenvf14 that mentions this package and this function, which I did not know about, and when I saw this function, I remembered this question, and I thought it should be possible to use that function to solve this problem.

ps. I need to work more on placing the grid row correctly....

update(1)

I can't figure how to splice the final row over the columns in the grid. Which is strange, as it has List head, yet it won't go across all the columns. It will only go in the first cell. Tried Sequence, SpanFromLeft, and such, but no luck. May be someone can figure this part out.

Here is my current trial:

Needs["Experimental`"];
row = {};
updateRow[x_, v_] := row = If[v, {"test:", 0, 0}, {}];
ValueFunction[show] = updateRow;
show = False;
Checkbox[Dynamic[show]]

f = Grid[{
   {Dynamic@a, Dynamic@b, Dynamic@c},
   List@Dynamic[row]
   },
  Frame -> All
  ]

It seems it should be doable. I do not see what is the problem now...

update(2)

As a temporary solution, I split the second row by force before hand. This made it possible to do what I want. Not sure if this meets the OP specifications or not (my guess is that it does not), but here it is:

Needs["Experimental`"];
ra = 0;
rb = 0;
rc = 0;
updateRow[x_, v_] := 
 row = If[v, ra = "test:"; rb = 0; rc = 0, ra = ""; rb = ""; rc = ""]
ValueFunction[show] = updateRow;
show = False;
Checkbox[Dynamic[show]]

f = Grid[{
   {Dynamic@a, Dynamic@b, Dynamic@c},
   {Dynamic@ra, Dynamic@rb, Dynamic@rc}
   },
  Frame -> All]

enter image description here

Safire answered 11/1, 2012 at 2:34 Comment(2)
Good to see the hours I've wasted trolling the dark spaces of the documentation have been put to good use. Thanks for the mention. :DPlasterboard
Nasser: Not exactly what I was looking for, but this nicely adds to all the approaches I've tried so far. The problem is basically to update a single row in a Grid such that the contents of the row are not split, seperated, looked into, dissected, etc.Sherysherye
N
1

This is actually a comment on @Nasser's solution and suggested fix to avoid manual splitting of the second row, but because of space limitations in the comment area, I post it as answer. Will be happy to delete it as soon as Nasser confirms that it works and incorporates it into his answer.

The clue to a solution is found in the Possible Issues section of Item in the documentation:

If Item is not the top-most item in the child of a function that supports Item, it will not work.

I use this to modify @Nasser's solution in the following way. First, I need to change the definition of row so that for both values of show the length of row is the same.

Needs["Experimental`"];
row = {"", "", ""}; 
updateRow[x_, v_] := row = If[v, Prepend[test, "test:"], {"", "", ""}];
Experimental`ValueFunction[show] = updateRow;

The second change needed is to wrap each element of Dynamic@row with Item:

Grid[{{Dynamic@a, Dynamic@b, Dynamic@c},
{Item[Dynamic@row[[1]]], Item[Dynamic@row[[2]]], 
 Item[Dynamic@row[[3]]]}}, Frame -> All]

Edit: Item wrapper is not really needed; it works just as well without it:

 Grid[{{Dynamic@a, Dynamic@b, Dynamic@c},
 {Dynamic@row[[1]], Dynamic@row[[2]], 
 Dynamic@row[[3]]}}, Frame -> All]
Nino answered 11/1, 2012 at 10:29 Comment(2)
Well, I would say that the second row is still split manually. I think I am convinced now that there is no simple and clear solution to this problem other than wrapping Dynamic around the whole Grid structure - or split the Grid into two Grids and update the second one only.Sherysherye
@Istvan, I agree. IMHO your first and last solutions the cleanest.Nino

© 2022 - 2024 — McMap. All rights reserved.