Jfree chart - click on rendered data point

Dear folks,

I have a requirement as follows:

When clicked on plotted data point, A popup should open for the user to tag that point with some comment.

Also need to fetch details of that particular x,y co-ordinates to store in database along with user comment.

If anyone hava a solution already, please help me.

Regards,
Saravanan.

@pturmel can confirm, but this sounds a lot like what his NoteChart module offers:
https://inductiveautomation.com/moduleshowcase/module/automation-professionals-llc-notechart

It doesn’t have any pre-built popup–you’d do that yourself. But it does expose all of the pen data for a particular X-trace point, and shows annotations (comments) as markers on the chart (drawn from a db, typically). The pop-up would take the timestamp and any user data and save it to the source for the annotations.

Hi pturmel,

Is it possible to use jfree chart’s method to bring a popup (I have pre built the popup screen) when user clicks on plotted data?

Since I am not familiar with jfree chart scripting, If possible can you guide me on jfree chart scripting.

(Or)

should I only use notechart module?

Thanks and regards,
Saravanan.

JFreeChart doesn’t have any scripting of its own. All of the scripting possibilities you see in Ignition were added by Ignition’s components or by third-party components (like mine). I recommend NoteChart, of course, because I wrote it. I might be a bit biased. But I would consider the feature you wish to implement as rather difficult without my module.

You will need to implement a customChartMouseListener class and add it to the ChartPanel. This will give you the access to both Ignition’s nav methods and JFreeCharts methods for getting the data. This isn’t a particularly straight forward process, and you may need to dig around in the JFreeChart api to accomplish your true end goal.

This should be enough to get you started. Place this in the configureChart extension function of the Chart component. Note, that this is written so that only clicking on the data point itself will open the window. It is possible to write the click event in a way that it will calculate the closest data point, but I haven’t done this here.

#NOTE: self is a reference to the invoking component, in the case of a standard chart
#component this is an com.inductiveautomation.factorypmi.application.components.PMIChart
#which inherits from ChartPanel

from org.jfree.chart import ChartMouseListener

class CustomChartMouseListener(ChartMouseListener):
     def chartMouseClicked(self,e):
          xyItem = e.getEntity() #gets the data entity that was clicked, None if no entity was clicked
          if xyItem: #ensure that a data entity was clicked
               dataset = xyItem.getDataset()
               xValue = dataset.getXValue(xyItem.getSeriesIndex(),xyItem.getItem())
               yValue = dataset.getYValue(xyItem.getSeriesIndex(),xyItem.getItem())

               system.nav.openWindow('YourPopupWindowClass',{'yValueParameterName':xValue,'yValueParameterName':yValue})

self.addChartMouseListener(CustomChartMouseListener())

It should be noted that there may indeed be a better/cleaner way to do this, though I see no issues with doing it this way.

Also, while working in the designer if you make changes to the configureChart function script, another version of the mouse listener will be added to the chart’s mouseListener list, the previous listeners are not unregistered. You will need to close the display in the designer at which point the component will unregister the listener. This is important because should your change cause and error within the listener object that exception will continue to be thrown even after you have corrected the root cause in the code.

You should be wary that it is possible this code will potentially be called more than 1 time, so do not put any code in the listener that is dependent upon only running once (e.g. database update querys, tag writes, etc.). Also you should minimize the amount of scripting that takes place in the listener, because long running scripts here will lock your GUI.

You may also want to look into the XYAnnotation Subclasses for adding annotations to the Plot. Adding annotations to the Plot is not particularly simple, especially if you are storing them for pulling up later. But they are probably the best way to indicate to your users that a data point has additional information available for it.

4 Likes

This is a memory leak. IIRC, since configureChart is call often in an EasyChart, you may discover this blowing up in your face.

Thank you so much lrose. :slightly_smiling_face:

I will try this proposal and get back.

Looks like you could iterate through the mouse listeners and remove any others, though, so not impossible to circumvent.
http://www.jfree.org/jfreechart/api/javadoc/org/jfree/chart/ChartPanel.html#addChartMouseListener-org.jfree.chart.ChartMouseListener-

1 Like

Thanks PGriffith :slightly_smiling_face:

Will check this also

If possible, can you provide a snippet on how to remove the other listeners.
Thanks.

This should do the trick:

from org.jfree.chart import ChartMouseListener

class CustomChartMouseListener(ChartMouseListener):
     def chartMouseClicked(self,e):
          xyItem = e.getEntity() #gets the data entity that was clicked, None if no entity was clicked
          if xyItem: #ensure that a data entity was clicked
               dataset = xyItem.getDataset()
               xValue = dataset.getXValue(xyItem.getSeriesIndex(),xyItem.getItem())
               yValue = dataset.getYValue(xyItem.getSeriesIndex(),xyItem.getItem())

               system.nav.openWindow('YourPopupWindowClass',{'yValueParameterName':xValue,'yValueParameterName':yValue})

for listener in self.getListeners(ChartMouseListener):
     self.removeChartMouseListener(listener)

self.addChartMouseListener(CustomChartMouseListener())
4 Likes

Thanks lrose and PGriffith,

Very kind of you :slight_smile:

We are able to do it with your help.

Regards,
Saravanan.

1 Like


i am also using jfree stacked bar
instead of mouse click, i want dobule click option in jfree chart . please give me solution to replace my script with dobule click using jfree script

The mouseClicked event includes the clickCount. Check in your script if that equals two.

1 Like

yes its working but i want use event.source.cursorCode = 3 to show loading of data. which line i have to use this event.source.cursorCode = 3?

If you are running a task that is long enough you need to signal to the user that it is working, you should probably not be doing that on the GUI thread.

The script you show in your post will run so quickly that changing the cursor would not be visible to the user even if the GUI thread actually updated prior to completing the script.

1 Like

Ok thanks for the information


i have a stacked bar…

staked bar has different colors . when i select a color. i what to highlight the color outline - in order to show that is selected. i have attached my script also

is possible to do?

Yeah.

One way to do it is to create an annotation, it would be a little simpler if you were using an XYPlot as you could just use a XYShapeAnnotation, however, since you’re using a bar chart that is a CategoryPlot so you need to create a custom annotation. Still not extremely complicated but not exactly straightforward either.

Add the following to you’re configureChart extension function

from org.jfree.chart import ChartMouseListener
from org.jfree.chart.annotations import CategoryAnnotation
from java.awt import Color, BasicStroke

class CategoryShapeAnnotation(CategoryAnnotation):
	def __init__(self,newShape):
		self.shape = newShape

	def draw(self,g2,plot,dataArea,domainAxis,rangeAxis):
		#set the color that you want to highlight the item with
		#g2.setColor(Color.YELLOW) #to use a preset color constant
		g2.setColor(Color(1.0,1.0,0.0,1.0)) #to use a custom color with alpha
		#set the size of the stroke.
		g2.setStroke(BasicStroke(4.0))
		g2.draw(self.shape)
		
class CustomChartMouseListener(ChartMouseListener):
	def chartMouseMoved(self,e):
		pass
	def chartMouseClicked(self,e):
	    #verify that an entity was clicked on
		if e.getEntity():
			plot = e.getChart().getPlot()
			entityBounds = e.getEntity().getArea().getBounds2D()
			for a in plot.getAnnotations():
				#there should only ever be 1 annotation
				a.shape = entityBounds
				break
			else:
			    #if the annotation does not exist yet create it
				plot.addAnnotation(CategoryShapeAnnotation(e.getEntity().getArea().getBounds2D()))

for listener in self.getListeners(ChartMouseListener):
	self.removeChartMouseListener(listener)
	
self.addChartMouseListener(CustomChartMouseListener())

This will work for any categoryPlot.

3 Likes