Change colour of item in Chart

Hi,

I am trying to change the colour of a shapes in chart based on the y value. For some reason the method is executing all the time when invoked once.

class XYItemRenderer0(XYLineAndShapeRenderer):
			def getItemPaint(self,series,row):
				if series == 0 and plot0.getDataset(0).getYValue(series, row) > ucl: 
					return Color.RED
				else: 
					return Color.BLUE								
		
plot0.setRenderer(XYItemRenderer0()) 								
plot0.getRendererForDataset(plot0.getDataset(0)).setSeriesLinesVisible(0, 0)
plot0.getRendererForDataset(plot0.getDataset(0)).setSeriesShape(0, Rectangle2D.Double(-3, -3, 6, 6)) 

Could you please let me know where the mistake is.

Thanks,
Raj

2 Likes

Your class is XYItemRenderer1, but you are instantiating XYItemRenderer0? What is temp?

Apologies, its a copy paste mistake. I have corrected the script now.

When you say ‘when invoked once’, where are you invoking getItemPaint?

I would expect that this method (from the AbstractRenderer base) is called whenever the chart wants to know what paint to use to colour a chart item, and hence it will be called for each item, prior to the chart drawing that item.

Is it an issue that it is being called in such a manner?

Regards,
Bevan Weiss

I have a button. When I click on it, it refreshes the dataset and runs the above code. If I say, give a print statement inside that method, it keeps printing it all the time. Even after the chart is refreshed and completely loaded.

from java.awt import Color
from org.jfree.chart.renderer.xy import XYLineAndShapeRenderer
from java.awt.geom import Rectangle2D
chart = event.source.parent.getComponent('Chart').getChart()

ucl = 58.0
plot = chart.getPlot()

class MyRenderer(XYLineAndShapeRenderer):	
	def getItemPaint(self,series,row):
		if series == 0 and plot.getDataset(0).getYValue(series, row) > ucl: 
			return Color.RED
		else: 
			return Color.BLUE

renderer = MyRenderer()
renderer.setSeriesLinesVisible(0,False)
renderer.setSeriesShape(0, Rectangle2D.Double(-3, -3, 6, 6)) 
plot.setRenderer(renderer)
3 Likes

Java Swing (and thus, JFreeChart) will call paint() methods continuously as it draws other things. For this and other reasons, they should thus be extremely optimized. Your configureChart method (assuming that’s where you’re defining this) should only be called by Ignition when necessary to re-run your customizations, but getItemPaint will absolutely be called constantly, and there’s nothing you can do about that.

1 Like

Thank you for the clarification @PGriffith. Any other alternatives to achieve this?

Honestly, you’re pretty close.

Use code similar to what @jpark posted and add a column to your dataset to use as a flag. When you need to refresh the item which is being modified just set the proper flag in the dataset and your custom renderer will just take care of the rest.

All you need do is have the script check the flag column for each data point.

Note: This will allow multiple points to be formatted so if you only want 1 point to be changed you will need to insure that what ever method modifies the dataset does so correctly.

Hi @lrose, it’s a bit confusing for me to understand. If I add a another column, wouldn’t that be another series? Also if I check that flag inside the method, the program would still execute the getItemPaint() right! Is there no way to stop this execution!

Yes, it would be another series, however, because you are providing the renderer you can control this.

There is no way to stop the execution, so you have to write the code in a way that this is not in play. Create the renderer class in the configureChart Extension function and then let the chart code call the renderer when it needs to.

Add a Column of type Integer or Short (the chart will argue with you if you use another type such as boolean.)

Then place this code in your configureChart extension function. This code has been tested and does work in vision 7.9

	from java.awt import Color
	from org.jfree.chart.renderer.xy import XYLineAndShapeRenderer
	from java.awt.geom import Rectangle2D
	
	#grabs a reference to plot of the chart
	plot = chart.getPlot()
	
	class MyRenderer(XYLineAndShapeRenderer):	
		def getItemPaint(self,series,row):
			if series == 0 and plot.getDataset(0).getYValue(1, row) > 0: 
				return Color.RED
			elif series == 0: 
				return Color.BLUE
	
	#create an instance of your renderer			
	renderer = MyRenderer()
	#Force the colorFlag not to render
	renderer.setSeriesShapesVisible(1,False)
	renderer.setSeriesLinesVisible(1,False)
	
	#These lines modify the rendering of the data series
	renderer.setSeriesLinesVisible(0,False)
	renderer.setSeriesShape(0, Rectangle2D.Double(-3, -3, 6, 6)) 
	
	#set the renderer of the plot to your custom renderer
	plot.setRenderer(renderer)

Just a few modifications from what @jpark posted above, I added some comments to help explain what is happening. Notice that we are setting the lines and shapes of series 1 to not be visible, this keeps your added column from rendering to the chart.

In the getItemPaint we check the value of series 1 value for the given row, if it is greater than 0 then we choose a different color. If you remove the “elif” segment of the code then default color will be used for the series. Any row which has a positive non-zero value will paint with the modified color. This is why if you only ever want 1 point to have the modified color you need to insure that only 1 row has a positive non-zero number.

@lrose I very much like the way you explain it. Thank you for that :slight_smile:

I think I didn’t explain the issue clearly. I am able to compare the Y Value with a parameter (ucl) like below and the code works.

if series == 0 and plot.getDataset(0).getYValue(series, row) > ucl:

The issue is that getItemPaint is called constantly and it slows down the window. I was looking for a way to stop this or at least not to slow down the page.

Going back to what @PGriffith said. Are you setting the renderer for the plot anywhere outside of the configureChart extension function?

getItemPaint will be called constantly, so if that is slowing down your window, then perhaps the code needs refactored.

For instance you haven’t shared all of the code. How is ucl defined? Where is plot0 defined?

Thanks for explaining @lrose, I now get what you and @PGriffith saying. In regards to ucl and plot0 I use something like this. I dont have the exact script with me now.

plot0 = chart.getXYPlot()
ucl = plot0.getDataset(0).getYValue(2, 1)

Thank you very much for the clarification.

Got solution for my issue
for change the xy line renderer color based on the condition

used this script


if pvalue < 0.05:
 XYPlot plot = chart.getXYPlot();
  XYItemRenderer xyir = plot.getRenderer();
  xyir.setSeriesPaint(0, Color.RED);
  plot.setDataset(0, xyDataset1);