6

To add a Divider between rows in a Grid, one way I know how to do it is by using the Dividers option. But this is used at the end, and one must then know the row number to which a divider needs to be below it. So, for large grid, I find myself using trial and error until I find the correct row number. Then when I change the grid later, I have to do the trial and error again to put the divider in the correct place in case it moved after my changes.

In this example, there is a grid with 3 rows and I want to add divider below say the second row, so I can do this:

Grid[{
  {1},
  {2},
  {3}
  }, Frame -> True, Dividers -> {All, 3 -> True}]

Another way, is to put False and True, in correct order where I want a divider, like this

Grid[{
  {1},
  {2},
  {3}
  }, Frame -> True, Dividers -> {All, {True, False, True, True}}]

enter image description here

It would be nice if I could do something like this (like one can do for Manipulate) (ofcourse this below does not work here)

Grid[{
  {1},
  {2},
  Delimiter,
  {3}
  }, Frame -> True]

or

Grid[{
  {1},
  {Item[2,Dividers->True]},
  {3}
  }, Frame -> True]

or such thing.

It will make code maintenance easier.

I looked at using Item and such for this, but not able to figure it out.

Any one knows of a trick to do it?

edit:

btw, This trick if possible, does not only apply to Dividers. But it would be useful to be able to do many of the other Grid options, which now is done at the Grid level, be done as well at the item level. For example, if I want to add an extra space after some row, it will easier to say so right at the pace I wanted this done. Also, if I wanted to change the Item size, easier to do it at the spot, same for Spacings, etc.. So that when moving/copying code for a whole row or Item, it is self contained and copy it with all its options together.

I think now this might require a new option add to Mathematica Grid to make it work right and be consistent with the over all grid design.

This is all until a GUI builder is made for Mathematica.

I find that I spend more than 60% of my time when I write a demo getting the GUI to fit and look right. With a GUI builder, I can spend this time working on the algorithm instead. When I use Matlab GUIDE to make GUI, it takes me less than 5% of my time to make a similar GUI.

I wish that WRI would make a GUI builder for Mathematica, I think this will be the killer app for Mathematica if you ask me. But no body asked me :)

edit(2)

comment on Mr Wizard nice solution below.
I mainly wanted this feature for the Grids I use to layout controls for Manipulate. Here is a simple example:

Manipulate[x,

 Grid[{
   {Control[{{x, 0, "x"}, 0, 10, 1}]},
   {Control[{{y, 0, "y"}, 0, 10, 1}]}
   }, Frame -> None, Dividers -> {None, {False, True, False}}
  ]
 ]

enter image description here

(and I have to use Grid for setting up the controls). I can't use function calls here. I can't write, using Wizard's solution below, the following:

Manipulate[x,

 myGrid[{
   {Control[{{x, 0, "x"}, 0, 10, 1}]},
   spec["Divider"],
   {Control[{{y, 0, "y"}, 0, 10, 1}]}
   }, Frame -> None
  ],

 Initialization :>
  {
   specfunc["Divider", lst_] := Dividers -> {None, # -> True & /@ lst};

   myGrid[dat_, opts : OptionsPattern[]] :=
    Module[{x = 1}, 
     Grid[#, opts, Sequence @@ #2] & @@ 
      Reap[If[MatchQ[#, _spec], Sow[x, #[[1]]]; ## &[], x++; #] & /@ 
        dat, _, specfunc]
     ]

   }
 ]

This gives an error, since Mathematica tries to first read the body of Manipulate to parse it, BEFORE reading and processing the initialization section.

But outside Manipulate, it will ofcourse work:

myGrid[{
  {Control[{{x, 0, "x"}, 0, 10, 1}]},
  spec["Divider"],
  {Control[{{y, 0, "y"}, 0, 10, 1}]}
  }, Frame -> None
 ]

specfunc["Divider", lst_] := Dividers -> {None, # -> True & /@ lst};
myGrid[dat_, opts : OptionsPattern[]] :=
 Module[{x = 1}, 
  Grid[#, opts, Sequence @@ #2] & @@ 
   Reap[If[MatchQ[#, _spec], Sow[x, #[[1]]]; ## &[], x++; #] & /@ 
     dat, _, specfunc]
  ]

enter image description here

I need to spend more time on this, to see if I can get it to work inside Manipulate.

btw, getting stuff like this to work inside Manipulate is really hard. Only trick I know is using macros with the With[{},.... Grid....] pattern I learned from Leonid.

For example of such difficulties, see this question of mine

How to define constants for use with With[] in one place and then apply them later?

edit(3) My be I doing something wrong, but I am getting some errors inside Manipulate:

first example:

Manipulate[x,

 Evaluate@grid[{
    {Control[{{x, 0, "x"}, 0, 10, 1}]}
    }
   ],

 Initialization :> 
  {
   grid[tab_, opts___] := 
     Module[{divlocal, divglobal, div, pos}, 
      divglobal = (Dividers /. opts) /. Dividers -> {False, False};
      If[Depth[divglobal] == 1, divglobal = {divglobal, divglobal}];
      If[Length[divglobal] == 1, AppendTo[divglobal, False]];
      pos = Position[tab, Dividers -> _, 1];
      divlocal = 
       MapIndexed[# - #2[[1]] + 1 -> Dividers /. tab[[#]] &, 
        Flatten[pos]];
      divglobal[[2]] = {divglobal[[2]], divlocal};
      Grid[Delete[tab, pos], Dividers -> divglobal, opts]];


   }
 ]

gives error:

ReplaceAll::rmix: Elements of {False,{}} are a mixture of lists and nonlists. >>

same if I replace the above with

Evaluate@grid[{
   Dividers -> {Thick, Blue},
   {Control[{{x, 0, "x"}, 0, 10, 1}]}
   }
  ],

Tried Dynamic@ in place of Evaluate@ no luck. may be small fix is all what is needed? or I am not using it right?

Community
  • 1
  • 1
Nasser
  • 12,849
  • 6
  • 52
  • 104
  • hi Mike. Yes, I know where I want to add the divider, but in long complicated grid, it is easier to add a code snippet at the place I want the divider added, rather than put the code at the bottom, and have to count and make mistakes. Then redo, when grid changes. That is my point. Thanks – Nasser Jan 16 '12 at 05:19
  • The whole issue of a gui builder vs hand coding of UI is something well discussed in many places on the net. I'd like to talk about it more, but I do not think this is the right place for it. May be when we get our own Mathematica forum we can talk more about it. But from my experience, using a GUI builder saves a lot of programmer time over hand coding. cost/time analysis favors using GUI builder. The time saved can be used for other much important things. btw, I am talking here about large complex UI's, not a simple one or 2 sliders and a Button and one plot type UI. – Nasser Jan 16 '12 at 06:51
  • 1
    To get the `Manipulate` in edit(2) to work you could replace the control with `Dynamic@myGrid[...]`. – Heike Jan 16 '12 at 09:07
  • @Heike, thanks, yes that does it. Now it works just fine inside this Manipulate example. I need to try this in my main code and I hope it will still work. Many times, Mathematica env. becomes too complex for me to reason it out by just looking at it, and I only find if things will work or not by trial and error :) – Nasser Jan 16 '12 at 09:21
  • Nasser, that complexity is a strong motivation for *not* using something like a custom `Grid` specification directly inside the `Manipulate` block, IMHO. – Mr.Wizard Jan 16 '12 at 09:36
  • @NasserM.Abbasi I've made a few corrections to my code below. It should work with the new version. – Heike Jan 16 '12 at 10:26

2 Answers2

4

This solutions should allow you to combine specifications for dividers between rows specified within the table with those specified using the Dividers option.

grid[tab_, opts___] :=
 Module[{divlocal, divglobal, div, pos},

  (* extract option value of Dividers from opts to divglobal *)
  (* default value is {False, False} *)
  divglobal = (Dividers /. {opts}) /. Dividers -> {False, False};

  (* transform divglobal so that it is in the form {colspecs, rowspecs} *)
  If[Head[divglobal] =!= List, divglobal = {divglobal, divglobal}];
  If[Length[divglobal] == 1, AppendTo[divglobal, False]];

  (* Extract positions of dividers between rows from tab *)
  pos = Position[tab, Dividers -> _, 1];

  (* Build list of rules for divider specifications between rows *)
  divlocal = MapIndexed[# - #2[[1]] + 1 -> Dividers /. tab[[#]] &, Flatten[pos]];

  (* Final settings for dividers are {colspecs, {rowspecs, divlocal}} *)
  divglobal[[2]] = {divglobal[[2]], divlocal};
  Grid[Delete[tab, pos], Dividers -> divglobal, opts]]

To specify a divider between rows you need to insert Dividers->spec at the desired position where spec is either False, True, or a graphics directive (colour, thickness, etc.). For example

tab = {{1, 2, 3}, Dividers -> {Thick, Blue}, {4, 5, 6}, {7, 8, 9}, 
   Dividers -> False, {10, 11, 12}};

grid[tab, Dividers -> All]

Mathematica graphics

Edit

I've added some comments to my code at the request of Mr. Wizard.

Heike
  • 24,102
  • 2
  • 31
  • 45
  • +1, however I would appreciate some explanation for each line of code. – Mr.Wizard Jan 16 '12 at 09:35
  • Also, I think you need `/. {opts}` – Mr.Wizard Jan 16 '12 at 09:46
  • Heike, thanks. But I am getting some difficulty getting this to work inside Manipulate. I tried Dynamic@ and Evaluate@ but get some errors. Will show examples I tried in my edit (3). – Nasser Jan 16 '12 at 10:13
  • Thanks! I just tried your new update, and the code now works very well! Your solution and Mr Wizard's are both equally excellent solutions, I have to flip a coin now to decide which one to use :). – Nasser Jan 16 '12 at 10:43
2

I propose that you use a new head that can hold instructions (spec below), and a new function that processes these instructions (specfunc below) as individually required. The benefit is that this is easily generalized for different interleaved instructions, and each can be processed in an arbitrary way.

specfunc["Divider", lst_] := Dividers -> {All, # -> True & /@ lst}

myGrid[dat_, opts:OptionsPattern[]] :=
 Module[{x = 1},
  Grid[#, opts, Sequence @@ #2] & @@
   Reap[
     If[MatchQ[#, _spec], Sow[x, #[[1]]]; ## &[], x++; #] & /@ dat,
     _,
     specfunc
   ]
 ]

Usage:

dat =
{
   {1, 2, 3},
   {4, 5, 6},
   spec["Divider"],
   {7, 8, 9}, 
   spec["Divider"],
   {"a", "b", "c"}
};

myGrid[dat, Frame -> True]

Mathematica graphics


If each instruction can be a single string such as "Divider" and you have no conflict using them this way, you could eliminate spec and use MatchQ[#, _String] and Sow[x, #].


Addressing your updated question, as I noted in a comment below I believe that it makes more sense to use the most basic elements in your final Manipulate object, and write tools to help you generate this object most easily. I believe that attempting to make these kinds of customization inside the Manipulate block itself is doomed to fail, and probably in weird and opaque ways.

Nevertheless, for this particular case this appears to work, though I doubt it is robust:

Manipulate[x,
 Evaluate[
  {specfunc["Divider", lst_] := Dividers -> {All, # -> True & /@ lst};
   myGrid[dat_, opts : OptionsPattern[]] := 
    Module[{x = 1}, 
     Grid[#, opts, Sequence @@ #2] & @@ 
      Reap[If[MatchQ[#, _spec], Sow[x, #[[1]]]; ## &[], x++; #] & /@ 
        dat, _, specfunc]]};
  myGrid[{{Control[{{x, 0, "x"}, 0, 10, 1}]}, 
    spec["Divider"], {Control[{{y, 0, "y"}, 0, 10, 1}]}}, 
   Frame -> True]
  ]
 ]
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • Thanks, this is really neat, and I could use it. But the main use I have for this feature is in building controls for Manipulate. And I was not able to use it as is in that context. I need to spend more time to see if I can adapt it for that. I am adding edit(2) now to better explain what I mean. The problem is that in Manipulate control area, the environment is a little bit more restrictive and something that work outside, do not work easily there. – Nasser Jan 16 '12 at 08:45
  • 1
    @Nasser I am sorry, I don't build demos, and I don't know how to do everything inside a `Manipulate` the way you are trying to. I can only suggest meta-programming, that is, writing easy to use tools that generate the convoluted `Manipulate` object that is surely required. In your case `Grid` works, so I would use it; instead create a tool to write the `Grid` object more easily. Does this help at all? – Mr.Wizard Jan 16 '12 at 09:09
  • MrWizard, Heike just pointed out a simple fix. Just needed to add Dynamics around the whole thing. I should have tried that myself, and I just forgot to do it. Thanks for a very nice solution. – Nasser Jan 16 '12 at 09:23
  • Yes, using `Evaluate[]` will also make it work inside `Manipulate`. Actually I am using Evaluate already in my main code inside the controls layout area (since I do things like `Evaluate@With[..Grid[....]...]`, so I just tried your solution in my current demo, and all I had to do is just replace the word `Grid` by `myGrid` and it worked like a charm. So, will use your solution in my new demo I am working on. Thanks again, this does help. Now I can add dividers much more easily than before. – Nasser Jan 16 '12 at 09:48