22
$\begingroup$

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.

$\endgroup$

3 Answers 3

26
$\begingroup$

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]]
 ]
$\endgroup$
7
  • $\begingroup$ Maybe you can post that solution involving cell edition you've mentioned on chat? I will certainly upvote it. $\endgroup$
    – Kuba
    Jul 19, 2013 at 19:03
  • $\begingroup$ I do find it useful! I thought you will only post information which box to edit in the cell, I'm impressed. Unfortunately I can't upvote twice. $\endgroup$
    – Kuba
    Jul 20, 2013 at 17:35
  • $\begingroup$ Thanks! I figure coding it could be worked out, only I wasn't motivated to do it until you asked. I'm glad you find it useful. $\endgroup$
    – Michael E2
    Jul 20, 2013 at 17:37
  • $\begingroup$ The Michael as I know him ;) +1 $\endgroup$
    – Stefan
    Jul 20, 2013 at 23:17
  • 4
    $\begingroup$ Amazing that you went so deep into undocumented functions! $\endgroup$
    – Ted Ersek
    Jul 21, 2013 at 0:49
8
$\begingroup$

with manipulate

Manipulate[
           Framed@y,

           Column@{Slider[Dynamic[Log[10, y], (y = 10^#) &], {1, 3}], 
                   InputField[Dynamic@y]}
          ]

single slider

y=10;    
{
 Slider[Dynamic[Log[10, y], (y = 10^#) &], {1, 3}], 
 Dynamic@y
}

Of course, here base is 10 but if it is what You are looking for, I can extend this.

$\endgroup$
8
  • $\begingroup$ You need to read more carefully: "When the variable value is changed separately, the slider position should update too." $\endgroup$
    – halirutan
    Jul 12, 2013 at 13:16
  • 1
    $\begingroup$ @halirutan Have You tried that? Works for me. Try to set y=123 for example after some sliding done. $\endgroup$
    – Kuba
    Jul 12, 2013 at 13:16
  • 1
    $\begingroup$ Indeed, I haven't tried. I just looked shortly over your code and saw that you haven't used any text input as asked by Szabolcs. Therefore, and for the simplicity of the approach (which I'm sure Szabolcs would have found by himself) I assumed you missed that part. I'm sorry, I didn't mean to be rude. I was in a hurry and just wanted to tell you, that you missed some parts of the question. $\endgroup$
    – halirutan
    Jul 12, 2013 at 15:58
  • $\begingroup$ I'm still not sure whether this is all Szabolcs wanted. Lets wait for a comment of him and if it really is, you can count on my upvote for your answer. $\endgroup$
    – halirutan
    Jul 12, 2013 at 16:00
  • $\begingroup$ @halirutan ah, indeed, You're right I have missed "text" part ;) Please take a look at update. $\endgroup$
    – Kuba
    Jul 12, 2013 at 17:06
4
$\begingroup$

Here there are two variables x and y that are coupled together by the If statements. So you can change the slider x and it changes the y value or you can change the input box y and it also changes the x. The log character of the slider and box are given by the z=10^x function, which means that the slider value is the log of z.

xOld = 0; yOld = 0;
Manipulate[If[10^xOld != 10^x, y = x; xOld = x];
           If[10^yOld != 10^y, x = y; yOld = y]; z = 10^x, {x, 1, 10}, {y}]

enter image description here

This basically extends Michael Pilat's answer to the linked question to the case of two controls (the slider and the input box).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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