Scroll scrollbar to position via script

I've had a request from a customer to auto-scroll a template repeater to the previous scroll position as left at on the last page view.

How would I go about doing this?

Cheers!

Can you get away with just not closing the page (i.e. opening a new page instead of swapping to it)?

The advantage is that switching back to the page will keep it’s old state (switching will become a lot faster too). The disadvantage is that the tags on the page remain active, so keep consuming bandwidth even though they’re not visible.

Depending on what type of page it is, not closing the page may be an acceptable solution.

Otherwise, you can dig into the Java components, and normally get and set the visible rectangle on such a template repeater.

The actual table pulls in quite a lot of data, so I’m not that keen on keeping this open all the time.

I’ve had a look into the Java components and found the getVisibleRect() and scrollRectToVisible(…) methods. With the following code, I can scroll the Designer’s page frame (???), but in a client runtime, it doesn’t do anything.
I’ve called this on a button for now:

me = event.source.parent.getComponent('Template Repeater')

rect = me.getVisibleRect()
print rect
rect.y = rect.y + 10

me.scrollRectToVisible(rect)

the print statement in runtime returns: java.awt.Rectangle[x=0,y=0,width=1141,height=685]
the width and height are the size of the template repeater component, however the me.scrollRectToVisible(rect) call does nothing…
If I scroll the template repeater manually and run the script again, none of the values change.

What am I doing wrong? P.s. my Java knowledge is very little

Cheers

Bump (this is 20 characters)

Got it!

RC = system.gui.getParentWindow(event).rootContainer
me = RC.getComponent('Template Repeater')

#Get the scrollPane object which contains the viewPort object by finding it
components = me.getComponents()
for component in components:
	if str(type(component)) == "<type 'com.jidesoft.swing.JideScrollPane'>":
		scrollPane = component

viewport = scrollPane.getViewport()

#Get the 'visible rectangle' which basically defines the current view of the full scroll-able pane
rect = viewport.getVisibleRect()
#Add '400' units to the horizontal scroll position
rect.y = rect.y + 400

#write the new visible rectangle back to the scroll-able object
viewport.scrollRectToVisible(rect)

Thanks for your prompt, Sander

Edit: I replaced the static scrollPane component assignment with a loop to find this component instead, so this should work for any component now and not just the template repeater

1 Like

For those who read this thread, a point I learned in applying a derivation of this solution:

You don’t need (or want) “from javax.swing import JScrollPane”, Ignition is using JideScrollPane which is like JScrollPane on steroids. However you can’t import JideScrollPane since it is a licensed package. No problem, it is built into Ignition.

Of course you can import it. Import has nothing whatsoever to do with licensing. However, you should never need to, as the ScrollPane already exists in the component, as noted above.

My bad Phil. I tried to import it and failed and then read it is a licensed package. So I quit and made an inaccurate statement. Thanks for the correction.
import com.jidesoft.swing.JideScrollPane does work

UPDATED: 2020-06-25

I’ve since created a script library for this (not exactly) function that will scroll to an item in the scrollable component’s dataset:

# call this on a button or other
scrollableObj = event.source.parent.getComponent('Table')
shared.components.scrollable.scrollToPos(scrollableObj, 'Col 1', 29, 1)
# copy into library: shared.components.scrollable
def scrollToPos(scrollableObj, DataSetFieldName, NewDataSetPosValue, position=0, recursionLevel=0):
	'''
	Description:
	Scrolls the table component to the item above the new position.
	
	Arguments:
	scrollableObj			- the scrollable component (table, power table, template repeater - untested with any others)
	DataSetFieldName		- the field name in the templateParams dataset to find the new position value in
	NewDataSetPosValue		- the field value of the field name to find in the dataset to move to
	Position				- the position to scroll to:
								0 - position item at the top minus 1
								1 - position item in the centre
                                2 - position item at the top
	
	Revision:
	No.		Date		Author			Comment
	1.0		2018-11		Nick Minchin	Original
	1.1		2019-12		Nick Minchin	Simplified function. Can set value of scrollbar directly without having to use the viewport.
	1.2		2019-12-15	Nick Minchin	Simplified further method to get the scrollbar object. Added ability to change scroll to position type.
	1.3		2020-06-25	Nick Minchin	Fixed issue with rowHeight for Power Tables. Made item height and items count round up
	'''
	import math
	objType = str(type(scrollableObj))
	
	ds = None
	#get the component's items dataset so that we can work out the current index of the maximum
	if any([t in objType for t in ['PMITable', 'VisionAdvancedTable']]):
		ds = scrollableObj.data
	if any([t in objType for t in ['TemplateRepeater']]):
		ds = scrollableObj.templateParams
	
	if ds == None:
		import sys
		system.gui.messageBox("The object type you passed in is not accounted for. '%s'" % objType, 'Error')
		sys.exit()
	ds = system.dataset.toPyDataSet(ds)
	
	if len(ds) > 0:
		#find the index of the new item from the dataset
		index = 0
		for i in range(len(ds)):
			if ds[i][DataSetFieldName] == NewDataSetPosValue:
				index = i
		
		scrollBar = scrollableObj.verticalScrollBar
		scrollBarHeight = scrollBar.height
		itemHeight = scrollableObj.getTable().getRowHeight(0)
		itemsInView = (1.0*scrollBarHeight) / itemHeight
		
		#Calculate the position of the current step using the item height and the new item's index
		if position == 0:
			newPos = int(itemHeight*(index - 1))
		elif position == 1: #WORKING
			newPos = int(itemHeight*(index + 1 - (itemsInView/2.0)))
			#newPos = int(math.ceil(itemHeight*(index  - (itemsInView/2.0))))
		elif position == 2:
			newPos = int(itemHeight*(index - 0))
		else:
			newPos = int(itemHeight*(index - 0))
		
		scrollBar.setValue(newPos)
		scrollableObj.selectedRow = index
		recursionLevel += 1
		# for some reason the first time this is run, the scroll is off by a few pixels.. so call it again
		if recursionLevel == 1:
			scrollToPos(scrollableObj, DataSetFieldName, NewDataSetPosValue, position, recursionLevel)
3 Likes

I tried using this script on a template repeater, and get the following error: "AttributeError: 'com.inductiveautomation.factorypmi.application.com' object has no attribute 'verticalScrollBar'". It looks like from the thread and the script that it is meant to work with a power table or a template repeater. Is there another attribute that would make it work with a template repeater?

The code in post 5 works perfectly for my template repeater. Is this the code you are using?