Change each bar width in histogram chart

I played around with this a little more this morning, and came up with a more flexible approach to changing the tick labels.

First, I added the following method to the beginning of the configureChart extension function to hide the stock labels and create a set of new labels to take their place:

	def dynamicLabelCreator(self, chart, rawData):
		domainAxis = chart.getCategoryPlot().getDomainAxis()
		for row, category in enumerate(chart.getCategoryPlot().getCategories()):
			for component in self.getComponents():
					if isinstance(component, PMILabel) and component.name == category:
						self.remove(component)
			domainAxis.setTickLabelPaint(category, Color(0,0,0,0))
			customLabel = PMILabel()
			labelName = category
			customLabel.setName(labelName)
			customLabel.setVisible(True)
			self.add(customLabel)
	rawData = self.rawData
	dynamicLabelCreator(self, chart, rawData)

Then, I set the stock labels to a 90 degree orientation to give myself more room at the bottom of the chart, and while experimenting, I found that the bars rendered better if I set the lower margin of the category axis to .1

Finally, I modified the calculateW0 method to set the labels based on the center of the bar minus 1/2 the width of the label. Initially, the result looked good, but as I added more bars, the labels began to overlap:


Consequently, I reduced the number of line breaks, and changed the rotation of the labels. I also decided to go ahead and employ an innate label generator to put the actual values on top of the bars:

Below is the full code. I am certain this will still require quite a bit of tweaking, changing, and refactoring to produce an acceptable product for your usage case, but there should be more than enough directions here to go off of. Good Luck!

Full Code:
def configureChart(self, chart):
	from com.inductiveautomation.factorypmi.application.components import PMILabel
	from java.awt import Color
	from org.jfree.chart.renderer.category import BarRenderer, StandardBarPainter
	from java.text import NumberFormat
	from org.jfree.chart.labels import StandardCategoryItemLabelGenerator
	def dynamicLabelCreator(self, chart, rawData):
		domainAxis = chart.getCategoryPlot().getDomainAxis()
		for row, category in enumerate(chart.getCategoryPlot().getCategories()):
			for component in self.getComponents():
					if isinstance(component, PMILabel) and component.name == category:
						self.remove(component)
			domainAxis.setTickLabelPaint(category, Color(0,0,0,0))
			customLabel = PMILabel()
			labelName = category
			customLabel.setName(labelName)
			customLabel.setVisible(True)
			customLabel.rotation = 290
			self.add(customLabel)
	rawData = self.rawData
	dynamicLabelCreator(self, chart, rawData)	
	chart.setTitle("Histogram")
	data = self.data
	padding = self.width * .25
	width = self.width - padding
	maxTimeValue = data.getRowCount()-1
	chartStartTime = data.getValueAt(0,0)
	chartEndTime = data.getValueAt(maxTimeValue,0)
	totalTime = system.date.minutesBetween(chartStartTime,chartEndTime)
	timeRatio = float(width)/float(totalTime)
	scaleRatio = float(self.width)*0.018
	class DynamicWidthRenderer(BarRenderer):
		def	calculateBarW0(BarRenderer, plot, orientation, dataArea, domainAxis, state, row, column):
			from com.inductiveautomation.factorypmi.application.components import PMILabel
			barStartTime = data.getValueAt(column,0)
			barStartingLocation = float(system.date.minutesBetween(chartStartTime, barStartTime))*timeRatio + padding * .5
			customLabel = self.getComponent(column)
			try:
				barEndTime = data.getValueAt((column + 1), 0)
				barScaleParameter = float(system.date.minutesBetween(barStartTime, barEndTime))*scaleRatio
				customLabel.text = '<html><b><center>WO# ' + str(self.rawData.getValueAt(column,1)) + '<br>' + str(system.date.format(barStartTime, "MM/dd HH:mm:ss")) + '<br>to ' + str(system.date.format(barEndTime, "HH:mm:ss"))
				labelOffset = customLabel.width * .5
				labelLocation = barStartingLocation+((float(system.date.minutesBetween(barStartTime, barEndTime))*.5)*timeRatio)-labelOffset
			except:
				barScaleParameter = float(width + padding - barStartingLocation)*scaleRatio
				customLabel.text = '<html><b><center>WO# ' + str(self.rawData.getValueAt(column,1)) + '<br>' + str(system.date.format(barStartTime, "MM/dd HH:mm:ss")) + '<br>to ' + '[...]'
				labelOffset = customLabel.width * .5
				labelLocation = barStartingLocation - (.5 * labelOffset)
			heightJustification = float(len(chart.getCategoryPlot().getCategories()[column]))*9
			customLabel.setLocation(int(labelLocation), self.height - int(heightJustification))
			customLabel.setSize(250,250)		
			dataArea.width = barScaleParameter
			BarRenderer.calculateBarWidth(plot, dataArea, 0, state)
			return barStartingLocation			
		def getItemPaint(BarRenderer, row, column):
			if column % 2 == 0:
				return Color.blue
			else:
				return Color.yellow
	chart.getCategoryPlot().setRenderer(DynamicWidthRenderer())
	numberFormat = NumberFormat.getInstance()
	defaultFormat = StandardCategoryItemLabelGenerator.DEFAULT_LABEL_FORMAT_STRING
	labelGenerator = StandardCategoryItemLabelGenerator(defaultFormat, numberFormat)
	chart.getCategoryPlot().getRenderer().setBaseItemLabelGenerator(labelGenerator)
	chart.getCategoryPlot().getRenderer().setBaseItemLabelsVisible(True)
	chart.getCategoryPlot().getRenderer().setBarPainter(StandardBarPainter())
	chart.getCategoryPlot().getRenderer().setShadowVisible(False)
1 Like