Easy Chart-Vision

Hi ,

I am currently working on vision easy chart to allow users to dynamically select the pen. I have 80 to 100 parameters for a process where i would like to display only the pens in groups not the chart as users want to only export the data in csv.


Any ideas to make only the pens to be visible and make chart inivisble?

I'm not entirely sure I understand what you are asking. Are you just wanting the checkboxes? You don't want any lines to be drawn when they are selected?

Yes, I need only the pens with grouping and i need to save the results from save button

You could probably just set the width of the chart in such a way to hide the chart, or I suppose you could loop through the subcomponents and set the visibility of the PMIEasyChart$EasyChart to false.

configureChart extension function example:

#def configureChart(self, chart):
	def getEasyChart(self):
		for component in self.getComponents():
			if 'PMIEasyChart$EasyChart' in str(component.__class__):
				return component
			else:
				easyChart = getEasyChart(component)
				if easyChart is not None:
					return easyChart
		return None
	getEasyChart(self).visible = False

but i would like to display atleast five groups, and it is displaying with long scroll.Is there any better idea to dynamically pass the parameters to historian?

There are numerous techniques for solving this problem, all somewhat complex. Consider using the "Vision Ad Hoc Trend Chart" from the Exchange, or study it for ideas:

Just for fun, I played around with this a bit more and developed a script that hides all of the chart's subcomponents except the group boxes and the save button. It also changes the layout of the group boxes, so they are aligned horizontally, and it dynamically resizes the chart to the group box sizes.
Here is the before and after:

Before:


After:

propertyChange Script:

if event.propertyName == 'datapoints':
	self = event.source
	from javax.swing import SwingUtilities, Box, JPanel, JViewport, JScrollPane
	from java.awt import FlowLayout, Dimension
	widthMargin = 80
	heightMargin = 40
	def getDimensions(self):
		for component in self.getComponents():
			if isinstance(component, Box):
				groups = []
				for group in component.parent.getComponents():
					groups.append(group)
				width = sum([group.width for group in groups])
				height = max([group.height for group in groups])
				for group in groups:
					if group.height != height:
						for penIndex, penControl in enumerate(group.getComponents()):
							if (penIndex + 1) < len(group.getComponents()):
								penHeight = penControl.height
							else:
								penHeight = height - ((penControl.height) * penIndex)
							penControl.setSize(group.width, penHeight)
							penControl.setPreferredSize(Dimension(group.width, penHeight))
					group.setSize(group.width, height)
					group.setPreferredSize(Dimension(group.width, height))
				dimensions = [(width + widthMargin), (height + heightMargin)]
				return dimensions
			else:
				dimensions = getDimensions(component)
				if dimensions is not None:
					return dimensions
		return None
	def getSaveButton():
		saveButton_Field = self.getClass().getDeclaredField('saveButton')
		saveButton_Field.setAccessible(True)
		saveButton = saveButton_Field.get(self)
		return saveButton
	def setVisibilities(self):
		for component in self.getComponents():
			saveButton = getSaveButton()
			if 'PMIEasyChart$EasyChart' in str(component.__class__):
				component.visible = False
			elif SwingUtilities.getAncestorOfClass(Box, component) is not None:
				pass
			elif 'PMIEasyChart$PenPanel' in str(component.__class__):
				component.layout = FlowLayout(FlowLayout.LEFT)
				for sub in component.getComponents():
					if isinstance(sub, JPanel):
						component.layout = FlowLayout(FlowLayout.LEFT)
			elif isinstance(component, Box):
				pass
			elif isinstance(component, JScrollPane):
				pass
			elif isinstance(component, JPanel) or isinstance(component, JViewport):
				component.layout = FlowLayout(FlowLayout.LEFT)
			elif component == saveButton:
				pass
			else:
				component.visible = False
			setVisibilities(component)
	dimensions = getDimensions(self)
	setVisibilities(self)
	system.gui.transform(self, newHeight=dimensions[1], newWidth=dimensions[0])

Edit: Added logic to make all of the group boxes the same height with the checkboxes top and left aligned.

3 Likes

Quick update on this post. The above solution of transforming the easychart component with scripting turned out to be too clunky for practical use. The other problem with the approach was that the save button was saving all of the pens no matter which pens were selected. The OP wanted to post the actual solution here, so it can help anybody else who needs to do something like this.

What ended up working was the use of a couple of templates in repeaters with a homemade save button.
This is the end result:

The templates are simple. One is nothing more than a checkbox with the isSelected property bidirectionally bound to a custom property. The other is a label and a template repeater with a border around it:
groupTags.zip (6.8 KB)

The group template is used in a template repeater with a custom property called tagPens. Obviously, the dataset was much simpler than the original chart's because it only contains what's needed for this function:

Once the tagPens dataset has been loaded, the following script parses the data into the two internal template repeaters' datasets:

chart = event.source.parent.getComponent('Template Repeater') #Change this path relative to where the script runs from
chartData =chart.tagPens
def getColumnIndex(columnName):
	for column in range(chart.tagPens.columnCount):
		if chart.tagPens.getColumnName(column) == columnName:
			return column
groupColumn = chart.tagPens.getColumnAsList(getColumnIndex('groupName'))
groups = set(groupColumn)
groupList = list(groups)
data = []
headers = ['groupName', 'colorBorder', 'colorText', 'tagPens']
for group in groupList:
	groupHeaders = ['colorText', 'isSelected', 'penName', 'tagPath']
	groupData = []
	for row in range(chart.tagPens.rowCount):
		if chart.tagPens.getValueAt(row, getColumnIndex('groupName')) == group:
			groupData.append(['0,0,0', False, chart.tagPens.getValueAt(row, getColumnIndex('penName')), chart.tagPens.getValueAt(row, getColumnIndex('tagPath'))])
	groupDataset = system.dataset.toDataSet(groupHeaders, groupData)
	data.append([group, "0,0,0", "0,0,0", groupDataset])
chart.templateParams = system.dataset.toDataSet(headers, data)

Here is the save button script that loops through pens to see which ones are checked and then converts the corresponding tag query to a spreadsheet which is then saved on the user's desktop:

import getpass
import time
from com.inductiveautomation.factorypmi.application.components import TemplateRepeater
def renameHeaders(dataset, headers):
	values = [[dataset.getValueAt(row,col) for col in range(dataset.columnCount)] for row in range(dataset.rowCount)]
	return system.dataset.toDataSet(headers,values)
def getFilename():
	current_time = time.localtime()
	username = getpass.getuser()
	filepath = 'C:/Users/' + username + '/Desktop/'
	fileName = 'tagHistoryExport_' + time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime())
	return filepath + fileName
primaryRepeater = event.source.parent.getComponent('Template Repeater')
tagPaths = []
headers = ['Time Stamp']
startTime = event.source.parent.getComponent('Date Group').getComponent('stDate').date
endTime = event.source.parent.getComponent('Date Group').getComponent('endDate').date
for template in primaryRepeater.getLoadedTemplates():
	for component in template.getComponents():
		if isinstance(component, TemplateRepeater):
			for subTemplate in component.getLoadedTemplates():
				isSelected = False
				tagPath = None
				penName = None
				for customProperty in subTemplate.dynamicProps:
					if customProperty == 'isSelected':
						isSelected = subTemplate.getPropertyValue(customProperty)
					elif customProperty == 'tagPath':
						tagPath = subTemplate.getPropertyValue(customProperty)
					elif customProperty == 'penName':
						penName = subTemplate.getPropertyValue(customProperty)
					if isSelected and tagPath is not None and penName is not None:
						tagPaths.append(tagPath)
						headers.append(penName)
			break
filename = getFilename()
tagHistory = system.tag.queryTagHistory(paths=tagPaths, startDate=startTime, endDate=endTime, returnSize=-1, aggregationMode="Maximum", returnFormat='Wide', noInterpolation=True)
dataset = renameHeaders(tagHistory, headers)
system.dataset.exportCSV(filename, True, dataset)

Edit: Included the function that was added to correct the header names that are returned by the tag history query, so the headers in the spreadsheet are the actual pen names instead of the full tag paths.

4 Likes