1
$\begingroup$

I want to make a program that allows to vary multiple parameters via multiple adjusters. It may well be that this question has been answered already somewhere. However, I am pretty new to programming in Mathematica and many times the answers to questions here are just completely overwhelming my rudimentary skills.

As said, I would like different adjusters, namely: A slider, an input field and grabbing objects in a plot environment. In addition I want to constrain the grabbing bit to be only in a horizontal motion. Basically I want to extend the Psst!-program from the University of St. Andrews.

I can do just the grabbing bit with

LocatorPane[{Dynamic[pt]}, Plot[Sin[x], {x, 0, 10}], {{0, 0}, {10, 0}}, 
 Appearance -> Graphics[Line[{{0, 1}, {0, -1}}]]]

Where I have restricted the LocatorPane to a line and substituted the adjuster appearance with a line (later this could be the image of say a lens in an optical system).

If I just wanted to have the Slider and InputField, I would just use the Manipulate environment and overlay with a line:

Manipulate[Plot[Sin[x], {x, 0, 2 Pi}, 
  Epilog -> {Line[{{0, -1}, {0, 1}}], Line[{{2 Pi, -1}, {2 Pi, 1}}], 
   Line[{{p, -1}, {p, 1}}]}], {p, 0, 2 Pi}]

Here there is no grabbing. I can combine the two (somewhat) with DynamicModule:

DynamicModule[{pt}, {Slider[Dynamic[pt], {0, 10}],  
  InputField[Dynamic[pt]], 
  LocatorPane[Dynamic[pt],Plot[Sin[x], {x, 0, 10}], {{0, 0}, {10, 0}}, 
   Appearance -> {Graphics[{Red, Line[{{0, 1}, {0, -1}}]}], 
  Graphics[{Black, Line[{{0, 1}, {0, -1}}]}]}]}]

The first issue pops up here. Everything is well behaved when I use the slider, i.e. the input field displays a single number and the line moves to the correct spot. However, once I grab the line and move it, the input field displays a vector and the sliders resets to 0.

I want to extend this to 2 or more parameters (and lines), so I went with

DynamicModule[{pt = {{1, 0}, {6, 0}}}, {LocatorPane[Dynamic[pt],   
  Plot[Sin[x], {x, 0, 10}], {{0, 0}, {10, 0}},
  Appearance -> {Graphics[{Red, Line[{{0, 1}, {0, -1}}]}],
  Graphics[{Black, Line[{{0, 1}, {0, -1}}]}]}]}]

Which works fine. However, in addition to the above problems with the slider and input field when those are added, the second line will vanish once the slider is touched.

I have played around using Dynamic[First[pt]] for the vector problem to no avail. I see how it would be difficult to determine which parameter I want to vary when I define only a single dynamic variable like this,

{pt = {{1, 0}, {6, 0}}}

so I went for the more intuitive version of

DynamicModule[{pt1 = {1, 0}, pt2 = {6, 0}}, 
 {Slider[Dynamic[pt1], {0, 10}], InputField[Dynamic[pt1]], 
  LocatorPane[Dynamic[{pt1, pt2}], Plot[Sin[x], {x, 0, 10}], {{0, 0}, {10, 0}},
   Appearance -> {Graphics[{Red, Line[{{0, 1}, {0, -1}}]}], 
  Graphics[{Black, Line[{{0, 1}, {0, -1}}]}]}]}]

But this does not change anything at all.

I would be greatful if people could explain where things went wrong.


Based on the answers by @Algohi and @Karsten 7. I have produced the two following working examples based on Karsten's answer

line[a_] := Graphics[{a, Line[{{0, 1}, {0, -1}}]}]
DynamicModule[{p = {1, 0}, px = 1, q = {2, 0}, qx = 2, r = {3, 0}, rx = 3},
Column[{
Row[{"a", Slider[Dynamic[px, (px = #; p[[1]] = px; &)], {0, 10},Appearance -> "Labeled"],
InputField[Dynamic[px, (px = #; p[[1]] = px; &)]]}],
Row[{"b", Slider[Dynamic[qx, (qx = #; q[[1]] = qx; &)], {0, 10}, 
  Appearance -> "Labeled"],
InputField[Dynamic[qx, (qx = #; q[[1]] = qx; &)]]}],
Row[{"c", Slider[Dynamic[rx, (rx = #; r[[1]] = rx; &)], {0, 10},Appearance -> "Labeled"],
InputField[Dynamic[rx, (rx = #; r[[1]] = rx; &)]]}],
Row[{LocatorPane[Dynamic[{p, q, r}, (
p = {First@#[[1]], 0}; px = First@p;
q = {First@#[[2]], 0}; qx = First@q;
r = {First@#[[3]], 0}; rx = First@r; &)]
, Plot[Sin[x], {x, 0, 10}, ImageSize -> 300], {{0, 0}, {10, 0}}, Appearance -> {line[Black], line[Blue], line[Red]}]
, {"a=" Dynamic@p[[1]], "b=" Dynamic@q[[1]], "c=" Dynamic@r[[1]]}}]}]]

and

DynamicModule[{pt1 = {1, 0}, pt2 = {6, 0}, pt3 = {3, 0}},
Column[{
Row[{"a", Slider[Dynamic[pt1[[1]]], {0, 10}],InputField[Dynamic[pt1[[1]]]]}],
Row[{"b", Slider[Dynamic[pt2[[1]]], {0, 10}],InputField[Dynamic[pt2[[1]]]]}],
Row[{"c", Slider[Dynamic[pt3[[1]]], {0, 10}],InputField[Dynamic[pt3[[1]]]]}], 
Row[{LocatorPane[Dynamic[{pt1, pt2, pt3}], 
Plot[Sin[x], {x, 0, 10}, ImageSize -> 300], {{0, 0}, {10, 0}}, Appearance -> {line[Black], line[Blue], line[Red]}],
{"a=" Dynamic@pt1[[1]], "b=" Dynamic@pt2[[1]], "c=" Dynamic@pt3[[1]]}}]}]]

based on Algohi's answer.

$\endgroup$
5
  • $\begingroup$ Probably the easy way to fix your code without much work is to change Input field into InputField[Dynamic[If[Length[pt] > 1, pt[[1]], pt]]] $\endgroup$ Oct 5, 2015 at 1:56
  • $\begingroup$ Thanks, that does fix the issue if you use a single parameter quite easily. However, the second one still vanishes. $\endgroup$
    – Christian
    Oct 5, 2015 at 3:00
  • 1
    $\begingroup$ @Christain, when you touch the slider the point pt1 changes from vector {a,b} into scalar a. This means the LocatorPane will have only one point which means one locator. A simple fix to this can be by using Slider[Dynamic[pt1[[1]]], {0, 10}] $\endgroup$ Oct 5, 2015 at 3:14
  • $\begingroup$ @Algohi Hum, I thought First[pt] would be identical to pt[[1]], but while the former (which I tried) returns: Set::write: "Tag First in First[{6,0}] is Protected." the second does exactly what I thought it should. Very strange. Sadly, it seems I cannot upvote your contribution which fixes my problem. $\endgroup$
    – Christian
    Oct 5, 2015 at 3:33
  • $\begingroup$ Actually, I just noted that with the InputField[Dynamic[If[Length[pt] > 1, pt[[1]], pt]]] when I try using the input field, it says Set::write: "Tag If in If[False,FEpt1$$21[[1]],FEpt1$$21] is Protected." $\endgroup$
    – Christian
    Oct 6, 2015 at 4:26

1 Answer 1

1
$\begingroup$

Using TrackingFunction or the second argument of Dynamic

An adaption of this answer to your problem:

line = Graphics[{Red, Line[{{0, 1}, {0, -1}}]}, PlotRange -> 1];

Manipulate[
 Plot[Sin[x], {x, 0, 10}], 
 {{p, {1, 1}}, {0, 0}, {10, 0}, Locator, Appearance -> line, 
  TrackingFunction -> (p = {First@#, 0}; px = First@p; &)}, 
 {{px, 1}, 0, 10, Slider, Appearance -> "Labeled", 
  TrackingFunction -> (px = #; p[[1]] = px; &)}, 
 {{px, 1}, InputField, ControlPlacement -> Bottom, 
  TrackingFunction -> (px = #; p[[1]] = px; &)}]

ManipulateOut

Using DynamicModule and LocatorPane:

DynamicModule[{p = {1, 0}, px = 1}, 
 Panel@Grid[{
  {Slider[Dynamic[px, (px = #; p[[1]] = px; &)], {0, 10}, Appearance -> "Labeled"]}, 
  {InputField[Dynamic[px, (px = #; p[[1]] = px; &)]]},
  {LocatorPane[Dynamic[p, (p = {First@#, 0}; px = First@p; &)], 
    Plot[Sin[x], {x, 0, 10}, ImageSize -> 300], {{0, 0}, {10, 0}}, 
    Appearance -> {Graphics[{Red, Line[{{0, 1}, {0, -1}}]}]}],
   Dynamic@p}}]]

DynamicModuleOut

Short explanation:
p is the point of the Locator. Whenever the Locator is changed p gets set to p = {First@#, 0}&. This pure function is applied to the position of the Locator, whenever it gets moved and therefore restricts its movement to a horizontal movement. px is the horizontal position of the Locator. Whenever it is changed the x-coordinate of p (p[[1]]) gets updated by p[[1]] = px. The value of px gets updated to px = First@p, whenever the Locator is moved.

Using Dynamic[p[[1]]] instead of Dynamic[First[pt]]

A little simpler DynamicModule (as also suggested in a comment by Algohi, mainly using Dynamic[p[[1]]] instead of the not working Dynamic[First[pt]] you tried):

DynamicModule[{p = {1, 0}}, 
 Panel@Grid[{
  {Slider[Dynamic[p[[1]]], {0, 10}, Appearance -> "Labeled"]}, 
  {InputField[Dynamic[p[[1]]]]}, 
  {LocatorPane[Dynamic[p], 
    Plot[Sin[x], {x, 0, 10}, ImageSize -> 300], {{0, 0}, {10, 0}}, 
    Appearance -> {Graphics[{Red, Line[{{0, 1}, {0, -1}}]}]}], 
   Dynamic@p}}]]

Why does using Dynamic[p[[1]]], but not Dynamic[First[pt]]?
When Dynamic[p[[1]]] is used, the first part of p gets Set dynamically. Whereas Dynamic[First[pt]] tries to dynamically change the DownValues of First. This is not allowed, as First has the Attributes {Protected}.
illustrativeExample

$\endgroup$
4
  • $\begingroup$ Hey Karsten, that certainly works. However, since I don't understand what's going on, I sadly cannot extend this to multiple parameters (or learn from it). Could you possibly explain what's done there? Also, the locators are not restricted in their vertical motion. $\endgroup$
    – Christian
    Oct 5, 2015 at 1:34
  • $\begingroup$ @Christian The Locators should be restricted to horizontal motion only now. $\endgroup$
    – Karsten 7.
    Oct 5, 2015 at 1:53
  • $\begingroup$ Check the Dynamic documentation (including the Details and Options sections), especially regarding the second argument of Dynamic, for detailed explanations of what is going on. TrackingFunction is the same as the second argument to Dynamic, but within a Manipulate. $\endgroup$
    – Karsten 7.
    Oct 5, 2015 at 2:00
  • $\begingroup$ I understand your answer now and have been able to extend to 3 parameters without problems. Thanks for your effort! $\endgroup$
    – Christian
    Oct 5, 2015 at 3:57

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.