Display X Value as Label on Easy Chart

Is there a way to display the x value as a label for each point on an easy chart? I have a tag history dataset, and I want to display the date by each point so the operators can print the chart and have the dates of the points readily available. I have seen some posts on the StandardXYItemLabelGenerator from jfree.charts, but have had no luck implementing. Is there another way, or does anyone have a more comprehensive example? I tried the below code snippet from this topic, but I cant get it to do anything. I am sure I am leaving something out, but can not seem to figure out what. Thank you in advance!

def configureChart(self, chart):
	from org.jfree.chart.labels import StandardXYItemLabelGenerator
	
	class MyLabelGenerator(StandardXYItemLabelGenerator):
		def generateLabel(self, xyds, series, item):
			return "s%dp%d" % (series, item)
	
	renderer = chart.plot.renderer
	renderer.setItemLabelGenerator(MyLabelGenerator())
	renderer.setBaseItemLabelsVisible(True)

Have you considered using xyTextAnnotations? There are quite a few examples here in the forum of how to implement them. It would probably be easier to control on an easy chart which can have a data point for each visible pixel along the domain axis.

1 Like

Thank you for the suggestions, I was able to modify one of your other forum posts to get it working

	from org.jfree.chart.annotations import XYTextAnnotation
	plot = chart.getPlot()
	for annotation in plot.getAnnotations():
		if isinstance(annotation, XYTextAnnotation):
			plot.removeAnnotation(annotation)

	labelData = self.parent.getComponent('Power Table').data
	for row in range(0, labelData.rowCount):
		print row
		text = str(labelData.getValueAt(row, 0))
		startTime = float(labelData.getValueAt(row, 0).getTime())
		x = startTime
		y = labelData.getValueAt(row, 1) + 0.05
		annotation = XYTextAnnotation(text, x, y)
		plot.addAnnotation(annotation)

I had to stick the data in a PowerTable component and loop through it to put the dates at the points of the Easy Chart. Is there a way to just iterate through the points already on the Easy Chart? I can make the above work, but would prefer to not have to hide another component on the screen unless necessary.

When I need this sort of thing, I usually just add a custom property to the parent container.
Example:
• Right click on the parent container and select "Customizers --> Custom Properties"

• Click the plus icon on the left, give the new property an intuitive name, and in this case, select the dataset datatype:

Result:
This eliminates the need for a hidden component, and the labelData = code becomes simply:

labelData = self.parent.labelData

The path to a jfree chart's xyPlot dataset is simply chart.plot.dataset, but for what you're wanting, populating the desired labels directly seems simpler and more maintainable. I would probably just switch from a power table to a custom property to get rid of the hidden component and otherwise keep using your current approach.

I added the custom dataset property and it works great, thank you! My next issue is that I can only see the labels in Designer, not in Vision. I added some print statements in my configureChart function and when running in vision, it appears to be firing before the data has been received(my rowcount = 0). Should the label creating code not live in configureChart routine? I tried moving it out to a property change event handler, but then I realized I dont know how to get the jfreechart chart object that is easily accessible in the configureChart routine. Is there a way to trigger the configureChart method after the data is loaded?

It's always been this:

easyChart.getComponent(0).getComponent(0).chart

It absolutely should, and I think you're making it far more complicated than it needs to be.

See:

I understand that post is from quite a while ago, and different version of the software, but it should still apply.

Try this code:

def configureChart(self, chart):
	from org.jfree.chart.labels import StandardXYItemLabelGenerator
	from java.text import DateFormat
	from java.text import NumberFormat
	
	renderer = chart.plot.renderer
	xFormat = DateFormat.getDateTimeInstance()
	yFormat = NumberFormat.getNumberInstance()
	renderer.setItemLabelGenerator(StandardXYItemLabelGenerator('{1}',xFormat,yFormat))
	renderer.setBaseItemLabelsVisible(True)

Note, it won't take many data points for this to become unreadable. You can of course provide a more granular format for the date format.

This would be my biggest concern for this approach with the easy chart.

I tried the code snippet in place of what I had in the configureChart routine for my Easy Chart, and i get nothing! My data points still show up, but no labels! I am sure I am missing something but can't figure out what! If this would work it would be great. These data points are only logged once every 3 days, so it wouldn't be messy at all.

How are you populating your dataset?

I drag tags out of the tree and drop them on the easy chart, then i save the page so the pens stay on there permanently.

Can you please show how this code looks in the configure chart event. And what the tagPens data looks like?

Of course.


sample of what the data will look like:

Current chart config lrose sent that I cant get to work:

Chart config that works in designer but not vision

I still don't see how the labelData dataset property is being populated

Originally it was a dropped pen on the chart, then I set up a custom dataset property on the container(dataForLabels). I bound it to the taghistory of my test tag, and then read that in the chartconfig as

labelData = self.parent.dataForLabels

. Sorry, I forgot I set that up yesterday. Below is a picture of the data bound to the custom property dataForLabels.

How is the pen configured on the Easy Chart

The pen is set up in the Chart Config-Tag Pens dataset:

image

Okay, the issue appears to be that you are using the Dot Renderer, and it appears that for whatever reason, the dot renderer does not honor the isLabelItemVisible attribute.

Try this:

def configureChart(self, chart):
	from org.jfree.chart.labels import StandardXYItemLabelGenerator
	from org.jfree.chart.renderer.xy import XYDotRenderer
	from java.text import DateFormat
	from java.text import NumberFormat
	
	class XYLabeledDotRenderer(XYDotRenderer):
		def drawItem(self,g2,state,dataArea,info,plot,domainAxis,rangeAxis,dataset,series,item,crosshairState,p):
			
			self.super__drawItem(g2,state,dataArea,info,plot,domainAxis,rangeAxis,dataset,series,item,crosshairState,p)
			orientation = plot.orientation
			x = dataset.getXValue(series,item)
			y = dataset.getYValue(series,item)
			xAxisLocation = plot.domainAxisEdge
			yAxisLocation = plot.rangeAxisEdge
			transX1 = domainAxis.valueToJava2D(x,dataArea,xAxisLocation)
			transY1 = rangeAxis.valueToJava2D(y,dataArea,yAxisLocation)
			
			if self.isItemLabelVisible(series,item):
				self.drawItemLabel(g2,orientation,dataset,series,item,transX1,transY1,(y<0.0))
		
		
	if isinstance(chart.plot.renderer, XYDotRenderer):
		chart.plot.renderer = XYLabeledDotRenderer()

	renderer = chart.plot.renderer
	xFormat = DateFormat.getDateTimeInstance()
	yFormat = NumberFormat.getNumberInstance()
	renderer.setItemLabelGenerator(StandardXYItemLabelGenerator('{1}',xFormat,yFormat))
	renderer.setBaseItemLabelsVisible(True)

What this does is creates a class XYLabeledDotRenderer, which extendes XYDotRenderer. This class provides the added logic to print the labels.

It will only replace the renderer if the current renderer is an instance of XYDotRenderer.

The Area Renderer has a similar issue.

Otherwise the code should work for any renderer that you select.

If needed, it doesn't take much effort to use a similar method to insure that you only print a lable when the data changes, you would just need to know what type of renderer you were going to use in advance. This would help with keeping the chart looking clean.

That worked, thank you so much! I would never have figured that out.