Hello,
Is there a way I can get two X trace on easy chart? I need to see two values on two time stamp at the same time.
I am sure someone must have done this in past. Let me know.
Thank you.
Hello,
Is there a way I can get two X trace on easy chart? I need to see two values on two time stamp at the same time.
I am sure someone must have done this in past. Let me know.
Thank you.
No, sorry. It has been on my to-do list for my NoteChart module for ages.
Well, this is Vision, so just about anything that is not included with the module is theoretically possible if you are willing to build it yourself. The only question is how deep down the rabbit hole a developer is willing to go to get the result they want.
In this case, the crosshair could be created with a value marker, and the labels could be created with xy text annotations. If you only ever need two x traces at the same time, then I would create a custom property on the parent container of the easy chart to store the current xtrace crosshair location and associated properties. Then, anytime the crosshair is moved, use the data to create a second crosshair in the old location before replacing the data with the current crosshair location. Messing around with this from the mouse clicked event handler of the easy chart, I was able to achieve this result:
To do this, I created custom property with a dataset datatype on the parent container called easyChartXTrace
:
Here is the script I developed for the mouseClicked event handler:
# If this is a left click
if event.button == 1:
# Get the jfreechart [panel] and its plot
# event.source.getComponent(0).getComponent(0) is the way most people do this, but I'm in the habit of not trusting subcomponent heiarchies to be static
chartField = event.source.getClass().getDeclaredField('chart')
chartField.setAccessible(True)
easyChart = chartField.get(event.source)
plot = easyChart.chart.plot
# If the chart is in XTrace mode:
if plot.annotationMode == 2:
# If the easyChartXTrace dataset exists, add the crosshair at the location the dataset provides:
if event.source.parent.easyChartXTrace:
event.source.addSecondaryCrosshair(plot)
# Get the data for the current xtrace location,
# ...and save it to the easyChartXTrace custom property on the parent container
event.source.setCrosshairData(easyChart, plot)
else:
# If the chart is not in xtrace mode, delete any previous xtrace position data
event.source.parent.easyChartXTrace = None
If there is a left click and the chart is in xtrace mode, the mouse clicked event handler calls on two custom methods that I added to the easy chart.
Note the required parameters for each one:
Here is the setCrosshairData
script:
#def setCrosshairData(self, easyChart, plot):
# Get the xtrace crosshair location
chartX = plot.domainCrosshairValue
# Gtet the bounds for the default range axis
displayedUpperBound = plot.rangeAxis.upperBound
displayedLowerBound = plot.rangeAxis.lowerBound
# Create an empty list and headers for the easyChartXTrace custom property dataset
data = []
headers = 'X', 'Y', 'TEXT', 'COLOR'
# Iterate through each plot dataset
# There will be a seperate dataset for each range axis that is displayed in the chart
for datasetIndex in range(plot.getDatasetCount()):
dataset = plot.getDataset(datasetIndex)
# Get the upper and lower bounds for the current dataset's range axis
# for scaling purposes
rangeAxis = plot.getRangeAxisForDataset(datasetIndex)
upperBound = rangeAxis.upperBound
lowerBound = rangeAxis.lowerBound
# Locate the relevent item in the dataset for the crosshair location and retrieve the y value
for seriesIndex in range(dataset.seriesCount):
# Grab the pen name from the dataset and iterate through get the pen color from whichever pen dataset the chart is using
penName = dataset.getSeriesKey(seriesIndex)
tagPens = self.tagPens # Change this to the relevent dataset if needed
for row in range(tagPens.rowCount):
if tagPens.getValueAt(row, 'NAME') == penName:
color = tagPens.getValueAt(row, 'COLOR')
for itemIndex in range(dataset.getItemCount(seriesIndex)):
if chartX == dataset.getXValue(seriesIndex, itemIndex):
yValue = dataset.getYValue(seriesIndex, itemIndex)
# Scale the y value, so it is displayed correctly on the default range axis
yPercent = (yValue - lowerBound) / (upperBound - lowerBound)
scaledYValue = displayedLowerBound + yPercent * (displayedUpperBound - displayedLowerBound)
# Convert the annotation to a string, and limit it to two decimal places
annotationText = "%s: %f" % (penName, yValue)
# Add all the collected data as a row to the dataset data and stop iterating through the item indexes
data.append([chartX, scaledYValue, annotationText, color])
break
# Create a dataset with the relevent data, and assign it to the parent container's easyChartXTrace custom property
self.parent.easyChartXTrace = system.dataset.toDataSet(headers, data)
Here is the addSecondaryCrosshair
script:
#def addSecondaryCrosshair(self, plot):
# Import the stuff needed to make this happen
from org.jfree.chart.plot import ValueMarker
from org.jfree.chart.annotations import XYTextAnnotation
from java.awt import BasicStroke, Font
# Get the dataset and the crosshair location
dataset = self.parent.easyChartXTrace
chartX = dataset.getValueAt(0, 'X')
# Clear any existing marker and annotations
plot.clearDomainMarkers()
plot.clearAnnotations()
# Dashed Crosshiar Stroke
boldDashed = BasicStroke(
1.0, # Width of the line
BasicStroke.CAP_ROUND, # Line cap style
BasicStroke.JOIN_ROUND, # Line join style
1.0, # Miter Limit
[2.0, 2.0], # Dash Pattern (2 pixels on, 2 pixels off)
0.0 # Dash Phase
)
# Set the crosshair and its properties
crosshair = ValueMarker(chartX)
crosshair.setStroke(boldDashed)
crosshair.setPaint(system.gui.color('black'))
plot.addDomainMarker(crosshair)
# Adjust the font size of the annotations
annotationFont = Font(Font.MONOSPACED, Font.PLAIN, 13)
# Iterate through the dataset, and use the row values to set the annotations
for row in range(dataset.rowCount):
chartY = dataset.getValueAt(row, 'Y')
annotationText = dataset.getValueAt(row, 'TEXT')
color = dataset.getValueAt(row, 'COLOR')
# Create a border effect with an underlaid annotation
borderAnnotation = XYTextAnnotation(u'█' * len(annotationText), chartX, chartY)
borderAnnotation.setFont(annotationFont) # Slightly larger font size
borderAnnotation.setPaint(color)
plot.addAnnotation(borderAnnotation)
# Underlay the annotation text with the plot background, so the pen lines don't make the letters hard to see
backgroundAnnotation = XYTextAnnotation(u'█' * len(annotationText), chartX, chartY)
backgroundAnnotation.setPaint(self.plotBackground)
backgroundAnnotation.setFont(annotationFont)
plot.addAnnotation(backgroundAnnotation)
# Set the actual annotation
annotation = XYTextAnnotation(annotationText, chartX, chartY)
annotation.setPaint(color)
annotation.setFont(annotationFont)
plot.addAnnotation(annotation)