4
$\begingroup$

I would like to add a Locator to a figure displayed by Manipulate when I click inside the figure, at the location where I click, and to have this trigger an update of the figure. I would also like to have clicking on an existing Locator remove it, and dragging a locator reposition it (again, redrawing the figure in response in each case).

For (a silly) example, I would like clicking anywhere in the region displayed by

With[{locs = {}}, 
 Manipulate[Graphics[Text[locs, {0, 0}], PlotRange -> 2],
  {{q, {1, 1}}, Locator, 
   Appearance -> Graphics[{Green, Disk[{0, 0}, .1]}, ImageSize -> 10]},
  {{p, locs}, Locator, 
   Appearance -> Graphics[{Red, Disk[{0, 0}, .1]}, ImageSize -> 10]}]]

to add the corresponding Point to locs, and for clicking on any p Locator to remove it from locs, with the text, updating accordingly in response to additions, deletions or repositionings of a Locator.

To make things hard, I have another locator q that needs to coexist with the one to which I would like to add/remove; and p and q have different appearances.

$\endgroup$
6
  • $\begingroup$ Check the docs for LocatorAutoCreate. $\endgroup$
    – kglr
    Apr 9, 2014 at 22:17
  • $\begingroup$ @kguler: Good suggestion, but I actually have another locator in the Manipulate that seems to "get in the way": nothing happens when I click, even with LocatorAutoCreate -> All. $\endgroup$
    – orome
    Apr 9, 2014 at 22:48
  • 1
    $\begingroup$ Seen this Q/A? $\endgroup$
    – kglr
    Apr 9, 2014 at 23:02
  • $\begingroup$ @kguler: I've asked to have this reopened. The linked question does not, in fact, answer this one: how do I add (remove) Locators by clicking on the location where I want the locator (of the locator I want to remove)? I should have thought that (and not adding at random locations, or removing the last-added Locator) was implicit. $\endgroup$
    – orome
    Apr 10, 2014 at 13:32
  • 1
    $\begingroup$ voted to reopen. $\endgroup$
    – kglr
    Apr 10, 2014 at 13:35

2 Answers 2

2
$\begingroup$

I would approach the overall goal of the program in a different way that avoids the limitations* of Manipulate. I would store a list of all the locators in one variable, with the permanent locator(s) at the beginning of the list. The transient locators can be added and removed with ALT+click. The permanent locators are maintained by the option of the form LocatorAutoCreate -> min, where min is the number of permanent locators.

Using With to assign symbols to represent the parts of the list locs of locators seems slightly inelegant, but overall the approach has the advantage of being simple.

Manipulate[
 With[{q = locs[[1]], p = Drop[locs, 1]}, 
  Graphics[Text[locs, {0, 0}], PlotRange -> 2]
  ],
 {{locs, {{1, 1}}}, Locator, LocatorAutoCreate -> 1}
 ]

*Limitation: When several Locator controls, p, q, ..., appear in a Manipulate, a LocatorPane is created of the form

LocatorPane[Dynamic[{p, q, ...}], ...]

The option LocatorAutoCreate can be passed to the LocatorPane; however, it does not work because the length of the list {p, q, ...} cannot be changed.


Update: Appearance

The appearance of the locators may be controlled in two ways. The documented, and hence common, solution is to use invisible locators and insert graphics primitives into the Graphics:

Manipulate[
 With[{q = locs[[1]], p = Drop[locs, 1]}, 
  Graphics[{
    Text[locs, {0, 0}],
    Locator[q], {Red, PointSize[Large], Point[p]},
   PlotRange -> 2]
  ],
 {{locs, {{1, 1}}}, Locator, LocatorAutoCreate -> 1}
 ]

Another way is to use the option setting

Appearance -> {Automatic, Graphics[{Red, Disk[]}, ImageSize -> 10]}

The last specification is repeated when there are more than two locators, a behavior that I could not find documented.

$\endgroup$
5
  • $\begingroup$ I've been balking at the complex solutions and had settled on exactly this as a possible approach. $\endgroup$
    – orome
    Apr 10, 2014 at 12:03
  • $\begingroup$ It looks like I'm stuck with a single Appearance with this approach. Is that correct? The first and rest need to look different. $\endgroup$
    – orome
    Apr 10, 2014 at 13:15
  • 1
    $\begingroup$ @raxacoricofallapatorius No, you can set Appearance -> None and use Locator[q], {Red, Point[p]} inside the graphics. You might be able to use a different setting for appearance, but I'm not where I can check at this time. $\endgroup$
    – Michael E2
    Apr 10, 2014 at 13:34
  • 1
    $\begingroup$ @raxacoricofallapatorius It also turns out that you can use another setting for Appearance. See update. :) $\endgroup$
    – Michael E2
    Apr 12, 2014 at 13:17
  • $\begingroup$ Excellent, thanks for the follow up. I tired that earlier and couldn't get it to work; I must have mistyped. $\endgroup$
    – orome
    Apr 12, 2014 at 14:32
3
$\begingroup$

As kguler points out, this question appears to have be treated previously here. However, jVincent's answer only treats the case of adding additional locators. Here is an example that where locators can be both added and deleted.

SetAttributes[canDelete, HoldFirst]
canDelete[locatorGroup_Symbol] :=
  Switch[locatorGroup,
    {}, False,
    {_}, Unevaluated@locatorGroup = {}; False,
    {__}, Unevaluated@locatorGroup = Most[locatorGroup]; True]

DynamicModule[{black = {}, red = {}, deleteQ},
  deleteQ["black"] = False;
  deleteQ["red"] = False; 
  Grid[{
    {Framed[
       Graphics[{
         Dynamic[
           With[{i = #}, Locator[Dynamic[black[[i]]], Style[\[FilledSquare], Black]]] &
             /@ Range@Length@black], 
         Dynamic[
           With[{i = #}, Locator[Dynamic[red[[i]]], Style[\[FilledCircle], Red]]] &
             /@ Range@Length@red]},
      PlotRange -> {{0, 1}, {0, 1}}]], 
    SpanFromLeft}, 
   {Button["Add Black", AppendTo[black, RandomReal[1, 2]]; deleteQ["black"] = True], 
    Button["Add Red", AppendTo[red, RandomReal[1, 2]]; deleteQ["red"] = True]},
   {Dynamic[Button["Delete Black", deleteQ["black"] = canDelete[black], 
              Enabled -> deleteQ["black"]]],
    Dynamic[Button["Delete Red", deleteQ["red"] = canDelete[red], 
              Enabled -> deleteQ["red"]]]}}]]

two-locator-groups

$\endgroup$
1
  • $\begingroup$ Note that this does not really answer the question, since the whole point of adding locators is that they appears where I click, not in some Random location (likewise for removing). I would have thought this was implicit. $\endgroup$
    – orome
    Apr 10, 2014 at 13:28

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.