Dynamic accordion perspective

Hello,

I have a table "Categories" in a database, I would like each category to create a new section in the accordion. But it doesn't works.

I have tried to copy the structure of one section in custom, and then duplicate it with a script. But it duplicate a reference to the copy, not the copy itself, and when I change the parameter to my view the initial copy was changed too.

Thanks,
Steven Cheron

Bind Accordion.props.items against a Named Query which returns the categories as a list/set/array, and make sure to set the format of the binding to "json".
Now add this transform.

def transform(self, value, quality, timestamp):
	return [{
				"expanded": False,
				"header": {
					"toggle": {
						"enabled": True,
						"expandedIcon": {
							"path": "material/expand_less",
							"color": "",
							"style": {
								"classes": ""
							}
						},
						"collapsedIcon": {
							"path": "material/expand_more",
							"color": "",
							"style": {
								"classes": ""
							}
						}
					},
				"content": {
					"type": "text",
					"text": category,
					"useDefaultViewWidth": False,
					"useDefaultViewHeight": False,
					"viewPath": "",
					"viewParams": {},
					"style": {
						"classes": ""
					}
				},
				"height": "40px",
				"reverse": False,
				"style": {
					"classes": ""
				}
			},
			"body": {
				"viewPath": "",
				"viewParams": {},
				"useDefaultViewWidth": False,
				"useDefaultViewHeight": False,
				"height": "auto",
				"style": {
					"classes": "",
					"margin": "16px"
				}
			}
		} for category in value]
1 Like

FWIW, this is expected behavior of Python/Jython when manipulating objects. If you wish to use such a "template" pattern in your code, whether for Perspective or elsewhere, your instances need to be deep copies of the template before they are updated with the per-instance customizations.

I was hoping there is other solutions than that. If the structure changed in the future my binding will be broken.

Maybe perspective can have a function to create the default structure like "system.perspective.createDefault(property)".

I haven't tried this already, but it will works.
Thank you

I've try with "copy.deepcopy()" but an exception is raised. I don't know which one, because the text was too long for the tooltip in the binding configurator. And there is no log in the gateway.

Then hand-roll your own deep copy. And/or catch the exception to log it yourself.

I got this exception

com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ObjectWrapper(): expected 40 args; got 0

This is the transform on my request

def transform(self, value, quality, timestamp):
	import copy
	logger = system.util.getLogger("RecipeList")
	
	items = []
	
	try:
		for c in value:
			item = copy.deepcopy(self.custom.defaultItem)
				
			item.header.content.viewParams.categoryId = c["id"]
			item.body.viewParams.categoryId = c["id"]
			
			items.append(item)
	except Exception as e:
		logger.info(str(e))
	
	return items

The copy of the default structure is self.custom.defaultItem

Looks like Perspective properties don't cooperate with jython's deepcopy(). Sorry. You'll have to roll your own.

1 Like

I found a solution, not sexy, but it's work easy :grinning:

def transform(self, value, quality, timestamp):
	items = []
	
	for i, c in enumerate(value):
		item = system.util.jsonDecode(system.util.jsonEncode(self.custom.defaultItem))
		item["header"]["content"]["viewParams"]["categoryId"] = c["id"]
		item["body"]["viewParams"]["categoryId"] = c["id"]
		item["expanded"] = self.props.items[i].expanded
		items.append(item)
		
	return items

I encode the default structure saved in custom in JSON then decode it, replace the categoryId to the right id.

Edit : I also keep the expanded sections expanded by copy the current state to the new.

Thanks for your help ! :wave:

1 Like