Syntax to change color of report time series chart legend

I have a time series chart in Ignition 8.1

I can change the limit line color from the default red to green using the following script.

Limit = data['tagPath1_ReportLimit']
green = Color(0, 255, 0)
stroke = BasicStroke(2.0)

markerLimit = ValueMarker(Limit, green, stroke)

chart.getPlot().addRangeMarker(markerLimit)

But what is the syntax to change the limit line color shown in the legend?

I'm assuming the limit in the legend is a fake item you're inserting, since the value marker is what's actually drawing the green line?

You could probably work off the example in this post to customize the rendered legend item; you won't need all this but it should get you started:

Heh heh, I have been trying to work off that example actually but not having any luck. The legend item is "fake" in the sense that I generate the "limit line" from a tag property.

This is my full script that gets called:

def gwConfigureChart(data, chart):
	"""Adds a limit line to a report chart."""
	from java.awt import Color
	from java.awt import BasicStroke;
	from org.jfree.chart.plot import ValueMarker
	import math
	
	plot = chart.getXYPlot()
	
	dataset = plot.getDataset()
	
	lowerBound = chart.getPlot().getRangeAxis().getLowerBound()
	upperBound = chart.getPlot().getRangeAxis().getUpperBound()
	
	Limit = data['tagPath1_ReportLimit']
	ReportDataAboveLimit = data['tagPath1_ReportDataAboveLimit']
	
	red 	= Color(255, 	0, 		0)
	green 	= Color(0, 		255, 	0)
	
	stroke = BasicStroke(2.0)
	
	if Limit <= lowerBound:
		chart.getPlot().getRangeAxis().setLowerBound(Limit - math.floor(Limit * 0.05))
		
	if Limit >= upperBound:
		chart.getPlot().getRangeAxis().setUpperBound(Limit + math.ceil(Limit * 0.05))
	
	if ReportDataAboveLimit:
		if data['tagPath1_MinValue'] > Limit:
			markerLimit = ValueMarker(Limit, green, stroke)
		else:
			markerLimit = ValueMarker(Limit, red, stroke)
	else:
		if data['tagPath1_MaxValue'] > Limit:
			markerLimit = ValueMarker(Limit, red, stroke)
		else:
			markerLimit = ValueMarker(Limit, green, stroke)

	chart.getPlot().addRangeMarker(markerLimit)
	
	plot.getRangeAxis().setLabel(data['tagPath1_ReportYAxisTitle'])

which generates this:

and the modified code:

def gwConfigureChart(data, chart):
	"""Adds a limit line to a report chart."""
	from java.awt import Color
	from java.awt import BasicStroke;
	from org.jfree.chart.plot import ValueMarker
	import math
	
	## modified
	from org.jfree.chart.renderer.xy import XYLineAndShapeRenderer
	from org.jfree.chart import LegendItem
	legend = chart.getLegend()
	plot = chart.getPlot()
	renderer = plot.getRenderer()
	
	class CustomRenderer(XYLineAndShapeRenderer):
		def getLegendItem(self, dataset, series):
			original = renderer.getLegendItem(dataset, series)
			new = LegendItem(
				original.getLabel(),
				original.getDescription(),
				original.getToolTipText(),
				"", #urlText
				original.getShape(),
				original.getLinePaint(), #set fill paint to line paint
				original.getOutlineStroke(),
				original.getOutlinePaint()
			)
			new.setSeriesIndex(series)
			new.setDatasetIndex(dataset)
			return new
		
		def getLegendItems(self):
			collection = XYLineAndShapeRenderer.getLegendItems(self)
			for ds in range(plot.getDatasetCount()):
				for series in range(plot.getSeriesCount()):
					legend = self.getLegendItem(ds, series)
					collection.add(legend)
			return collection

	dataset = plot.getDataset()
	
	lowerBound = chart.getPlot().getRangeAxis().getLowerBound()
	upperBound = chart.getPlot().getRangeAxis().getUpperBound()
	
	Limit = data['tagPath1_ReportLimit']
	ReportDataAboveLimit = data['tagPath1_ReportDataAboveLimit']
	
	red 	= Color(255, 	0, 		0)
	green 	= Color(0, 		255, 	0)
	
	stroke = BasicStroke(2.0)
	
	if Limit <= lowerBound:
		chart.getPlot().getRangeAxis().setLowerBound(Limit - math.floor(Limit * 0.05))
		
	if Limit >= upperBound:
		chart.getPlot().getRangeAxis().setUpperBound(Limit + math.ceil(Limit * 0.05))
	
	if ReportDataAboveLimit:
		if data['tagPath1_MinValue'] > Limit:
			markerLimit = ValueMarker(Limit, green, stroke)
		else:
			markerLimit = ValueMarker(Limit, red, stroke)
	else:
		if data['tagPath1_MaxValue'] > Limit:
			markerLimit = ValueMarker(Limit, red, stroke)
		else:
			markerLimit = ValueMarker(Limit, green, stroke)

	chart.getPlot().addRangeMarker(markerLimit)
	
	plot.getRangeAxis().setLabel(data['tagPath1_ReportYAxisTitle'])
	
	newRenderer = CustomRenderer(True, False)
	newRenderer.setBaseSeriesVisibleInLegend(True)
	legend.setSources([newRenderer])

which currently generates the same thing.

I don't really understand what a renderer is or how to use it appropriately [obviously].

Any help is appreciated.

Try (this is totally untested) poking in a fake LegendItem to the LegendItemCollection that's being returned in getLegendItems:

	red 	= Color(255, 	0, 		0)
	green 	= Color(0, 		255, 	0)
	
	class CustomRenderer(XYLineAndShapeRenderer):
		def getLegendItem(self, dataset, series):
			original = renderer.getLegendItem(dataset, series)
			new = LegendItem(
				original.getLabel(),
				original.getDescription(),
				original.getToolTipText(),
				"", #urlText
				original.getShape(),
				original.getLinePaint(), #set fill paint to line paint
				original.getOutlineStroke(),
				original.getOutlinePaint()
			)
			new.setSeriesIndex(series)
			new.setDatasetIndex(dataset)
			return new
		
		def getLegendItems(self):
			collection = XYLineAndShapeRenderer.getLegendItems(self)
			for ds in range(plot.getDatasetCount()):
				for series in range(plot.getSeriesCount()):
					legend = self.getLegendItem(ds, series)
					collection.add(legend)

			### add a new, fake legend item
			collection.add(LegendItem(
				"Limit",
				None,
				None,
				None,
				collection[0].shape
				red,
				collection[0].outlineStroke,
				collection[0].outlinePaint
			))

			return collection

Basically, you're convincing the chart to render an additional item it doesn't have.

Disclaimer: The easier path forward at this point might be to just have a static value dataset (created by script in your report data config) that you just attach to the same chart, no need to get so fancy in configureChart.

Well I tested it and no luck.

def gwConfigureChart(data, chart):
	"""Adds a limit line to a report chart."""
	from java.awt import Color
	from java.awt import BasicStroke;
	from org.jfree.chart.plot import ValueMarker
	import math
	
	## modified
	from org.jfree.chart.renderer.xy import XYLineAndShapeRenderer
	from org.jfree.chart import LegendItem
	legend = chart.getLegend()
	plot = chart.getPlot()
	renderer = plot.getRenderer()
	
	red 	= Color(255, 	0, 		0)
	green 	= Color(0, 		255, 	0)
	
	class CustomRenderer(XYLineAndShapeRenderer):
		def getLegendItem(self, dataset, series):
			original = renderer.getLegendItem(dataset, series)
			new = LegendItem(
				original.getLabel(),
				original.getDescription(),
				original.getToolTipText(),
				"", #urlText
				original.getShape(),
				original.getLinePaint(), #set fill paint to line paint
				original.getOutlineStroke(),
				original.getOutlinePaint()
			)
			new.setSeriesIndex(series)
			new.setDatasetIndex(dataset)
			return new
		
		def getLegendItems(self):
			collection = XYLineAndShapeRenderer.getLegendItems(self)
			for ds in range(plot.getDatasetCount()):
				for series in range(plot.getSeriesCount()):
					legend = self.getLegendItem(ds, series)
					collection.add(legend)
			
			### add a new, fake legend item
			collection.add(LegendItem(
				"Limit",
				None,
				None,
				None,
				collection.get(0).shape,
				red,
				collection.get(0).outlineStroke,
				collection.get(0).outlinePaint
			))
			
			return collection

	dataset = plot.getDataset()
	
	lowerBound = chart.getPlot().getRangeAxis().getLowerBound()
	upperBound = chart.getPlot().getRangeAxis().getUpperBound()
	
	Limit = data['tagPath1_ReportLimit']
	ReportDataAboveLimit = data['tagPath1_ReportDataAboveLimit']
	
	
	
	stroke = BasicStroke(2.0)
	
	if Limit <= lowerBound:
		chart.getPlot().getRangeAxis().setLowerBound(Limit - math.floor(Limit * 0.05))
		
	if Limit >= upperBound:
		chart.getPlot().getRangeAxis().setUpperBound(Limit + math.ceil(Limit * 0.05))
	
	if ReportDataAboveLimit:
		if data['tagPath1_MinValue'] > Limit:
			markerLimit = ValueMarker(Limit, green, stroke)
		else:
			markerLimit = ValueMarker(Limit, red, stroke)
	else:
		if data['tagPath1_MaxValue'] > Limit:
			markerLimit = ValueMarker(Limit, red, stroke)
		else:
			markerLimit = ValueMarker(Limit, green, stroke)

	chart.getPlot().addRangeMarker(markerLimit)
	
	plot.getRangeAxis().setLabel(data['tagPath1_ReportYAxisTitle'])
	
	newRenderer = CustomRenderer(True, False)
	newRenderer.setBaseSeriesVisibleInLegend(True)
	legend.setSources([newRenderer])
	

Resulting error:

17:25:24.041 [AWT-EventQueue-0] WARN reporting.TimeseriesChart - Error drawing component.
org.python.core.PyException: TypeError: 'org.jfree.chart.LegendItemCollection' object is unsubscriptable

and the timeseries chart is now blank.

Yes, I think I'll take a look at inserting a static dataset.

Try swapping these for .get(0), as in collection.get(0).shape and so on; should fix that immediate error.

I got my Kotlin and Python mixed up :smile:

1 Like

Hey, it did something. Thanks for the help.

1 Like