3
$\begingroup$

I have created a list (listA) that specifies disks based on color, center point, and radius:

(* Define disks based on (i) color, (ii) center point, and (iii)
radius. listA is a list of such disks. *)
listA = {
   {Red, {0, 0}, 0.5},(* object 1 *)
   {Blue, {1, 1}, 0.75}(* object 2 *)
   };

I have also defined a function displayList to display the disks with Graphics:

(* Create a function to plot the disks using Graphics. *)
displayList[list_List] := Module[{},
   Graphics[{
     list[[1, 1]], Apply[Disk, list[[1, 2 ;; 3]]],
     list[[2, 1]], Apply[Disk, list[[2, 2 ;; 3]]]
     }
    , Frame -> True, PlotRange -> {{-2, 2}, {-2, 2}}]
   ];

Applying displayList to listA gives graphical output:

displayList[listA]

disks

I would like to make the disks "clickable" -- I would like the disks' color to (dynamically) change when the user clicks on the disks.

Specifically, I want the following:

  • When the user clicks on the red disk (i.e., the disk of radius 0.5 centered at {0, 0}), listA is updated so that listA[[1, 1]] changes from Red to Blue; and displayList[listA] is automatically reevaluated so that the disk is now displayed as blue.

  • When the user clicks on the blue disk (i.e., the disk of radius 0.75 centered at {1, 1}), listA is updated so that listA[[2, 1]] changes from Blue to Red; and displayList[listA] is automatically reevaluated so that the disk is now displayed as red.

How should I go about approaching this problem? Should I think about defining a Locator for each disk?

$\endgroup$
  • 1
    $\begingroup$ Take a look at ClickPane, Button, EventHandler and Dynamic in general. $\endgroup$ – Kuba Jan 13 at 21:57
4
$\begingroup$

There are lots of ways to do what you ask for. I decided that an implementation based on FlipView and Insert would be fun because I had not used that approach before. Also, I decided to use an association to hold the disk parameters because I believe doing so makes the code more readable.

diskData =
  {<|"cntr" -> {0, 0}, "r" -> .5, "hues" -> {Red , Blue}|>,
   <|"cntr" -> {1, 1}, "r" -> .75, "hues" -> {Blue, Red}|>};

flipper[disk_Association] :=
  Inset[
    FlipView[Graphics[{#, Disk[]}] & /@ disk["hues"]], 
    disk["cntr"], Automatic, 2 disk["r"]]

Graphics[flipper /@ diskData, Frame -> True, PlotRange -> 2]

graphics

Clicking on either the two disks will change that disk's color.

$\endgroup$
2
$\begingroup$

The problem with Graphics within Graphics is that the inner one won't respect things like AspectRatio or coordinate system in general.

Here is how I would do it, with Dynamic @ color nothing but color needs to be reevaluated. For more complicated cases it is crucial not to rerender the whole graphics again and again and not to transfer redundant data between kernel and frontend.

Graphics[
  Table[ 
    With[{i = i},
      EventHandler[
        { Dynamic @ listA[[i, 1]], Disk @@ listA[[i, 2 ;;]]}
      , "MouseClicked" :> (listA[[i, 1]] = listA[[i, 1]] /. {Red -> Blue, Blue -> Red})
      ]
    ]
  , {i, Length @ listA}
  ]
, Frame -> True
, PlotRange -> 2
, AspectRatio -> 1/2
]
$\endgroup$
1
$\begingroup$

I really like goldberg's idea of using Inset. Here is an extension which includes a readback of the current state:

diskData = {
   <|"cntr" -> {0, 0}, "r" -> .5, "hues" -> {Red, Blue}, 
    "currentValue" -> Unique[currentValue]|>,
   <|"cntr" -> {1, 1}, "r" -> .75, "hues" -> {Blue, Red}, 
    "currentValue" -> Unique[currentValue]|>};

flipper[disk_Association] := Inset[
   FlipView[
     Graphics[{#, Disk[]}] & /@ disk["hues"], 
     With[{currentval = disk["currentValue"]}, Dynamic[currentval]]
   ],
   disk["cntr"], Automatic, 2 disk["r"]];

Column@{
   Graphics[flipper /@ diskData, Frame -> True, PlotRange -> 2, ImageSize -> Medium],
   Dynamic@diskData[[All, "currentValue"]]
}

It uses a second argument to FlipView:

FlipView[list,Dynamic[i]] takes the displayed object to be specified by the dynamically updated current value of i, with the value of i being reset if a new object is displayed.

A unique symbol is created for each disk in diskData that will hold the current state of the disk in FlipView. With is used to inject the symbol into the Dynamic statement.

One possible issue is that new unique symbols are created for each evaluation of diskData, and that there is no garbage collection for symbols which are no longer used.

$\endgroup$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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