12
$\begingroup$

In the following minimal working example:

Manipulate[BarChart[{a,b}],{a,1,2},{b,1,2}]

I'd like to be able to control the size of the bars by using some sort of Locator property for manipulate i.e. change the vertical size of each bar by placing my mouse on the edge of the bar (ideally anywhere along its width).

Obviously, Locator won't work. Any ideas how to easily do this?

I want the value of the bar chart to dynamically update. In the above example, I would have a, b obey some constraint (like a + b = 2 so whenever one changes the other does so accordingly).

-------- Answered --------

This was so that there's some playing with the Greek general election. I wanted to play with the latest poll results and vary them before the election on Sunday.

@Mike Honeychurch: You are right, barspacing doesn't change the locator positioning but the locators are off center like so (this is my output when I run your code):

Mikes

I assume you run a different version of Mathematica than me - I run 7, sorry, should have said so. So, I had to fiddle with the x-coordinates of your locators in some way like this when defining pt: numberofpoints = 5; barspacing = 0.1; xcoords = Table[i + barspacing i - 1/2, {i, 1, numberofpoints}]; pt = xcoords~Riffle~RandomInteger[{0, 3}, 3]~Partition~2

I got the chart to look OK at the end (locators here for only the first three bars):

Mikes to mine

but wasn't entirely happy with the way it was done because of the fiddling I mentioned above. So I am going with @Heike 's approach that works a treat out of the box exactly the way I wanted:

lastpoll = {{LightBlue, 24.3, "ΝΔ"}, {Green, 15.4, "ΠΑΣΟΚ"}, {Red, 10.9, "ΚΚΕ"}, 
{Yellow, 8, "ΔΗΜΑΡ"}, { Pink, 9.5, "ΣΥΡΙΖΑ"}, {Blue, 9.6, "Αν. Ελληνες"},
{Gray, 6.3, "Χρυση Αυγη"}, {LightBlue, 4.2, "ΛΑΟΣ"}, {Green, 4.4, "Οικολογοι"}, 
{LightCyan, 2.1, "Δημ. Συμμαχια"}, {Yellow, .8, "ΕΠΑΜ"}, { LightRed, .8, "ΔΡΑΣΗ"},
{Blue, .8, "ΑΝΤΑΡΣΥΑ"}, {LightGray, .8, "Κοιν. Συμφωνια"}, {White, 2, "Λοιπα"}};
values = #[[2]] & /@ lastpoll;
dragBar[values, 
ChartLabels -> Placed[(Last /@ lastpoll), Axis, Rotate[#, 55 Degree] &],
ChartStyle -> (First /@ lastpoll), BarSpacing -> .6, ImageSize -> Large]

with the following result (all fiddlable):

enter image description here

In any case, thank you all for taking the time. This was harder than I expected it would be..

$\endgroup$
9
  • $\begingroup$ What does "MWE" mean? $\endgroup$ May 2, 2012 at 15:00
  • $\begingroup$ Sorry, it stands for Minimal Working Example. It's not the code of what I want (as it's more complicated) but demonstrates the problem I have $\endgroup$
    – gpap
    May 2, 2012 at 15:02
  • $\begingroup$ Do you also need to use the bar heights in some other place in the Manipulate, or do you merely need resizable boxes? $\endgroup$
    – Szabolcs
    May 2, 2012 at 15:13
  • $\begingroup$ So you want the bar height to be draggable in order to control a a variable dynamically? Or do you want the bar height to be dynamically controlled by the value of a variable? $\endgroup$
    – Jens
    May 2, 2012 at 15:20
  • 1
    $\begingroup$ whoopsy - ran it without interactivity so that the bars don't move when I select the image. Should work now. $\endgroup$
    – gpap
    May 4, 2012 at 9:14

4 Answers 4

8
$\begingroup$

Here's my attempt. The tricky part turned out to be to only change the height of a bar when the mouse cursor is close enough to its top edge. By default, the vertical distance between the mouse cursor and the top edge needs to be less than .5 in order to drag the bar. You can change this value by setting the option "resolution".

I've also implemented the constraint that the sum of the heights is constant so when one bar is dragged the heights of other bars change to keep the total height constant.

Note that dragBar accepts any option of BarChart so you can still use all the features of BarChart.

SetAttributes[dragBar, HoldFirst];
Options[dragBar] = Append[Options[BarChart], "resolution" -> .5];

dragBar[values_, opt : OptionsPattern[dragBar]] := 
 DynamicModule[{widths, ind, pt = {0, 0}, index},
  widths = Reap[BarChart[ConstantArray[1, Length[values]], 
      ChartElementFunction :> ((If[Head[#1] === List, Sow[#1[[1]]]];) &), 
      FilterRules[{opt}, Options[BarChart]]]][[2, 1]];
  ind[x_] := Piecewise[
    Table[{i, widths[[i, 1]] < x < widths[[i, 2]]}, {i, Length[widths]}], 0];

  LocatorPane[Dynamic[pt,
    {(index = ind[#[[1]]]; 
      If[index == 0 || Abs[#[[2]] - values[[index]]] > OptionValue["resolution"], 
        index = None]) &,
     (If[IntegerQ[index],
        values += (values[[index]] - #[[2]])/(Length[values] - 1);
        values[[index]] = #[[2]]]) &,
     None}],
   Dynamic[BarChart[values, FilterRules[{opt}, Options[BarChart]]]], Appearance -> None]]

values = RandomInteger[10, 10];
dragBar[values, BarSpacing -> .6]

Mathematica graphics

$\endgroup$
13
$\begingroup$

Here is something that ticks all the boxes (I think).

  1. you use locators to change the height of the bars

  2. you have a BarChart and can style it as you wish.

  3. the controlling locator is centred on each bar

  4. the sum of the heights is constrained -- in this case they sum to 5.

So let me know if this is what you are after:

DynamicModule[{pt = {{1, 3}, {2, 2}}, tmp, sum = 5},

 LocatorPane[
  Dynamic[pt, (tmp = #; 
     pt = {{1, sum - tmp[[2, 2]]}, {2, sum - tmp[[1, 2]]}}) &],

  Dynamic@BarChart[{pt[[1, 2]], pt[[2, 2]]},
    PerformanceGoal -> "Speed",
    PlotRange -> {{0.3, 2.5}, {0, 5}}]
  ]
 ]

enter image description here

Edit

BarSpacing does not effect the locator positioning:

DynamicModule[{pt = {{1, 3}, {2, 2}, {3, 3}}, tmp},

 LocatorPane[Dynamic[pt, (tmp = #;
     pt = {{1, tmp[[1, 2]]}, {2, tmp[[2, 2]]}, {3, tmp[[3, 2]]}}) &],

  Dynamic@BarChart[{pt[[1, 2]], pt[[2, 2]], pt[[3, 2]]},
    BarSpacing -> Large,
    PerformanceGoal -> "Speed",
    PlotRange -> {{0.3, 3.5}, {0, 5}}]
  ]
 ]

enter image description here

$\endgroup$
2
  • $\begingroup$ Mike: thanks - I think yours is my favourite so far although my problem is that still in defining the Locator you need to specify the x-coordinate for it which is extra information in BarChart. So, if I want to plot n bars in my chart I define some variable barspacing and add the x-coordinates to each locators as Table[i + barspacing i - 1/2,{i,1,n}] (in the middle) and then add BarSpacing->barspacing to the BarChart. Anyway, I'll give it until tonight to play more with it. The idea is to have a bar chart for all parties in the Greek general election and fiddle with their %s. $\endgroup$
    – gpap
    May 3, 2012 at 11:22
  • $\begingroup$ As per my edit the setting for BarSpacing does not change your locater positioning. The locator will always be centred. The edited example is not constrained but you can add a constraint with "n" bars as per the first example by e.g. for the first bar sum - tmp[[2, 2]] -tmp[[3, 2]] - tmp[[n,2]] and so on. $\endgroup$ May 3, 2012 at 20:27
9
$\begingroup$

Here is another sketch:

DynamicModule[{a, b, ab, val},
 {a, b} = {1, 1};
 ab[{x_, y_}] := Which[
   0.2 <= x <= 1.2, {a = y, b = 2 - a},
   1.2 <= x <= 2.4, {a = 2 - y, b = y},
   True, {a, b}
   ];
 Dynamic@BarChart[{val = ab@MousePosition["Graphics", {0, 0}]},
   PlotRange -> {-10, 10},
   BarSpacing -> None,
   ChartLabels -> {val}
   ]
 ]

The chart changes dynamically when you move over the bars, no need to click. The dependence a+b=2 is implemented as well.

enter image description here

$\endgroup$
4
$\begingroup$

Does this give you the basis for a solution:

Manipulate[
 Framed@Graphics[{Red, Rectangle[{0, 0}, {1, Last@p}], Blue, 
    Rectangle[{1.1, 0}, {2.1, Last@q}]}], {{p, {1, 1}}, 
  Locator}, {{q, {1.1, 2}}, Locator}]

Mathematica graphics

$\endgroup$
3
  • $\begingroup$ Well, kind of but ideally want to use some of the native labelling and spacing within BarChart and trying to use the Locator the way you do doesn't quite work. The bars in BarChart have varied widths depending on how you space them so a Locator's x-coordinate can vary in a BarChart. Also I was hoping the equivalent of the Locator cross could be the entire upper line (just for aesthetic reasons). $\endgroup$
    – gpap
    May 2, 2012 at 15:48
  • $\begingroup$ At the moment I believe you can click on any part of the "bar" and adjust it's height. Normally the Locator symbol can be set to none (or replaced with something else ) , but I couldn't quite work out how to do that here. Maybe someone else knows how. $\endgroup$ May 2, 2012 at 16:04
  • $\begingroup$ @image_doctor you can append control options inside a variable specification: {{p, {1, 1}}, Locator, Appearance -> None} $\endgroup$
    – amr
    May 4, 2012 at 13:58

Your Answer

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

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