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.
InputField[Dynamic[If[Length[pt] > 1, pt[[1]], pt]]]
$\endgroup$LocatorPane
will have only one point which means one locator. A simple fix to this can be by usingSlider[Dynamic[pt1[[1]]], {0, 10}]
$\endgroup$First[pt]
would be identical topt[[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$InputField[Dynamic[If[Length[pt] > 1, pt[[1]], pt]]]
when I try using the input field, it saysSet::write: "Tag If in If[False,FE
pt1$$21[[1]],FEpt1$$21] is Protected."
$\endgroup$