1
$\begingroup$

I have made many functions that connect to a MySQL database perform operations then export exel spreadsheet. I also import spreadsheet data into the database. I would like to get many of these function into a simple GUI that allows me to do many of these functions easily. I have run into many problems with my little GUI. I have tired to create the simplest code that demo's some of these issues.

 Clear[pmWorkHorseTest]
 Options[pmWorkHorseTest] = {};
 pmWorkHorseTest[opts : OptionsPattern[pmWorkHorseTest]] := Module[{},

CreateWindow[CreateDialog[
  Column[{
   DynamicModule[{x = 0, t = False, wait, ts, te},
    TabView[{

      "Method Example" -> Column[{
         Row[{

           Button["Start", t = True; x = 0; While[t, x++], 
            Method -> "Queued", Enabled -> Dynamic@Not@t],

           Button["Stop", t = False, Method -> "Preemptive", 
            Enabled -> Dynamic@t],
           Button["Stop", t = False, Method -> "Preemptive"],

           Button["Stop", t = False, Method -> "Preemptive", 
            Enabled -> Dynamic@Not@Not@t],
           "x=",
           Dynamic[Refresh[x, UpdateInterval -> 0]]}]
         }],

      "Queued/Preemptive Pause" -> Column[{
         PopupMenu[Dynamic@wait, Range[10]],
         Row[Button[ToString[#], wait = #] & /@ Range[10]],

         Row[{"wait =", 
           Dynamic[Refresh[wait, UpdateInterval -> 0]]}],
         Row[{

           Button["Queued", Pause[Evaluate@Dynamic@wait], 
            Method -> "Queued"],

           Button["Preemptive", Pause[Dynamic@wait], 
            Method -> "Preemptive"]
           }],

         Row[Button["Q: " <> ToString[#], ts = AbsoluteTime[]; 
             Pause[#]; te = AbsoluteTime[], 
             Method -> "Queued"] & /@ Range[10]],

         Row[Button["P " <> ToString[#], Pause[#], 
             Method -> "Preemptive"] & /@ Range[10]],
         Row[{"Time wasted ->", Dynamic[te - ts]}]
         }]

       }](*End Tabview*)
     ],(*End DynamicModule*)

    DefaultButton["Done", DialogReturn[]]
    }]
  ], WindowTitle -> "AutoPM Work Horse", 
 DynamicEvaluationTimeout -> 5]
 ];


pmWorkHorseTest[]; 

Questions:

  • I would like to toggle the Enabled state of button as things change. The "Start" button works as expected. But the "Stop" buttons do not. I would have expected the first "Stop" button to toggle its state. But notice that the last "Stop" button does work when I do something silly Enabled -> Dynamic@Not@Not@t (The middle "Stop" button is to stop when the others do not work.) What is the best way to mange state?
  • One of my buttons calls a function that gets data from the database then writes out a spreadsheet. This takes some time. The function by itself works find but the button times out. I have tried Method -> "Queued". Which with the database/spreadsheet function does nothing. With Method -> "Preemptive" it times out. I've tried changing DynamicEvaluationTimeout but I can't get that to work either. In the example code on the "Queued/Preemptive Pause" tab I try to use buttons that use "Preemptive" and "Queued" where I can pick the Pause[] time. No matter what I have tired I cannot seam to change the Time out time. What is the best way to deal with functions that take a long time?
  • Also in the "Queued/Preemptive Pause" why can't I use the Dynamic value of wait to set the Pause[wait] time?
$\endgroup$

1 Answer 1

2
$\begingroup$

Problem 1

Your first problem seems to be related to a subtle bug (?) which results in a Dynamic[t] to be treated slightly different than a Dynamic[something[t]]. You can get the first button to work with either double negating the boolean as you did or by using TrueQ[t] (which might be a good idea in the sense of of defensive programming anyway). So this would work:

DynamicModule[{t = False}, Row[{
   Button["Start",
    t = True;
    x = 0;
    While[t, Pause[0.001]; x++], Method -> "Queued", 
    Enabled -> Dynamic@Not@t
    ],
   Button["Stop", t = False, Method -> "Preemptive", 
    Enabled -> Dynamic@TrueQ[t]],
   "x=", Dynamic[x, UpdateInterval -> 0]
   }]
 ]

For those who are interested in here are some more variants:

identity[x_]:=x;
DynamicModule[{t = False}, Row[{
   Button["Start",
    t = True;
    x = 0;
    While[t, Pause[0.001]; x++], Method -> "Queued", 
    Enabled -> Dynamic@Not@t
    ],
   Spacer[5],
   Button["Stop", t = False, Method -> "Preemptive", 
    Enabled -> Dynamic@t],
   Button["Stop", t = False, Method -> "Preemptive", 
    Enabled -> Dynamic@TrueQ[t]],
   Spacer[5],
   Button["Stop", t = False, Method -> "Preemptive", 
    Enabled -> Dynamic@Identity[t]],
   Button["Stop", t = False, Method -> "Preemptive", 
    Enabled -> Dynamic@identity[t]],
   "x=", Dynamic[x, UpdateInterval -> 0]
   }]
 ]

to me that doesn't look very consistent but at least we have several workarounds...

Problem 2

The second and third problems result in a misunderstanding of how Dynamic works, other than that Method->"Queued" is indeed doing what you expect and works alright. You should probably never need to use a Dynamic within the action of a button. This post to mathgroup of John Fultz is the probably most authorative place to look for more details about how to correctly use Dynamic (except of course the tutorials in the documentation). The short story is that Dynamic only has the desired effect when it is part of the output shown in the frontend. Everywhere else it basically just behaves like a Hold wrapper -- which explains why your queued button doesn't work as expected. Actually I did get a warning message when pushing the buttons (Pause::numnm) and if something doesn't work as expected it is a good idea to have an eye on such warnings. If you didn't see it you might want to check the settings for how and where warning messages are shown. The warning indicates that Pause didn't get a number as an argument. What it did get was a Dynamic[wait]. Unfortunately when the message is displayed the Dynamic does its job and is displayed in the message as the current value of wait so what you see is:

Pause::numnm: Non-negative machine-sized number expected at position 1 in Pause[3]. >>

which looks a little self contradicting and actually should better be:

Pause::numnm: Non-negative machine-sized number expected at position 1 in Pause[Dynamic[wait]]. >>

Here is that part of your code which I think should be doing what you expect:

Style[
 DynamicModule[{wait = 0},
  Column[{
    Row[Button[ToString[#], wait = #] & /@ Range[3]],
    Row[{"wait =", Dynamic[wait]}],
    Row[{
      Button["Queued", Pause[wait]; wait = 0, Method -> "Queued"],
      Button["Preemptive", Pause[wait]; wait = 0]
      }]
    }]
  ],
 DynamicEvaluationTimeout -> 1
 ]

and the answer to your last question is that of course you can use the value of the dynamic-module variable wait in Pause, you just must not use the Dynamic wrapper there.

$\endgroup$
2
  • $\begingroup$ +1 Linking John's post was very helpful; the race condition that can be set up was quite surprising behavior IMO. $\endgroup$ Mar 2, 2014 at 14:21
  • $\begingroup$ Thank you. Yes I did see that Pause message but because I thought 3 was a number I was rather confused. $\endgroup$
    – c186282
    Mar 2, 2014 at 16:24

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.