Classic Chart getting Null Paint Exception in X-Trace and Mark modes

I am currently using the Classic Chart’s configureChart() function to change the color of the data points if it is outside of a user-defined spec limit. It is currently working as expected.

However, when I go into X-Trace or Mark mode, I get this error

java.lang.IllegalArgumentException: Null 'paint' argument.
	at org.jfree.chart.annotations.XYTextAnnotation.setPaint(XYTextAnnotation.java:222)
	at com.inductiveautomation.factorypmi.application.components.chart.runtime.AutoAnnotateXYPlot.draw(AutoAnnotateXYPlot.java:634)
	at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1227)
...

This is the code I am using in my configureChart()

	from org.jfree.chart.renderer.xy import XYLineAndShapeRenderer
	from java.awt import Color
	
	specData = self.SpecData
	
	if specData.getRowCount() == 0:
		rend = XYLineAndShapeRenderer()
	else:
		highVal = specData.getValueAt(0, 1)
		lowVal = specData.getValueAt(0, 2)
		class myRenderer(XYLineAndShapeRenderer):
			def getItemPaint(self, row, column):
				val = self.getPlot().getDataset(0).getYValue(row, column)
				if val > highVal or val < lowVal:
					return Color.red
				else:
					return Color.blue
		rend = myRenderer()	
		
	chart.getXYPlot().setRenderer(rend)

Is there something I am missing in my overridden getItemPaint() method to cause it to send a null value?

Update: I have tried testing getItemPaint() by having it always return Color.blue. This still causes the null paint exception. When I don’t override the getItemPaint() method at all, this exception does not happen. Am I not allowed to override this method? What could possibly be happening on Ignition’s backend to change that value to null?

What exact version are you using? Your line numbers don’t match up with mine in the current codebase, so I can’t see exactly which path we’re following to end up sending a null.

What’s happening, broadly, is that we’re trying to send a color to the annotation, and the annotation itself (vanilla JFreeChart code) is guarding against a null paint.

I am using 8.0.12.

This is our code that’s ultimately returning null and assigning it to the annotation object:
image

It looks like the call getSeriesPaint on your subclassed renderer is where the null is coming from. Try overriding getSeriesPaint in your subclass as well.

1 Like

This works, thank you so much!

Can someone expand on this and show where in the @Zachary_Zipper's you would need to override getSeriesPaint? I'm assuming you make another definition like below:

def getSeriesPaint(self, row, column)

When I did that, it says it needs three arguments and only 2 given, example below:

def getSeriesPaint(self, row, column):
	valx = plot.getDataset().getXValue(row, column)
	valy = plot.getDataset().getYValue(row, column)
	if valx > 6:
		return Color.RED
	else:
		return Color.GREEN

getSeriesPaint would accept self and one additional argument series (an integer). Not as convenient as a row/column.

I’m having trouble on constructing the code for this. For simplicity, I’m working with just a single series, so zero (0) is what I’m assuming I can use.

Ok, so it seems it was as simple as writing this:

def getSeriesPaint(self, series):
	return Color.BLACK

It didn’t matter what I put in the 2nd argument as long as it had a string. It doesn’t accept an integer itself. What this did was return the color to the trace or mark label font.