# Change each bar width in histogram chart

**URL:** <https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847>\
**Category:** Ignition\
**Tags:** vision, scripting\
**Created:** [November 23, 2022, 11:49am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847 "2022-11-23T11:49:56Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [November 30, 2022, 4:12pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/21 "2022-11-30T16:12:27Z")

</div>

Yes in my example chart time column is not used  
but right now i need to use the timeframe for bar width adjustment

Y axis is electric and gas percentage value will be ploted based on work order number

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [November 30, 2022, 4:23pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/22 "2022-11-30T16:23:23Z")

</div>

> [@prasath.t](#):
>
> Any example code please

This is a non-trivial chunk of code you are asking volunteers to write. They keep telling you to try to write it yourself, and then they can review. Show **your** code.

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [November 30, 2022, 5:00pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/24 "2022-11-30T17:00:04Z")

</div>

I have tired one thing passing start and end time in different column but i got a error

I have created x axis with time

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/6/167dc721ea8ef5e0d7902f47b5c49b567e050bed.png)

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [November 30, 2022, 8:07pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/25 "2022-11-30T20:07:06Z")

</div>

> [@prasath.t](#):
>
> I have tired one thing passing start and end time

You shouldn't need to do this. You are going to have to calculate the start position of each bar relative to width of your chart, so you will need to develop some kind of time ratio relative to the total amount of time in your dataset. The code needed at the beginning of the configure chart extension function will look something like this:

```auto
	data = self.data
	width = self.width
	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)

```

The time ratio can then be used to calculate the start positions of each bar based upon the end time of the previous value, and this will be done within the calculateBarW0 definition. It will look something like this:

```auto
	barStartTime = data.getValueAt(column,0)
	barStartingLocation = float(system.date.minutesBetween(chartStartTime, barStartTime))*timeRatio

```

The `if` statements used to define column positions in my original example will need to be deleted. They were only necessary because the numbers I used were arbitrary and uncalculated. To calculate the bar width, some sort of scale parameter will have to be developed. I imagine that that calculation will look something like this:

```auto
	barEndTime = data.getValueAt((column + 1), 0
	barScaleParameter = float(system.date.minutesBetween(barStartTime, barEndTime))*scaleParameter

```

Although looking at the untested code above, I can foresee that at some point an out of bounds exception is going to have to be handled in some way.

As pturmel and lrose indicated, this is not trivial development, and it will require quite a bit of time and experimentation to perfect. I find this problem interesting, so I'm inclined to mess around with it, and if I find the time, I probably will, but unfortunately, I don't have that kind of time today. If I do get some time to experiment, and I learn something worth sharing, I will post it here. When you figure this out, please do the same.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [November 30, 2022, 11:34pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/26 "2022-11-30T23:34:32Z")

</div>

I did find some time to play around with this, and I applied the ideas I listed in the previous post to the example I gave above using a modified version of your dataset. I discovered that some padding is necessary to get the timestamps to render properly.

> **Here is the full code:**
>
> ```auto
> def configureChart(self, chart):
> from org.jfree.chart.renderer.category import BarRenderer, StandardBarPainter
> from java.awt import Color
> chart.setTitle("Histogram")
> padding = self.width * .25
> data = self.data
> 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):
> barStartTime = data.getValueAt(column,0)
> barStartingLocation = float(system.date.minutesBetween(chartStartTime, barStartTime))*timeRatio + padding * .5
> try:
> barEndTime = data.getValueAt((column + 1), 0)
> barScaleParameter = float(system.date.minutesBetween(barStartTime, barEndTime))*scaleRatio
> except:
> barScaleParameter = float(width + padding - barStartingLocation)*scaleRatio
> dataArea.width = barScaleParameter
> BarRenderer.calculateBarWidth(plot, dataArea, 0, state)
> return barStartingLocation			
> def getItemPaint(BarRenderer, row, column):
> if column % 2 == 0:
> return Color.orange
> else:
> return Color.yellow
> chart.getCategoryPlot().setRenderer(DynamicWidthRenderer())
> chart.getCategoryPlot().getRenderer().setBarPainter(StandardBarPainter())
> chart.getCategoryPlot().getRenderer().setShadowVisible(False)
> 
> ```

The scale calculation needs to be developed a little further, but the arbitrary number I stuck in there gets the bars pretty close in my designer.

The next step for your usage case will probably be to develop a [label generator](https://www.jfree.org/jfreechart/api/javadoc/org/jfree/chart/labels/StandardCategoryItemLabelGenerator.html) to add the work order numbers and time stamps to the bar chart. Just read the java doc I linked to and experiment with the methods listed.

It will probably be easiest to put your full dataset in a custom propery, and then just access or move the data from that dataset as needed to render the chart. In the example above, I only used two columns from your dataset to render the chart:  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/0/c0ba0490ac6316fb4ffece95e78513bcd12d2a16.png)

This was the result:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/d/1d7cb5161ce67128148065ae8456ca89ccb6124c.png)

Shrinking the width of the chart and adding/removing bars was also tested with positive results:  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/9/5940414d505b58414fbca2e782b2f565fc2d70d5.png)

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [December 1, 2022, 2:49am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/27 "2022-12-01T02:49:31Z")

</div>

> [@justinedwards.jle](#):
>
> ```auto
> def configureChart(self, chart):
> from org.jfree.chart.renderer.category import BarRenderer, StandardBarPainter
> from java.awt import Color
> chart.setTitle("Histogram")
> padding = self.width * .25
> data = self.data
> 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):
> barStartTime = data.getValueAt(column,0)
> barStartingLocation = float(system.date.minutesBetween(chartStartTime, barStartTime))*timeRatio + padding * .5
> try:
> barEndTime = data.getValueAt((column + 1), 0)
> barScaleParameter = float(system.date.minutesBetween(barStartTime, barEndTime))*scaleRatio
> except:
> barScaleParameter = float(width + padding - barStartingLocation)*scaleRatio
> dataArea.width = barScaleParameter
> BarRenderer.calculateBarWidth(plot, dataArea, 0, state)
> return barStartingLocation			
> def getItemPaint(BarRenderer, row, column):
> if column % 2 == 0:
> return Color.orange
> else:
> return Color.yellow
> chart.getCategoryPlot().setRenderer(DynamicWidthRenderer())
> chart.getCategoryPlot().getRenderer().setBarPainter(StandardBarPainter())
> chart.getCategoryPlot().getRenderer().setShadowVisible(False)
> 
> ```

one question to show work order number in bar. we need to create separate dataset?

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [December 1, 2022, 3:03am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/28 "2022-12-01T03:03:47Z")

</div>

No separate dataset is needed. You’re providing the renderer implementation so you get to determine how it processes the provided dataset.

This question was actually addressed earlier in the thread.

> [@Change each bar width in histogram chart](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/18):
>
> Here's an idea on how to circumvent this problem: Since we're generating a renderer from scratch, we could possibly use a label generator to stick the work order numbers in a label centered above each bar.

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [December 1, 2022, 3:06am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/29 "2022-12-01T03:06:02Z")

</div>

Yaa i understood i will try to implement that one

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [December 1, 2022, 3:34am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/30 "2022-12-01T03:34:05Z")

</div>

> [@justinedwards.jle](#):
>
> ```auto
> from org.jfree.chart.renderer.category import BarRenderer, StandardBarPainter
> from java.awt import Color
> chart.setTitle("Histogram")
> padding = self.width * .25
> data = self.data
> 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):
> barStartTime = data.getValueAt(column,0)
> barStartingLocation = float(system.date.minutesBetween(chartStartTime, barStartTime))*timeRatio + padding * .5
> try:
> barEndTime = data.getValueAt((column + 1), 0)
> barScaleParameter = float(system.date.minutesBetween(barStartTime, barEndTime))*scaleRatio
> except:
> barScaleParameter = float(width + padding - barStartingLocation)*scaleRatio
> dataArea.width = barScaleParameter
> BarRenderer.calculateBarWidth(plot, dataArea, 0, state)
> return barStartingLocation			
> def getItemPaint(BarRenderer, row, column):
> if column % 2 == 0:
> return Color.orange
> else:
> return Color.yellow
> chart.getCategoryPlot().setRenderer(DynamicWidthRenderer())
> chart.getCategoryPlot().getRenderer().setBarPainter(StandardBarPainter())
> chart.getCategoryPlot().getRenderer().setShadowVisible(False)
> 
> ```

I have tired your same dataset but i am getting chart like this

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/9/c9e68d485fb306a56f0611858f6c1bbcb743deac.png)

Its it something i missing?

I have created category axis for X axis and y axis is number axis

and chart type is category chart

i have checked the console i am not getting any error

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [December 1, 2022, 3:40am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/31 "2022-12-01T03:40:50Z")

</div>

> [@prasath.t](#):
>
> we need to create separate dataset?

If the main dataset in a custom property, then it can be called in the same way the data is called from the chart's rendering dataset. For example, if the custom property is called **rawData** and the work order numbers are in the second column from the left [column index 1], then the script to access the first row of data would be:  
`self.rawData.getValueAt(0,1)`.

I like the idea of building a string for the label. It would be something like:

```auto
label = 'WO# ' + str(self.rawData.getValueAt(column,1)) + '\n' + str(system.date.format(barStartTime, "MM/dd/yy HH:mm:ss")) + '\nto\n' + str(system.date.format(barEndTime, "HH:mm:ss"))

```

The output should look like this:  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/4/d/4d204f5b96dde6ac0af5962eba84007eaec8382f.png)  
I haven't worked on this at all, so I'm not sure if html would be applicable, but if so, then the code would probably look like this instead:

```auto
label = '<html><center>WO# ' + str(self.rawData.getValueAt(column,1)) + '<br>' + str(system.date.format(barStartTime, "MM/dd/yy HH:mm:ss")) + '<br>to<br>' + str(system.date.format(barEndTime, "HH:mm:ss"))

```

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [December 1, 2022, 3:44am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/32 "2022-12-01T03:44:54Z")

</div>

> [@prasath.t](#):
>
> i have checked the console i am not getting any error

It looks like the code is working properly. You will probably want to use a print statement to troubleshoot your starting positions:

```auto
print str(column) + ', '+ str(chartStartTime) + ', '+ str(barStartTime) + ', '+ str(barStartingLocation)

```

This should help you identify any meridian reciprocations or other datetime anomalies in your dataset. Also, that scale ratio I threw in there was based on what worked with my dataset and not any reasonable algorithm. It will almost certainly need to be experimented with and developed further.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [December 1, 2022, 9:00pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/33 "2022-12-01T21:00:49Z")

</div>

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:

```auto
	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:

 ![Overlap](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/7/178e28f803c09f6ccf6e849fc80706a1e999a2ff.png)  
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:  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/e/8e04ff9828950ff1079f44932caa9b2e3d6f46df.png)

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:**
>
> ```auto
> 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)
> 
> ```

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [March 3, 2023, 6:48am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/34 "2023-03-03T06:48:43Z")

</div>

@justinedwards.jle@lrose

I tired using area chart instead of bar chart. width of the bar is adjusting ok now

but line chart showing one hour difference from the actula time

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/f/2f5cbb6624170aa3ecd6f5f3316b54351c9b78ec.png)

I have attached my project file for your reference  
can you please correct which place i am doing worng  
[BarWidth.zip](https://forum.inductiveautomation.com/uploads/short-url/9BHIfZy4u0HxPkl7PmrZtkElG5N.zip) (16.1 KB)

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 4, 2023, 6:20am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/35 "2023-03-04T06:20:50Z")

</div>

I don't see anything wrong with the way the data is actually displayed. The problem is the xTrace is getting its date from the bar dataset, which is actually accurate for the bar that the trace is over even if it's not accurate for the depicted domain position. Is there no way to correlate the dates between the two datasets?

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [March 7, 2023, 10:22am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/36 "2023-03-07T10:22:07Z")

</div>

Hi

I think data is populated correct only. I have checked with Mark mode. and checked the timing. It shows correct time

one more question

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/0/307cbd7a67f2bf4cf94669dd1f1ec555993cf357.png)

i have another dataset with WO number and i have populate the wO numbers in Bar chart

Note - both dataset timing are same only

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/b/5b5efd5f93f434831f7fae8b3df88ecaf7e3c7a8.png)

I want to populate like this

 ![wooo](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/a/aa29c6f989189f28cbfb2d5909ffa239f6a61275.png)

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 7, 2023, 12:25pm UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/37 "2023-03-07T12:25:23Z")

</div>

You could experiment with adding a custom label generator. Expanding upon the configureChart extension function code that you are using to create that border, the code will end up looking something like this:

```auto
#def configureChart(self, chart):
	from java.awt import BasicStroke, Color
	from org.jfree.chart import labels
	from org.jfree.chart.renderer.xy import XYStepAreaRenderer
	
	#This will be your WO datset, so correct the path accordingly
	labelData = self.parent.getComponent('Power Table 3').data
	
	class CustomLabelGenerator(labels.XYItemLabelGenerator):
		def generateLabel(self, dataset, series, item):
			woNum = labelData.getValueAt(item, 1)
			label = str(woNum)
			return label
	plot = chart.plot
	plot.setOutlineStroke(BasicStroke(1))
	plot.setOutlinePaint(Color.black)
	renderer = plot.renderer
	generator = CustomLabelGenerator()
	renderer.setSeriesItemLabelGenerator(0, generator)
	renderer.setSeriesItemLabelsVisible(0, True)

```

That chart is going to have multiple renderers, so it is possible that you will need to build a function that finds the specific renderer that you want to set the label generator to. It will look like this:

```auto
from org.jfree.chart.renderer import #The renderer you are looking for
def getRenderer(plot):
	for renderer in range(plot.rendererCount):
		plotRenderer = plot.getRenderer(renderer)
		print plotRenderer
		if isinstance(plotRenderer, #The renderer you are looking for):
			return plotRenderer
plot = chart.plot
renderer = getRenderer(plot)

```

Since this is an XYPlot, XYText annotations would also be an option. Just convert the date in the dataset to millis for the domain axis location, and use the bar chart value for the range axis location. Here is an example of this that I developed for a status chart:

> [@Show tooltipText in foreground of the Status Chart - Vision](https://forum.inductiveautomation.com/t/show-tooltiptext-in-foreground-of-the-status-chart-vision/69084/7):
>
> I finally developed a reliable way to achieve this result. Initially, I tried various label generators, but all I could get them to do is apply a single label to each series, so it appears that I was mistaken in my initial suggestion. What ended up working quite well are XYTextAnnotations. Below is a script that iterates through the chart data, and calculates the appropriate place to put the annotations. Since timeDiff is what was requested in the original post, in the below example, I calculat…

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [March 8, 2023, 4:49am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/38 "2023-03-08T04:49:27Z")

</div>

Hi have tired this script

```auto
#def configureChart(self, chart):
	from java.awt import BasicStroke, Color
	from org.jfree.chart import labels
	from org.jfree.chart.renderer.xy import XYStepAreaRenderer
	
	#This will be your WO datset, so correct the path accordingly
	labelData = self.parent.getComponent('Power Table 3').data
	
	class CustomLabelGenerator(labels.XYItemLabelGenerator):
		def generateLabel(self, dataset, series, item):
			woNum = labelData.getValueAt(item, 1)
			label = str(woNum)
			return label
	plot = chart.plot
	plot.setOutlineStroke(BasicStroke(1))
	plot.setOutlinePaint(Color.black)
	renderer = plot.renderer
	generator = CustomLabelGenerator()
	renderer.setSeriesItemLabelGenerator(0, generator)
	renderer.setSeriesItemLabelsVisible(0, True)

```

this above script adding labels to line chart. Not in area bar

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/9/29cff64d21174927304550258c96caf169405072.png)

then tired your tooltip script for area bar

but that also not working.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 8, 2023, 5:18am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/39 "2023-03-08T05:18:17Z")

</div>

> [@prasath.t](#):
>
> tired your tooltip script for area bar
> 
> but that also not working.

I'm sorry you had trouble figuring out. I was able to adapt the tool tip script to your usage case. **Here is the script:**

```auto
#def configureChart(self, chart):
	from java.awt import BasicStroke, Color
	from org.jfree.chart.annotations import XYTextAnnotation
	plot = chart.getPlot()
	plot.setOutlineStroke(BasicStroke(1))
	plot.setOutlinePaint(Color.black)
	for annotation in plot.getAnnotations():
		if isinstance(annotation, XYTextAnnotation):
			plot.removeAnnotation(annotation)

	#This will be your WO datset, so correct the path accordingly
	labelData = self.parent.getComponent('Power Table 1').data
	
	barData = self.parent.getComponent('Power Table').data
	for row in range(0, labelData.rowCount - 1):
		text = str(labelData.getValueAt(row, 1))
		startTime = float(barData.getValueAt(row, 0).getTime())
		endTime = float(barData.getValueAt(row + 1 , 0).getTime())
		topBound = float(barData.getValueAt(row, 2))
		bottomBound = float(barData.getValueAt(row, 1))
		x = (startTime + endTime) * .5
		y = (topBound + bottomBound) * .5
		annotation = XYTextAnnotation(text, x, y)
		plot.addAnnotation(annotation)

```

**Here is the result:**

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/5/5530ddf1865a50ae83a3c15f88547a52121ca58a.png)

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [March 8, 2023, 5:32am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/40 "2023-03-08T05:32:11Z")

</div>

working perfectly thank you soo much

---

<div class="post-metadata">

**Author:** ![prasath.t](https://avatars.discourse-cdn.com/v4/letter/p/898d66/32.png) [@prasath.t](https://forum.inductiveautomation.com/u/prasath.t)\
**Post date:** [March 8, 2023, 9:09am UTC](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847/41 "2023-03-08T09:09:46Z")

</div>

@justinedwards.jle  
one issue  
last row label is not showing in bar

 ![wooooo](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/6/069bfca78512f7b15e7da8bdfe7df5dc8747c123.png)

i changed for loop like this and checked - this error is coming

`for row in range(labelData.getRowCount()):`

```auto
14:40:15.528 [AWT-EventQueue-0] ERROR Vision.ClassicChart - Error invoking extension method.
org.python.core.PyException: java.lang.ArrayIndexOutOfBoundsException: java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at org.python.core.Py.JavaError(Py.java:547)
	at org.python.core.Py.JavaError(Py.java:538)
	at org.python.core.PyReflectedFunction. __call__ (PyReflectedFunction.java:192)
	at org.python.core.PyReflectedFunction. __call__ (PyReflectedFunction.java:208)
	at org.python.core.PyObject. __call__ (PyObject.java:494)
	at org.python.core.PyObject. __call__ (PyObject.java:498)
	at org.python.core.PyMethod. __call__ (PyMethod.java:156)
	at org.python.pycode._pyx171.configureChart$1(<extension-method configureChart>:35)
	at org.python.pycode._pyx171.call_function(<extension-method configureChart>)
	at org.python.core.PyTableCode.call(PyTableCode.java:173)
	at org.python.core.PyBaseCode.call(PyBaseCode.java:306)
	at org.python.core.PyFunction.function ___call__ (PyFunction.java:474)
	at org.python.core.PyFunction. __call__ (PyFunction.java:469)
	at org.python.core.PyFunction. __call__ (PyFunction.java:459)
	at org.python.core.PyFunction. __call__ (PyFunction.java:454)
	at com.inductiveautomation.vision.api.client.components.model.ExtensionFunction.invoke(ExtensionFunction.java:151)
	at com.inductiveautomation.factorypmi.application.components.PMIChart.createChartImpl(PMIChart.java:481)
	at com.inductiveautomation.factorypmi.application.components.chart.PMILineChartPanel.createChart(PMILineChartPanel.java:135)
	at com.inductiveautomation.factorypmi.application.components.PMIChart.setExtensionFunctions(PMIChart.java:466)
	at com.inductiveautomation.factorypmi.designer.eventhandling.ComponentScriptEditor.applyChanges(ComponentScriptEditor.java:596)
	at com.inductiveautomation.factorypmi.designer.eventhandling.ComponentScriptEditor$4.actionPerformed(ComponentScriptEditor.java:327)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
	at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.Component.processEvent(Unknown Source)
	at java.desktop/java.awt.Container.processEvent(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at com.inductiveautomation.ignition.common.BasicDataset.getValueAt(BasicDataset.java:122)
	at jdk.internal.reflect.GeneratedMethodAccessor480.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.python.core.PyReflectedFunction. __call__ (PyReflectedFunction.java:190)
	... 53 common frames omitted

```

[Previous page](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847.md?page=1)

[Next page](https://forum.inductiveautomation.com/t/change-each-bar-width-in-histogram-chart/66847.md?page=3)
