Skip to main content

dynamic - Logarithmic slider


Is it possible to create a logarithmic slider similar to this one that responds to a change of the variable value?


That is, when the slider is moved, the variable value should update. When the variable value is changed separately, the slider position should update too.


The ultimate aim is to use this in Manipulate and have both a text input and a logarithmic slider input for the same parameter.





The post I linked to does not address changing the slider position when the variable is changed elsewhere.



Answer



A reliable composition of elements


Perhaps something like this? (Edit: Fixed to work with Autorun.) Note that the InputField label is editable, similar to a normal Manipulator. One can also add an additional InputField[Dynamic @ x] if a regular InputField is desired.


Manipulate[
x,
{{x, 1.}, 1., 100.,
Row[{Slider[Dynamic[Log10[#], (x = 10^#) &], Log10[#2]], " ",
InputField[#, Appearance -> "Frameless", BaseStyle -> "Label"]}] &}
]


Autorun of Manipulate


It's not a Manipulator, so no animator/input field. That's harder, since they (and the label) are built into the front-end implementation of a Manipulator. A Trigger and InputField could be added to simulate a Manipulator, I suppose.




A proper hack


All right, Kuba, you asked for it. :)


This is based on some spelunking of undocumented functions. The section titles reflect my feeling that the first is the best and a very good way to go. (These UI/Manipulate questions never seem to generate much interest in this SE community. This one wasn't particularly hard, but it did take some time to go through the details. I hope someone will find it useful, which is much more rewarding to me that "upvotes." In fact, I hope the first one is even more useful.)


The code is long, mainly because I worked out how the options to Manipulator are passed to the internal function. I put it at the end. I wrote a function logManipulator that works (almost exactly) like Manipulator (one unimportant thing was left undone).


{logManipulator[Dynamic[x], {1., 100.}], InputField[Dynamic@x]}


Mathematica graphics


The OP mentioned using it in a Manipulate with an input field. My original answer put the editable field as a label, just as a Manipulator does. However if a separate InputField is desired, that it as easy as adding a line to Manipulate for it.


To use logManipulator in Manipulate, one needs to pass a pure Function as with any custom control. Note: the animation below was produce with Export via Autorun, which interpolates x linearly between 10.^-5 and 10.; the animator, however, when run, interpolates linearly between their logarithms, and the slider moves with constant speed (more or less).


Manipulate[
Plot[t Sin[1/t], {t, -x, x}, PlotRange -> x, ImagePadding -> 10],
{x, 10.^-5, 10.,
logManipulator[##, Appearance -> {"Labeled", "Open"}, AnimationDirection -> Backward] &},
{{x, 1.}, Number, InputField},
AutorunSequencing -> {1}
]


Autorun of Manipulate


One can enter a value for x in the InputField (note the position of the slider):


Mathematica graphics


Code dump


The elements and options of Manipulator are nearly each passed as separate arguments to FEPrivate`FrontEndResource["FEExpressions", "Manipulator04"][..]. Only the animator elements in AppearanceElements -> {..} are passed together as a list. Some of the options are passed in other places. Since the Manipulator is wrapped in a DynamicBox, I used With to inject the values. I've given the arguments names that correspond more or less to the names of the elements or options. I hope that is enough of a hint as to how it works. The basis for the code was the output cell of a simple Manipulator[Dynamic[x]] (which can be inspected with the menu command "Cell > Show Expression").


ClearAll[logManipulator];
With[{smallerRule = {Large -> Medium, Medium -> Small, Small -> Tiny}},
logManipulator[Dynamic[x_], range_: {1, 10},
OptionsPattern[Manipulator]] := With[{

logrange = Log10[range],
imagesize = OptionValue[ImageSize] /. Automatic -> Medium,
inputfieldsize =
OptionValue[ImageSize] /. Automatic -> Medium /. smallerRule,
enabled = OptionValue[Enabled],
continuousaction = OptionValue[ContinuousAction],
appearance =
First[Cases[OptionValue[Appearance],
Tiny | Small | Medium | Large] /. {} -> {Automatic}],
labeled = ! FreeQ[OptionValue[Appearance], "Labeled"] || !

FreeQ[OptionValue[AppearanceElements], "InlineInputField"],
opener =
OptionValue[AppearanceElements] /. {Automatic -> True,
All -> True, None -> False,
l_List :> (Cases[l, Except["InlineInputField"]] =!= {})},
inputfield =
MatchQ[OptionValue[AppearanceElements], Automatic | All] ||
! FreeQ[OptionValue[AppearanceElements], "InputField"],
appearanceelements =
OptionValue[AppearanceElements] /. {Automatic -> All, None -> {},

l_List :> Cases[l, Except["InlineInputField" | "InputField"]]},
autoaction = OptionValue[AutoAction],
exclusions = OptionValue[Exclusions]},
ReleaseHold@MakeExpression[
PaneBox[
DynamicModuleBox[{
Typeset`open$$ = ! FreeQ[OptionValue[Appearance], "Open"],
Typeset`paused$$ = OptionValue[PausedTime],
Typeset`rate$$ = OptionValue[AnimationRate],
Typeset`dir$$ = OptionValue[AnimationDirection]},

StyleBox[
DynamicBox[
FEPrivate`FrontEndResource["FEExpressions", "Manipulator04"][
Dynamic[x],
Dynamic[Log10[x], (x = 10^#) & ],
logrange,
imagesize,
inputfieldsize,
enabled,
continuousaction,

appearance,
labeled,
opener,
inputfield,
appearanceelements ,
autoaction,
exclusions,
Dynamic[Typeset`open$$],
Dynamic[Typeset`paused$$],
Dynamic[Typeset`rate$$],

Dynamic[Typeset`dir$$]]],
DynamicUpdating -> True],
DynamicModuleValues :> {}],
BaselinePosition -> (OptionValue[BaselinePosition] /. Automatic -> Baseline),
ImageMargins -> OptionValue[ImageMargins]],
StandardForm]]
]

Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

Is there a way to do conditional matrix loop using 'continue'

I have the following: n = 3; m = 5; ww = RandomReal[{0, 0.1}, {n, n}]; uu = RandomReal[{0, 1}, {m, n}]; pp = RandomReal[{0, 1}, {n, n}]; ss = RandomInteger[{0, 5}, {m, n}]; Grid[{{"ww", "uu", "pp", "ss"}, {ww // TableForm, uu // TableForm, pp // TableForm, ss // TableForm}}, Spacings -> {5, 2}, Dividers -> All] where I would like to look at every element of matrix ss and produce a matrix tt , with zeroes at the locations in ss which have zeroes, and in all other positions do the following: tt = (-1/Subscript[ww, m]) Log[(1 - uu)/(Subscript[pp, m - 1])], where Subscript[ww, m] is the value at index of ww matrix and where Subscript[pp, m - 1] is the value at index-1 of pp matrix. So for example if the first value ever read from matrix ss happens to be 2, then value taken from matrix ww would be from the row 2, but from pp would be from row 1. Also how to tell difference between a 0 as a valid value from within the matrix elemen...