1
$\begingroup$

My primary objective is to build a file chooser that aides a user in selecting files into one of two categories. Files can be individually selected, or based on the clicking of a specific checkbox, all files listed above it will be selected, or de-selected.

Consider Code Snippet 1: (this code almost accomplishes what I need)

celdatafiles = {"GSM15432", "GSM98765", "GSM34521", "GSM65912", 
  Style["Select All", Bold, Red], 
  Style["Unselect All", Bold, Red]};(* arbitrary test data *)

expsym = ConstantArray[False, Length[celdatafiles]];
contsym = ConstantArray[False, Length[celdatafiles]];

expcheckbox = 
  Table[With[{i = i}, Checkbox[Dynamic[expsym[[i]]]]], {i, 1, 
    Length[expsym]}]; 
contcheckbox = 
  Table[With[{i = i}, Checkbox[Dynamic[contsym[[i]]]]], {i, 1, 
    Length[contsym]}];

celoptions = 
  MapThread[List, {celdatafiles, expcheckbox, contcheckbox}]; 

(* BEGIN CODE to add control over Select All and Unselect All \
functionality *)
Dynamic[Which[
  expsym[[-2]] == True \[And] 
   expsym[[-1]] == False, (expsym[[1 ;; (Length[expsym] - 2)]] = 
    ConstantArray[True, (Length[expsym] - 2)];
   expsym[[-2]] = False),

  expsym[[-2]] == False \[And] 
   expsym[[-1]] == True, (expsym[[1 ;; (Length[expsym] - 2)]] = 
    ConstantArray[False, (Length[expsym] - 2)];
   expsym[[-1]] = 
    False)]](* do NOT use semicolon at end of statement, because it \
affects display updating *)

(* control code for Select All and UnSelect All functionality \
*)Dynamic[
 Which[contsym[[-2]] == True \[And] 
   contsym[[-1]] == False, (contsym[[1 ;; (Length[contsym] - 2)]] = 
    ConstantArray[True, (Length[contsym] - 2)];
   contsym[[-2]] = False),

  contsym[[-2]] == False \[And] 
   contsym[[-1]] == True, (contsym[[1 ;; (Length[contsym] - 2)]] = 
    ConstantArray[False, (Length[contsym] - 2)];
   contsym[[-1]] = False)]]

expchips = 
 Dynamic[Pick[celdatafiles, expsym, 
   True]];(* grab experimental chips that were selected *)
contchips = 
 Dynamic[Pick[celdatafiles, contsym, 
   True]];(* grab control chips that were selected *)

analysisfiles = 
 TableForm[celoptions, TableAlignments -> Center, 
  TableHeadings -> {None, 
    Map[Style[#, Bold, Blue] &, {"File Name", "Experimental?", 
      "Control?"}]}];(* formatting for gui element where user chooses \
cel files to be analyzed *)

(* DialogInput is used because it blocks the kernel from moving \
forward until the dialog is closed *)
DialogInput[{analysisfiles,

  Button["I'm finished selecting files",
   DialogReturn[]]},

 WindowTitle -> "Choose the CEL files you want to analyze",
 WindowSize -> {400, 400},
 WindowElements -> "VerticalScrollBar"]

Comments and questions about Code Snippet 1 above:

  1. Notice how the custom control allows multiple files to be chosen by clicking the "Select All" checkbox under a category, and also if files are already chosen, the control allows multiple files to be unchosen by clicking the "Deselect All" checkbox. This is the behavior that I want.

  2. Notice how execution of Code Snippet 1 always produces the "Null", "Null" output in the notebook. I believe this is because the Dynamic statements controlling the "SelectAll" and "Deselect All" behavior do not have a semicolon after them. If I use a semicolon, the Dynamic statements are suppressed and the "Select All"/"Deselect All" functionality does not work anymore.

Q1. How can I maintain the desired behavior without producing the "Null", "Null" output?

  1. Because I am using the code above inside a custom palette, I really would like to place Code Snippet 1 inside DynamicModule. Unfortunately, this completely destroys the "Select All" and "Unselect All" behavior that I really need to preserve.

Consider what happens when you place code snippet 1 inside a DynamicModule

Code Snippet 2: (desired functionality of code snippet 1 destroyed)

celdatafiles = {"GSM15432", "GSM98765", "GSM34521", "GSM65912", 
   Style["Select All", Bold, Red], 
   Style["Unselect All", Bold, Red]};(* arbitrary test data *)

celChooser[celdatafiles_] := 
 DynamicModule[{expsym, contsym, expcheckbox, contcheckbox, 
   celoptions, expchips, contchips, analysisfiles},

  expsym = ConstantArray[False, Length[celdatafiles]];
  contsym = ConstantArray[False, Length[celdatafiles]];

  expcheckbox = 
   Table[With[{i = i}, Checkbox[Dynamic[expsym[[i]]]]], {i, 1, 
     Length[expsym]}]; 
  contcheckbox = 
   Table[With[{i = i}, Checkbox[Dynamic[contsym[[i]]]]], {i, 1, 
     Length[contsym]}];

  celoptions = 
   MapThread[List, {celdatafiles, expcheckbox, contcheckbox}]; 

  (* BEGIN CODE to add control over Select All and Unselect All \
functionality *)
  Dynamic[
     Which[expsym[[-2]] == True \[And] 
       expsym[[-1]] == False, (expsym[[1 ;; (Length[expsym] - 2)]] = 
        ConstantArray[True, (Length[expsym] - 2)];
       expsym[[-2]] = False),

      expsym[[-2]] == False \[And] 
       expsym[[-1]] == True, (expsym[[1 ;; (Length[expsym] - 2)]] = 
        ConstantArray[False, (Length[expsym] - 2)];
       expsym[[-1]] = False)]](* 
    do NOT use semicolon at end of statement, 
    because it affects display updating *)

    (* control code for Select All and UnSelect All functionality *)
    Dynamic[Which[
      contsym[[-2]] == True \[And] 
       contsym[[-1]] == 
        False, (contsym[[1 ;; (Length[contsym] - 2)]] = 
        ConstantArray[True, (Length[contsym] - 2)];
       contsym[[-2]] = False),

      contsym[[-2]] == False \[And] 
       contsym[[-1]] == True, (contsym[[1 ;; (Length[contsym] - 2)]] =
         ConstantArray[False, (Length[contsym] - 2)];
       contsym[[-1]] = False)]]

    expchips = Dynamic[Pick[celdatafiles, expsym, True]];(* 
  grab experimental chips that were selected *)
  contchips = Dynamic[Pick[celdatafiles, contsym, True]];(* 
  grab control chips that were selected *)

  analysisfiles = 
   TableForm[celoptions, TableAlignments -> Center, 
    TableHeadings -> {None, 
      Map[Style[#, Bold, Blue] &, {"File Name", "Experimental?", 
        "Control?"}]}];(* 
  formatting for gui element where user chooses cel files to be \
analyzed *)

  (* DialogInput is used because it blocks the kernel from moving \
forward until the dialog is closed *)
  DialogInput[{analysisfiles,

    Button["I'm finished selecting files",
     DialogReturn[]]},

   WindowTitle -> "Choose the CEL files you want to analyze",
   WindowSize -> {400, 400},
   WindowElements -> "VerticalScrollBar"]]

celChooser[celdatafiles]

Comments and questions about Code Snippet 2:

  1. If you run code snippet 2, you notice the following things:

A. The "Select All" and "Unselect All" functionality no longer works.

Q2. Why does placing this code inside DynamicModule break the behavior I need?

Q3. I feel like I'm wrestling with Mathematica (and I'm not winning!), so is there an alternative approach I should be trying to get the "Select All"/"Unselect All" functionality to work properly inside DynamicModule.

B. Code snippet 2 produces a Tag Times error, which I believe is also do to the Dynamic[Which .....] statement not having a semicolon after it.

In conclusion, how can I maintain the behavior I am after while inside a DynamicModule? Thanks!

$\endgroup$
1
$\begingroup$

I have been unsuccessful troubleshooting my code above. As is typical, I've had to step back and approach the problem from a different angle. As such, the code here does accomplish my goal of creating a file chooser that allows a user to select files into one of two categories, while existing inside a DynamicModule. Hopefully this approach may help others seeking to build simple gui elements.

Working code:

DynamicModule[{expsym, contsym, heading, list1, list2, filenames, 
   groupstuff, poop1, poop2, poop3, poop4, lastline1, lastline2, 
   combine},

  expsym = ConstantArray[False, 6];
  contsym = ConstantArray[False, 6];
  filenames = {"file1", "file2", "file3", "file4", "file5", "file6"};
  heading = {" ", Style["Experimental", Bold, Blue], 
    Style["Control", Bold, Blue]};

  list1 = 
   Table[With[{i = i}, Checkbox[Dynamic[expsym[[i]]]]], {i, 1, 6}];
  list2 = 
   Table[With[{i = i}, Checkbox[Dynamic[contsym[[i]]]]], {i, 1, 6}];
  groupstuff = MapThread[List, {filenames, list1, list2}];

  poop1 = 
   Button["O", (expsym = ConstantArray[True, 6]), ImageSize -> Small];
  poop2 = 
   Button["O", (expsym = ConstantArray[False, 6]), ImageSize -> Small];
  poop3 = 
   Button["O", (contsym = ConstantArray[True, 6]), ImageSize -> Small];
  poop4 = 
   Button["O", (contsym = ConstantArray[False, 6]), 
    ImageSize -> Small];

  lastline1 = {Style["Select All", Bold, Red], poop1, poop3};
  lastline2 = {Style["Unselect All", Bold, Red], poop2, poop4};

  combine = 
   Partition[Flatten[Join[heading, groupstuff, lastline1, lastline2]],
     3];

  DialogInput[{Grid[combine],
    Button["I'm finished selecting files", DialogReturn[]]},
   WindowTitle -> "Choose the CEL files you want to analyze", 
   WindowSize -> {400, 400}, WindowElements -> "VerticalScrollBar"]];

Some musings on the questions proposed above in regard to the code that I could not get working:

Under Code Snippet 1, I had asked: Q1. How can I maintain the desired behavior without producing the "Null", "Null" output?

It seems likely that the large Dynamic expression (Dynamic[Which[Expsym[[-2]] .....) is responsible for the Null output (when no semicolon is present). As I was troubleshooting this snippet of code, I noticed that when I placed a semicolon after the expression, the checkboxes of the gui element (executed in the front end) did not update. However, when I requested the values of the checkboxes in the kernel, they did seem to update. So, I think I somehow designed code that would not update in the front end, but would update in the kernel.

How can one disambiguate the two? I don't know.

Under code snippet 2, I asked:

Q2. Why does placing this code inside DynamicModule break the behavior I need?

My only guess is in reference to the paragraph above, for some reason the code is updating in the kernel, but is not reflected visually in the front end.

Q3. I feel like I'm wrestling with Mathematica (and I'm not winning!), so is there an alternative approach I should be trying to get the "Select All"/"Unselect All" functionality to work properly inside DynamicModule.

The alternative approach I settled upon, is reflected in the working code of this answer. I decided to simplify the layout of the gui element, progressively building rows of the output, and waiting until the last expression inside the DynamicModule to actually display the element. (In this way you can avoid issues with semicolons, as I had above).

Additionally, rather than trying to dynamically update checkboxes that were part of a list of checkboxes, I changed the "Select All" and "Unselect All" checkboxes to Buttons that would accomplish the same effect.

$\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.