I've got a grid of buttons that each display an image from an array. When a button is clicked, its image changes.

DynamicModule[{g = Table[Image[RandomReal[{0.5, 1}, {16, 16, 3}]], {19}, {19}]},
  Grid[Table[With[{i = i, j = j},
    Button[Dynamic[g[[i, j]]], g[[i, j]] = Image[RandomReal[1, {16, 16}]],
      ImageSize -> Full, Appearance -> None]],
    {i, 19}, {j, 19}], Spacings -> {0, 0}]]

The problem is that it is very slow. I presume this is because all the buttons get updated when only one is changed. This raises the general question as to how can you dynamically change only the control corresponding to the changed element in the list?

I've already come up with a workaround for my particular problem:

DynamicModule[{g = Table[Image[RandomReal[{0.5, 1}, {16, 16, 3}]], {19}, {19}]},
    (g[[19 - Floor[#[[2]]/16], Floor[#[[1]]/16] + 1]] =
      Image[RandomReal[1, {16, 16}]])&],
    ImageSize -> Full]]

This splices together a single image, works out where you clicked on it, then changes the appropriate element in the list. It doesn't answer the original question, but it's lots faster.

Additional note:

I'd quite like g to be available outside the grid. Then you could have multiple grids doing different things. Or some other view of the data.


Studying various parts of the your code I found that having an array hold your images seems to be the main cause of delay for some reason not directly clear to me. Using normal variables (which can be done easily by putting the DynamicModule deeper in the hierarchy) the process gets much quicker:

   {g = Image[RandomReal[{0.5, 1}, {16, 16, 3}]]},
    g = Image[RandomReal[1, {16, 16}]],
    ImageSize -> Full, Appearance -> None
   ], {i, 19}, {j, 19}
  ], Spacings -> {0, 0}

Mathematica graphics

You can still see the ugly white lines that are also in your original code (but not in your alternative). Have to find out how to remove these...

  • $\begingroup$ Interesting. This seems to be speed-equivalent to my code, but much simpler. +1. $\endgroup$ – Leonid Shifrin Mar 13 '12 at 22:09
  • $\begingroup$ Actually, my version seems a little faster, because I use kernel variables, which for some reason tend to be tracked faster. But, OTOH, I pollute the Global` context. $\endgroup$ – Leonid Shifrin Mar 13 '12 at 22:34
  • $\begingroup$ @LeonidShifrin I was just about to say something about that. There are literally hundreds of $ variables after executing your code. About speed difference: I could not notice it, but it may be true. $\endgroup$ – Sjoerd C. de Vries Mar 13 '12 at 22:37
  • $\begingroup$ Those variables will be garbage-collected immediately after the image gets deleted - but I agree that their presence is not welcome. $\endgroup$ – Leonid Shifrin Mar 13 '12 at 22:41
  • 1
    $\begingroup$ @Mr.Wizard {0, -0.05}. That's why I don't like tweaking the output that way. $\endgroup$ – Sjoerd C. de Vries Mar 14 '12 at 10:37

This seems to be a bit faster, still not as fast as Sjoerd's method, that cleverly uses a different variable for each cell. The solution below keeps the original 19 x 19 table g. I also removed the unwanted horizontal lines (by negative spacing) and used EventHandler to get rid of the button-effect.

DynamicModule[{g = 
   Table[Image[RandomReal[{0.5, 1}, {16, 16, 3}]], {19}, {19}], ref},
    EventHandler[#1, {"MouseClicked" :> (g[[Sequence @@ #2]] = 
          Image[RandomReal[1, {16, 16}]])}] &, g, {2}],
   Spacings -> {0, -.05}], TrackedSymbols :> {g}]

Mathematica graphics


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.