Cursor shape on mouse-over


The following script works inside a template:

	from java.awt import Cursor, MouseInfo
	from java.awt.event import MouseEvent
	from javax.swing import SwingUtilities
	from com.inductiveautomation.factorypmi.application.components import PMIButton, BasicContainer
	from com.inductiveautomation.factorypmi.application.components.template import TemplateHolder
	def getCoordinates(component):
		x1 = component.x
		y1 = component.y
		x2 = component.x + component.width
		y2 = component.y + component.height
		return [x1, y1, x2, y2]
	def isOverlapped(selfCoordinates, componentCoordinates):
		outOfBoundsLeft = (selfCoordinates[2] <= componentCoordinates[0])
		outOfBoundsRight = (selfCoordinates[0] >= componentCoordinates[2])
		outOfBoundsTop = (selfCoordinates[3] <= componentCoordinates[1])
		outOfBoundsBottom = (selfCoordinates[1] >= componentCoordinates[3])
		if outOfBoundsLeft or outOfBoundsRight or outOfBoundsTop or outOfBoundsBottom:
			return False
		else:
			return True
	selfCoordinates = getCoordinates(event.source)
	templateHolder = SwingUtilities.getAncestorOfClass(TemplateHolder, event.source)
	container = SwingUtilities.getAncestorOfClass(BasicContainer, templateHolder)
	for component in container.getComponents():
		if component != event.source and isinstance(component, PMIButton):
			componentCoordinates = getCoordinates(component)
			if isOverlapped(selfCoordinates, componentCoordinates):
				mousePosition = SwingUtilities.	convertPoint(event.source, event.x, event.y, container)
				if mousePosition.x >= componentCoordinates[0] and mousePosition.x <= componentCoordinates[2] and mousePosition.y >= componentCoordinates[1] and mousePosition.y <= componentCoordinates[3]:
					event.source.setCursorCode(Cursor.HAND_CURSOR)
					component.getModel().setRollover(True)
					if event.ID == MouseEvent.MOUSE_CLICKED:
						component.doClick()
				else:
					event.source.setCursorCode(Cursor.DEFAULT_CURSOR)
					component.getModel().setRollover(False)

It's the same as the above script except that it gets the template holder first, and then gets the parent container relative to the holder. I also eliminated the getOverlapCoordinates function, since it really wasn't necessary. Due to the fact that events are specific to the component they are firing from, the component coordinates are effectively the same thing.

I put this script in a custom method that I called evaluateCursorAction:
image

This works as expected, but it has to be called independently from the mouseClicked and mouseMoved event handlers on each component within the template that the cursor function is supposed to pass through.

It seemed like it would be a good idea to not use the individual components event handlers, and instead add a custom listener to individual component classes at runtime. In this way, if components within the template get added or replaced, there will be less risk of regression and less technical overhead.

Perhaps such a script would look like this:

Automated Listener Application Script
from java.awt.event import MouseListener, MouseMotionListener
from com.inductiveautomation.factorypmi.application.components.template import VisionTemplate
def getTemplateInstance(component):
	if isinstance(component.parent, VisionTemplate):
		return component.parent
	else:
		templateInstance = getTemplateInstance(component.parent)
		if templateInstance is not None:
			return templateInstance
class mouseClickListener(MouseListener):
	def mouseClicked(self, event):
		getTemplateInstance(event.source).evaluateCursorAction(event)
	
	def mouseEntered(self, event):
		pass
	
	def mouseExited(self, event):
		pass
	
	def mousePressed(self, event):
		pass
	
	def mouseReleased(self, event):
		pass
	
class mouseMovementListener(MouseMotionListener):
	def mouseMoved(self, event):
		getTemplateInstance(event.source).evaluateCursorAction(event)
	
	def mouseDragged(self, event):
		pass
def setListeners(self):
	for component in self.getComponents():
		if component is not None:
			component.addMouseListener(mouseClickListener())
			component.addMouseMotionListener(mouseMovementListener())
			component.setFocusable(True)
			setListeners(component)
if event.propertyName == 'componentRunning':
	setListeners(event.source)

I haven't messed with it enough to get it working, but nevertheless, there's the idea if anybody wants to follow it up.

1 Like