1
$\begingroup$

This is an outgrowth of an earlier question.

I have an extensive user interface that has InputFields that I want to hold their specific structure as input by the user (e.g., keeping scientific notation if they enter the numbers that way). And since the code will be pretty much the same throughout for each of these (many) InputFields, I want a function to do it for me. I have what seems to be a great way to do this:

SetAttributes[HoldInputField, HoldFirst];
HoldInputField[var_, OptionsPattern[FieldSize -> 10]] := 
  DynamicModule[{boxes = ToBoxes[var]}, 
    InputField[Dynamic[boxes, 
      Function[result, boxes = result; var = ToExpression[boxes]]], 
      Boxes, FieldSize -> OptionValue[FieldSize]]]

We can test it like this:

test = 3;
Row[{HoldInputField[test], " = ", Dynamic[test]}]

enter image description here

Unfortunately, if the Row statement is itself within a Dynamic wrapper, the behavior fails:

enter image description here

Why do I need to do this? Because the number of fields itself is dynamic. For example:

numFields = 3;
fieldvalue[index_] = 0;
Column[{Row[{"Number of fields: ",SetterBar[Dynamic[numFields],Range[5]]}], 
        Panel[Dynamic[Column[
          Row[{"Field ", #, ": ", HoldInputField[fieldvalue[#]], 
              " = ", fieldvalue[#]}] & /@ Range[numFields]]]]}]

enter image description here

Are there any ways to fix this problem? Thanks!

$\endgroup$
  • $\begingroup$ If you need to keep information about typesetting you should not do ToExpression until you really need to. Otherwise you lose it and next time the field is called with a fraction value that contains no information about formatting. Shortly, your input fields should be a controller for a an array of boxes. You can use them however you need later. $\endgroup$ – Kuba Jul 25 '19 at 21:56
  • $\begingroup$ try MakeBoxes[var] in place of ToBoxes[var]? $\endgroup$ – kglr Jul 25 '19 at 22:05
  • $\begingroup$ @kglr: Doesn't seem to work. The early examples start with the variable name in the input field rather than the initial value. And the multi-field example has the value continually reverting to the variable names. $\endgroup$ – Kevin Ausman Jul 25 '19 at 22:11
  • $\begingroup$ @Kuba: That's hard to envision for this application, because I am using those values to update graphs in real-time. $\endgroup$ – Kevin Ausman Jul 25 '19 at 22:17
  • $\begingroup$ I think I see the issue. When numFields updates, MMA reexecutes Column in its entirety, which means that it destroys and recreates the HoldInputField instantiations, which reinitializes the boxes variables. So I need to either create the fields once at the beginning and then only have the Dynamic functionality change the DISPLAY of these pre-existing fields (and I didn't want to have a fixed max, so maybe create on the fly but check for existence before recreating?), or I need to store the boxes information and pass it to the function. The last option seems the easiest to me. $\endgroup$ – Kevin Ausman Jul 25 '19 at 22:21
2
$\begingroup$

try

ClearAll[numfields, fieldvalue]

DynamicModule[{numfields = 3}, 
  Column[{Row[{"Number of fields: ", SetterBar[Dynamic[numFields], Range[5]]}], 
   Dynamic @ Panel[Column[Row[{"Field ", #, ": ", 
          InputField[Dynamic@fieldvalue[#], Boxes], " = ", 
          Dynamic@ToExpression@fieldvalue[#]}] & /@ Range[numFields]]]}], 
   Initialization -> {fieldvalue[_] = 0}]

enter image description here

$\endgroup$
  • $\begingroup$ Hmmm. So basically this is an example of leaving the input as boxes, and only interpreting it as a number when needed. Given how many places I use the numbers, that is slightly less than ideal (it will expand already-large code and cause lots of re-evaluations of ToExpression that aren't needed). But I definitely grant you that this works. Thank you! $\endgroup$ – Kevin Ausman Jul 25 '19 at 22:44
  • $\begingroup$ @Kevin, thank you for the vote. $\endgroup$ – kglr Jul 25 '19 at 23:00
1
$\begingroup$

Ok, I have a workaround, based on my comment above. I maintain the value of the boxes variable between instantiations. I wish there were a more elegant solution, and will be happy to entertain such if anyone has any ideas. Regardless, here is the test code that works for me:

SetAttributes[HoldInputField, HoldAll];
  HoldInputField[var_, boxes_, OptionsPattern[FieldSize -> 10]] := 
   DynamicModule[{}, If[boxes == Null, boxes = ToBoxes[var]]; 
    InputField[
     Dynamic[boxes, 
      Function[result, boxes = result; var = ToExpression[boxes]]], 
     Boxes, FieldSize -> OptionValue[FieldSize]]]

And testing:

DynamicModule[{b, numFields, fieldvalue}, numFields = 3;
 fieldvalue[index_] = 0;
 b[index_] = Null;
 Column[{Row[{"Number of fields: ", 
     SetterBar[Dynamic[numFields], Range[5]]}], 
   Panel[Dynamic[
     Column[Row[{"Field ", #, ": ", 
          HoldInputField[fieldvalue[#], b[#]], " = ", 
          fieldvalue[#]}] & /@ Range[numFields]]]]}]]

enter image description here

$\endgroup$

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.