Scrollable Popup Menu

Hi folks,

I'm working with the Inductive created Ad Hoc trend from the exchange. Looking at the "Load" feature for loading saved charts, we see the following:

'Load' Button Script
database = ""
hasCharts = 1
try:
	chartCount = system.db.runPrepQuery("SELECT COUNT(*) FROM saved_graphs",[], database)
except:
	hasCharts = 0

if not hasCharts or chartCount[0][0]<1:
	system.gui.messageBox("No saved graphs currently exist. Use adjacent save button to store current graph.")
else:
	database = ""
	username = system.security.getUsername()	
	
	names = []
	funcs = []
	
	privateNames = []
	privateFuncs = []
	
	create = 0
	
	try:
		charts = system.db.runPrepQuery("SELECT id, title FROM saved_graphs WHERE username = ? ORDER BY title ASC", [username], database)
		for row in charts:
			cNames = ["Load", "Delete"]
			#this if-else seems redundant - ARH
			if event.source.parent.databaseType == 'ORACLE':
				rowId=row["ID"]
			else:
				rowId=row["id"]
			def load(event, id=rowId):
				import system
				info = system.db.runPrepQuery("SELECT title, tagpens, axes, chartmode, startdate, enddate, rangestartdate, rangeenddate, unit, unitcount FROM saved_graphs WHERE id = ?", [id], database)
				if len(info):
					chart = event.source.parent.getComponent('Easy Chart')
					chart.title = info[0][0]
					chart.tagPens = system.dataset.fromCSV(info[0][1])
					chart.axes = system.dataset.fromCSV(info[0][2])
					chart.updateMinMaxAvg()
					chart.chartMode = int(info[0][3])
					chart.startDate = info[0][4]
					chart.endDate = info[0][5]
					chart.outerRangeStart = info[0][6]
					chart.outerRangeEnd = info[0][7]
					chart.unit = int(info[0][8])
					chart.unitCount = int(info[0][9])
					def doLater():
						chart.chartMode = int(info[0][3])
						chart.startDate = info[0][4]
						chart.endDate = info[0][5]
						chart.outerRangeStart = info[0][6]
						chart.outerRangeEnd = info[0][7]
						chart.unit = int(info[0][8])
						chart.unitCount = int(info[0][9])
					system.util.invokeLater(doLater, 1000)
			if event.source.parent.databaseType == 'ORACLE':
				rowId=row["ID"]
				rowTitle=row["TITLE"]
			else:
				rowId=row["id"]
				rowTitle=row["title"]
			def delete(event, id=rowId, title=rowTitle):
				import system
				if system.gui.confirm("Are you sure you want to delete chart '%s'?" % title):
					system.db.runPrepUpdate("DELETE FROM saved_graphs WHERE id = ?", [id], database)
			cFuncs = [load, delete]
			privateNames.append(rowTitle)
			privateFuncs.append([cNames, cFuncs])	
	except:
		create = 1
	
	publicNames = []
	publicFuncs = []
	
	try:
		charts = system.db.runPrepQuery("SELECT id, title FROM saved_graphs WHERE username IS NULL ORDER BY title ASC", [], database)
		for row in charts:
			pubNames = ["Load", "Delete"]
			#this if-else seems redundant - ARH
			if event.source.parent.databaseType == 'ORACLE':
				rowId=row["ID"]
			else:
				rowId=row["id"]
			def load(event, id=rowId):
				import system
				info = system.db.runPrepQuery("SELECT title, tagpens, axes, chartmode, startdate, enddate, rangestartdate, rangeenddate, unit, unitcount FROM saved_graphs WHERE id = ?", [id], database)
				if len(info):
					chart = event.source.parent.getComponent('Easy Chart')
					chart.title = info[0][0]
					chart.tagPens = system.dataset.fromCSV(info[0][1])
					chart.axes = system.dataset.fromCSV(info[0][2])
					chart.updateMinMaxAvg()
					chart.chartMode = int(info[0][3])
					chart.startDate = info[0][4]
					chart.endDate = info[0][5]
					chart.outerRangeStart = info[0][6]
					chart.outerRangeEnd = info[0][7]
					chart.unit = int(info[0][8])
					chart.unitCount = int(info[0][9])
					def doLater():
						chart.chartMode = int(info[0][3])
						chart.startDate = info[0][4]
						chart.endDate = info[0][5]
						chart.outerRangeStart = info[0][6]
						chart.outerRangeEnd = info[0][7]
						chart.unit = int(info[0][8])
						chart.unitCount = int(info[0][9])
					system.util.invokeLater(doLater, 1000)
			if event.source.parent.databaseType == 'ORACLE':
				rowId=row["ID"]
				rowTitle=row["TITLE"]
			else:
				rowId=row["id"]
				rowTitle=row["title"]
			def delete(event, id=rowId, title=rowTitle):
				import system
				if system.gui.confirm("Are you sure you want to delete chart '%s'?" % title):
					system.db.runPrepUpdate("DELETE FROM saved_graphs WHERE id = ?", [id], database)
			pubFuncs = [load, delete]
			publicNames.append(rowTitle)
			publicFuncs.append([pubNames, pubFuncs])	
	except:
		create = 1
		
	names.append("Public")
	funcs.append([publicNames, publicFuncs])
	names.append(username)
	funcs.append([privateNames, privateFuncs])
	
	menu = system.gui.createPopupMenu(names, funcs)
	menu.show(event, 0, 28)

This effectively creates a multi-level popup menu, with User / Public as the top tier, a list of saved charts as the second tier, and Load/Delete as the third tier.

The client has managed to create enough saved charts to fill the list (currently 45), and yet there is no scroll bar, so they cannot access components off screen. Reading the other forum thread on this topic, it seems like scrollbars aren't supported for the built-in script, and we need to rework the script to use JidePopupMenu or JYPopupMenu, but I am at a bit of a loss for how to best transform the above for loops into something that will produce a working list.

I was hoping it would be a simple ask to provide basic function, but it seems like its more than I was expecting. Any advice on how to best approach this and what the core of that code would look like?

Thank you in advance.

What version of Ignition are you using? As of 8.1.25, system.gui.createPopupMenu automatically creates one that scrolls. There are a few minor issues with deeply nested menus/menus near the edge of the screen, resolved in 8.1.32, but the core functionality should be there.

1 Like

Go figure! The gateway in question is 8.1.24 :man_facepalming:

Sounds like another reason to get the minor revision updates scheduled if we can.

I went ahead and ran the upgrade on a test bench (8.1.32), but I'm not seeing the scroll bar behavior. Is there some additional configuration we need to apply to the popup?

The top level menu scrolls. Submenus don't. :man_facepalming:

We've already got an internal ticket going for the fix.

Ahh. Thank you for the clarification.

Hello,

Somehow I don't get third tier options for LOAD / DELETE, Its always loads the trend. I am looking for an option to delete some trend to make this list shorter. I can do it from DB but would like to do it from runtime. Is there something I am missing?

Actually My mistake, We don't get that option for Public trends.
I hope we get solution for this soon. I am using 8.1.22 and I am not sure how I can add scroll bar or anybody solved it any other way?

The fix is to update.

1 Like

I pivoted from downtown menu to a pop-up window with a scrollable selection. Currently it makes more sense than upgrading for this client.

1 Like