I am using JFreeCharts in java to create a bar chart. My question is fairly simple... how can I choose a custom color for all of the bars in a bar chart? I'm not sure if this customization would be done in a GradientPaint. An example of my code that determines bar color is:
final GradientPaint gp0 = new GradientPaint(
0.0f, 0.0f, Color.blue,
0.0f, 0.0f, Color.blue
);
I'm not sure if this is the right way to go for custom colors or not. Basically, I don't know if GradientPaint is the right way to go or not. If it is, could someone let me know how I could edit this code to make it a custom color rather than blue?
I'm not sure if this helps, but say the information for the custom color was
hue: 142
Sat: 109
Lum:126
Red: 79
Green: 129
Blue: 189
With this is there a way to customize the color of the chart?
It was a while since i coded with jfreechart.Bud if i remember corectly this was code that i wrote to change bar paint ;).
CategoryPlot cplot = (CategoryPlot)chart.getPlot();
cplot.setBackgroundPaint(SystemColor.inactiveCaption);//change background color
//set bar chart color
((BarRenderer)cplot.getRenderer()).setBarPainter(new StandardBarPainter());
BarRenderer r = (BarRenderer)chart.getCategoryPlot().getRenderer();
r.setSeriesPaint(0, Color.blue);
Im looking at the code for my first application ever written.Im not sure if it will work now.
For future i recommend to google out or purchase PDF guide to jfreechart.You find all the references and samples there.Bud if you can ,skip to JavaFX i strongly recommend it ,working with jfreechart is pain.To be honest.Implementing charts in javafx is easy and looks way better ;)
CategoryPlot plot = chart.getCategoryPlot();
BarRenderer renderer = (BarRenderer) plot.getRenderer();
// set the color (r,g,b) or (r,g,b,a)
Color color = new Color(79, 129, 189);
renderer.setSeriesPaint(0, color);
This will set all bars to that specific color. If you would like the colors to change for each row (say, for a stacked bar chart), you can call dataset.getRowCount(), with dataset being of type CategoryDataset, to return to you the number of rows involved for each column of the bar chart. Then, you could index the series in the renderer.setSeriesPaint() call based on the index of the row.
for (int i = 0; i < dataset.getRowCount(); i++){
switch (i) {
case 0:
// red
color = new Color(255, 0, 0);
break;
case 1:
// blue
color = new Color(0, 0, 255);
break;
default:
// green
color = new Color(0, 255, 0);
break;
}
}
Custom Colors in Bar Chart using JfreeChart
CategoryItemRenderer barColor = new CustomRenderer(new Paint[]{});
plot.setRenderer(barColor);
create a new class name is CustomRenderer extends BarRenderer3D or you choose BarRenderer
class CustomRenderer extends BarRenderer3D {
private Paint[] colors;
public CustomRenderer(final Paint[] colors) {
this.colors = colors;
}
public Paint getItemPaint(final int row, final int column) {
if(column==0)
return Color.blue;
else if(column==1)
return Color.CYAN;
else
return Color.RED;
}
}
I think easiest way is using getRenderer().setSeriesPaint(index, color) method.
So as an example you can try the below code for a bar chart which has 3 bars grouped.
JFreeChart barChart = ChartFactory.createBarChart(
"Bar Chart Titke",
"Category", "Score",
dataset,PlotOrientation.HORIZONTAL,
true, true, false);
CategoryPlot plot = barChart.getCategoryPlot();
plot.getRenderer().setSeriesPaint(0, new Color(128, 0, 0));
plot.getRenderer().setSeriesPaint(1, new Color(0, 0, 255));
plot.getRenderer().setSeriesPaint(2, new Color(0, 230, 255));
Related
I am trying to plot a dynamic pie chart using JFreeChart. I do not know how many sections will be present in advance. I receive data every second which updates the graph.
How can I set colour for each section of the chart ? I do not wish to have the default colours.
I have tried setting the DrawingSupplier. Doesn't work. Can anybody help ?
The Plot of the chart for pie charts can be cast to a PiePlot which lets you set the paint color, outline color, shadow color ect.. of each piece of the pie. Heres a little sample of setting the colors for each piece of the pie. If the number of pieces is greater than the amount of colors defined in our array then it will reuse the colors.
The setSectionPaint method takes any type of java.awt.Paint so you can also pass in gradient paints among other things.
PieDataset dataset = ...
JFreeChart chart = ChartFactory.createPieChart("My Chart", dataset, false, true, false);
// all possible colors for each piece of the pie
Color[] colors = new Color[] { new Color(232, 124, 35),
new Color(51, 109, 178), new Color(182, 52, 49),
new Color(103, 131, 45), new Color(108, 77, 146),
new Color(46, 154, 183), new Color(151, 64, 64) };
PiePlot plot = (PiePlot) chart.getPlot();
// set each sections inside paint
int i = 0;
for (Object key : dataset.getKeys()) {
plot.setSectionPaint((Comparable) key, colors[i % colors.length]);
i++;
}
I have the code below; I want it also want the code in the 'action performed' to happen when it is hovered over. Instead of copy and pasteing the code again and have twice as many times, Is there a way to set it easily?
The code:
private void btnGreenActionPerformed(java.awt.event.ActionEvent evt) {
Color btnGrn = new Color(159, 191, 143); //Sets the colour to a class
Color txtGrn = new Color(201, 255, 191); //Sets the colour to a class
Color txtGry = new Color(89, 89, 89); //Sets the colour to a class
this.getContentPane().setBackground(new java.awt.Color(127,191,95)); //Sets background color to green
btnConvert.setBackground(btnGrn); //Changes the colors to green
btnReset.setBackground(btnGrn); //Changes the colors to green
btnClose.setBackground(btnGrn); //Changes the colors to green
btnInfo.setBackground(btnGrn); //Changes the colors to green
txtIncome.setBackground(txtGrn); //Changes the colors to green
txtPayable.setBackground(txtGrn); //Changes the colors to green
txtStatus.setBackground(txtGrn); //Changes the colors to green
txtIncome.setForeground(txtGry); //Changes the colors to grey
txtPayable.setForeground(txtGry); //Changes the colors to grey
txtStatus.setForeground(txtGry); //Changes the colors to grey
}
Note: I have 7 buttons that are all the same except for the color values.
I would add an inner class to your panel class, to look after the colours, then create as many instances of it as you have colour schemes, including whatever colours you like. Something like this.
This is all inside your panel class, by the way. I'm not envisaging this as a stand-alone class in its own .java file; because it's entirely bound to the characteristics of the panel. A more re-usable version of it would look a bit different.
private class ButtonColorScheme {
final Color paneBackground;
final Color buttonBackground;
final Color textBackground;
final Color textForeground;
ButtonColorScheme(Color paneBackground, Color buttonBackground, Color textBackground, Color textForeground) {
this.paneBackground = paneBackground;
this.buttonBackground = buttonBackground;
this.textBackground = textBackground;
this.textForeground = textForeground;
}
void apply() {
getContentPane().setBackground(paneBackground);
btnConvert.setBackground(buttonBackground);
btnReset.setBackground(buttonBackground);
btnClose.setBackground(buttonBackground);
btnInfo.setBackground(buttonBackground);
txtIncome.setBackground(textBackground);
txtPayable.setBackground(textBackground);
txtStatus.setBackground(textBackground);
txtIncome.setForeground(textForeground);
txtPayable.setForeground(textForeground);
txtStatus.setForeground(textForeground);
}
}
private final ButtonColorScheme greenAndGrey = new ButtonColorScheme(
new Color(127,191,95), new Color(159, 191, 143), new Color(201, 255, 191), new Color(89, 89, 89));
private final ButtonColorScheme redAndBlack = new ButtonColorScheme(
new Color(191,120,95), new Color(202, 160, 143), new Color(255, 180, 191), Color.BLACK);
public void btnGreenActionPerformed(ActionEvent evt){
greenAndGrey.apply();
}
public void btnRedActionPerformed(ActionEvent evt){
redAndBlack.apply();
}
Just create a method,
private void setWidgetColors(Color ... colours){
int i=0;
Color btnGrn = i++ <= colours.length?colours[i-1]:new Color(159, 191, 143); //Sets the colour to a class
Color txtGrn = i++ <= colours.length?colours[i-1]:new Color(201, 255, 191); //Sets the colour to a class
Color txtGry = i++ <= colours.length?colours[i-1]:new Color(89, 89, 89);
//Sets the colour to a class
this.getContentPane().setBackground(i++ <= colours.length ? colours[3] : new Color(127,191,95)); //Sets background color to green
btnConvert.setBackground(btnGrn); //Changes the colors to green
btnReset.setBackground(btnGrn); //Changes the colors to green
btnClose.setBackground(btnGrn); //Changes the colors to green
btnInfo.setBackground(btnGrn); //Changes the colors to green
txtIncome.setBackground(txtGrn); //Changes the colors to green
txtPayable.setBackground(txtGrn); //Changes the colors to green
txtStatus.setBackground(txtGrn); //Changes the colors to green
txtIncome.setForeground(txtGry); //Changes the colors to grey
txtPayable.setForeground(txtGry); //Changes the colors to grey
txtStatus.setForeground(txtGry); //Changes the colors to grey
}
And then
private void btnGreenActionPerformed(ActionEvent evt){
Color btnGrn = new Color(159, 191, 143); //Sets the colour to a class
Color txtGrn = new Color(201, 255, 191); //Sets the colour to a class
Color txtGry = new Color(89, 89, 89);
Color backgroundColor = new Color(127,191,95);
setWidgetColors(btnGrn,txtGrn,txtGry,backgroundColor);
}
Methods are quite useful in such cases. Place this code into a method.
Add whatever parameters needed (e.g. you may decide to turn these values
159, 191, 143, etc. into parameters of the method say named r,g,b). Then
just call your method with the arguments you need (e.g. with r=166, g=202, b=192).
I have a single series XYAreaChart
final JFreeChart chart = ChartFactory.createXYAreaChart(
"",
"", "Rolling",
dataset,
PlotOrientation.VERTICAL,
false, // legend
true, // tool tips
false // URLs
);
final XYPlot plot = chart.getXYPlot();
I want to set paint color to Red when RangeValue >0 otherwise set it to Green. This is the nearest bit of code I can find BUT I think it is for two series:
plot.setRenderer(new XYDifferenceRenderer(Color.green, Color.red, false));
Which renderer do I need for a single series?
This may be able to help you. Below is an example showing a basic chart from a JFreechart tutorial. I have added the renderer in your example to it, my theory is that when you have 1 series the 'second series' as it were is effectively 0. Therefore when you apply the two series renderer, if the first series dips below 0 it becomes the negative color, and when it is above 0 becomes the positive colour.
When I opened my saved chart it had red on the minus points, and green on the posotive. See if it helps:
public class SO{
public static void main(String[] args) {
// Create a simple XY chart
XYSeries series = new XYSeries("Gradient logs chopped");
series.add(1, -7);
series.add(2, -2);
series.add(3, 4);
series.add(4, 7);
series.add(5, 10);
// Add the series to your data set
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(series);
// Generate the graph
JFreeChart chart = ChartFactory.createXYLineChart(
"Logs chopped", // Title
"Week", // x-axis Label
"Logs", // y-axis Label
dataset, // Dataset
PlotOrientation.VERTICAL, // Plot Orientation
true, // Show Legend
true, // Use tooltips
false // Configure chart to generate URLs?
);
//The renderer part
XYPlot plot = chart.getXYPlot();
XYDifferenceRenderer rend = new XYDifferenceRenderer(Color.GREEN, Color.RED, false);
rend.setSeriesFillPaint(0, Color.DARK_GRAY);
rend.setRoundXCoordinates(true);
plot.setRenderer(rend);
//The renderer part
try {
ChartUtilities.saveChartAsJPEG(new File("D:\\Users\\user2777005\\Desktop\\XYchart.jpg"), chart, 500, 300);
} catch (IOException e) {
System.err.println("Problem occurred creating chart.");
}
}
}
Good luck!
I'm building multiple stacked bar charts (subplots) that are combined through a CombinedRangeCategoryPlot.
As the subplots datasets do not have the same number of items and since JFreeChart decides to allocate the same space for each subplot, I have different widths of bars.
Is there any way I can align their width (even if it means that the subplots have different widths)?
Please see below for the result and the code I have so far.
Many thanks,
Thomas
//Builds commong range axis
NumberAxis rangeAxis = new NumberAxis("%");
rangeAxis.setRange(0, 1.0);
rangeAxis.setNumberFormatOverride(NumberFormat.getPercentInstance());
//Builds common data set
CombinedRangeCategoryPlot combinedPlots = new CombinedRangeCategoryPlot(rangeAxis);
for (int groupIndex=0; groupIndex<LeakGroups.values().length; ++groupIndex){
//Builds category axis
CategoryAxis categoryAxis = new CategoryAxis(GuiConstants.LEAK_GROUPS_LABELS[groupIndex]);
//Sets margins between bars
categoryAxis.setCategoryMargin(0.5f);
//Builds bar renderer
StackedBarRenderer barRenderer = new StackedBarRenderer();
barRenderer.setRenderAsPercentages(true);
//Builds dot/level renderer
LineAndShapeRenderer dotRenderer = new LineAndShapeRenderer();
//dotRenderer.setSeriesLinesVisible(0, false);
//dotRenderer.setSeriesShapesVisible(0, false);
//dotRenderer.setSeriesLinesVisible(1, false);
//Defines level shape height (depends on chart size): nominal values are for a height of 1000px
int shapeHeightPx = (int) Math.round(20 * (this.getHeight() / 1000.0));
dotRenderer.setSeriesShape(1, new Rectangle(-1, -shapeHeightPx/2, 2, shapeHeightPx));
//Builds plot
CategoryPlot plot = new CategoryPlot();
plot.setDomainAxis(categoryAxis);
plot.setDataset(0, data[groupIndex].bars);
plot.setRenderer(0, barRenderer);
plot.setDataset(1, data[groupIndex].dots);
plot.setRenderer(1, dotRenderer);
//Adds to combined
combinedPlots.add(plot);
}
combinedPlots.setOrientation(PlotOrientation.HORIZONTAL);
//Puts range axis at the bottom
combinedPlots.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
//Changes plot render sequence so that bars are in the background and shapes in front
combinedPlots.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
//Shows gridlines for categories and not for values
combinedPlots.setDomainGridlinesVisible(true);
combinedPlots.setRangeGridlinesVisible(false);
//Creates chart
JFreeChart chart = new JFreeChart("Leaks", combinedPlots);
//Sets a margin right to allow space for last catergory label ("100%")
chart.setPadding(new RectangleInsets(0, 0, 0, 20));
return chart;
For some reason, the weight gets reset to value 1 when adding the plot.
By way of explanation,
The add(CategoryPlot subplot) method specifies a default weight of 1,
The add(CategoryPlot subplot, int weight) method lets you specify a weight value.
After a few hours of search, found the solution: use plot.setWeight().
For some reason, the weight gets reset to value 1 when adding the plot to the CombinedRangeCategoryPlot, hence it has to be set after.
Hope this helps.
Objective is to build an interval bar chart indicating, for each category, a range of acceptable values (between 0 - 100%).
As for each category, I would like to indicate 2 intervals (i.e. a center interval that is green and a surrounding interval that is grey), I think that it is not feasible with a IntervalBarRenderer and I have thus been using a StackedBarRenderer (with transparent layers at the extreme left & right of each bar).
Would this be the right approach?
Also, if this is the right approach, it seems that the bar intervals that are transparent (at the extreme left and right of each bar), do not show their outline: is their any way to have an outline for a transparent paint?
Please see below for the main code and a screenshot of current results.
Many thanks for any help or hint !
Thomas
private JFreeChart createChart(LeaksChartSeriesVO data){
JFreeChart chart = ChartFactory.createStackedBarChart(
"Leak meters", //Title
"Leaks", //Domain axis (X) label
"%", //Range axis (Y) label
data.bars,
PlotOrientation.HORIZONTAL,
true, //Legend?
true, //Tooltip?
false); //Urls?
CategoryPlot plot = (CategoryPlot) chart.getPlot();
//Sets X axis sub-legends
SubCategoryAxis subCat = new SubCategoryAxis("Leak meters");
//Adds second dataset
plot.setDataset(1, data.dots);
//Defines level renderer
LevelRenderer renderer1 = new LevelRenderer();
renderer1.setSeriesPaint(0, Color.black);
plot.setRenderer(1, renderer1);
//Sets Y axis as %
((StackedBarRenderer) plot.getRenderer()).setRenderAsPercentages(true);
//Sets colors
((StackedBarRenderer) plot.getRenderer()).setSeriesPaint(0, new Color(0, 0, 0, 0)); //Transparent for start
((StackedBarRenderer) plot.getRenderer()).setSeriesPaint(1, Color.gray); //Grey low
((StackedBarRenderer) plot.getRenderer()).setSeriesPaint(2, Color.green); //Green
((StackedBarRenderer) plot.getRenderer()).setSeriesPaint(3, Color.gray); //Grey high
((StackedBarRenderer) plot.getRenderer()).setSeriesPaint(4, new Color(0, 0, 0, 0)); //Transparent for end
((StackedBarRenderer) plot.getRenderer()).setDrawBarOutline(true);
((StackedBarRenderer) plot.getRenderer()).setBaseOutlinePaint(Color.black);
//Setup which items not to see in legend
((StackedBarRenderer) plot.getRenderer()).setSeriesVisibleInLegend(0, false);
((StackedBarRenderer) plot.getRenderer()).setSeriesVisibleInLegend(3, false);
((StackedBarRenderer) plot.getRenderer()).setSeriesVisibleInLegend(4, false);
//Sets renderer & axis
plot.setDomainAxis(subCat);
//Changes plot render sequence so that bars are in the background and shapes in front
chart.getCategoryPlot().setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
//Sets margins between bars
chart.getCategoryPlot().getDomainAxis().setCategoryMargin(0.5f);
return chart;
}
This is the default behaviour of the GradientBarPainter which is used to draw the bars: when the bar is transparent, it doesn't draw the outline.
You can instead use the StandardBarPainter which will draw the outline.
Just add:
StackedBarRenderer renderer = (StackedBarRenderer) plot.getRenderer();
renderer.setBarPainter(new StandardBarPainter());
Another solution is to keep the GradientBarPainter but use an almost transparent color for the bar:
setSeriesPaint(0, new Color(0, 0, 0, 1));