Get Project Property 'Dock Axis Precedence' In Scripting

Hi, for resizing purposes I would like to get the value of the 'Docking Axis Precedence' in the 'Project Properties'.

I found a topic where it was possible to found the project description using folowing code:
print(str(event.source.parent.getAppContext().getProject().getDescription()))

Thanks in advance

This is probably a bad idea, and I would encourage you to not try to rely on specific information like this from the runtime when designing your windows. That said...

print(str(event.source.parent.getAppContext().getPrimaryDesktop().getAxisPrecedence()))

Thanks,

The reason I would like to use this is cause I use floating non maximized windows that I resize accordingly to be for instance a 'west overlay' that functions like an overlay (not cropping the main window) instead of a docked window that crops the main window. See screenshot:

I've written some project scripting functions (getOverlayWidth, getOverlayHeight and getLeftTop ect.) to detirmine sizes and points in my framework. These functions depend on 2 ignition docked windows, a north and a west one with a dock index of -1. Whenever the client starts I open these windows: north with a height of 0 and west with a width of 0, so you dont actually see them. Afterwards I use those windows to determine the width (north docked window) and height (west docked window) of the actual client runtime.

In my example you can see that my dock axis precedence is set to 'North/South' so to determine my actual overlay width I take the width of my north docked window and subtract it with the width of all my other open east or west windows:

# Get west and east docks width
winDockEastWidth = 0
winDockWestWidth = 0
for window in system.gui.getOpenedWindows():
	if window.getComponentForPath('_parent').dockPosition == winDockLocationEast:
		winDockEastWidth = winDockEastWidth + window.width
	if window.getComponentForPath('_parent').dockPosition == winDockLocationWest:
		winDockWestWidth = winDockWestWidth + window.width

# Get overlay width
return system.gui.getWindow(winPathClientDockSizeWidth).width - winDockEastWidth - winDockWestWidth

Whenever for some reason somebody changes this axis precedence, there is no need to subtract the east nor west dock. Thats why I like to know the axis precedence.

Now I realize this is a tricky way of working around the Ignition Vision posibilities, and after testing I've also noticed that I get other values for the axis precedence in 'Designer Testing' (play button) gives me 'North / South' and the actual Ignition client gives me 'Ă‚ÂżAxisPrecedence.NorthSouth?'. It stil give me north and south so I can get it out but this might change in future Ignition updates.

So, if you have a better solid option to get the actual client runtime width and height (red line A in the screenshot) so I don't need to use the trick with hidden docked windows that would make my day and probably be a better solution. I tried playing around with code I found at:
https://forum.inductiveautomation.com/t/how-to-get-vision-client-resolution/27328/4.
But this ends up in giving me the display resolution and not the actual client drawing resolution.

Next to this one more question. I got your solution working from a button, but how do i get to the getAppContext() from within project scripting?

Big thanks upfront already.

To get the client size. You will actaually want the root's contentPane, as the JFrame size includes the border.

I stuck this on a button, but it should work in the internalFrameActivated event as well.

from javax.swing import SwingUtilities
component = event.source

# Get the root JFrame
root = SwingUtilities.getRoot(component)
JFrameSize = root.size
JFrameWidth = JFrameSize.width
JFrameHeight = JFrameSize.height


# Get the ContentPane (Inside the borders)
contentPane = root.getContentPane()
contentPaneSize = contentPane.size
contentPaneWidth = contentPaneSize.width
contentPaneHeight = contentPaneSize.height

print 'JFrame: {} x {}'.format(JFrameWidth, JFrameHeight)
print 'ContentPane: {} x {}'.format(contentPaneWidth, contentPaneHeight)
6 Likes

Hi Jordan, thanks for the reply. It indeed works. Although I have still one problem.
Whenever en update is available and the update message underneath the windows commands pops up, it doesn't change in height value. Which does work with using hidden windows. Is there a way to catch this?

Thanks

No idea, as it doesn't change the dimensions of the root JFrame.

@PGriffith, can we catch the project update message in a vision client?

Another option, if on 8.1.24, is to skip using our project update banner and provide your own (or disable it completely):

1 Like

Hi Jordan,

Turns out there is a way.

parent = event.source
while parent is not None:
	print "=============================="
	print parent
	print type(parent)
	# Get next parent
	parent = parent.parent

Ive put this on a button, which prints the entire graphical buildup until your object.

==============================
PMIButton: Print Parent Component Tree
<type 'com.inductiveautomation.factorypmi.application.components.PMIButton'>
==============================
Root Container
<type 'com.inductiveautomation.factorypmi.application.components.BasicContainer'>
==============================
javax.swing.JLayeredPane[null.layeredPane,0,0,1594x900,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=,optimizedDrawingPossible=true]
<type 'javax.swing.JLayeredPane'>
==============================
javax.swing.JRootPane[,5,22,1594x900,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@11e69176,flags=449,maximumSize=,minimumSize=,preferredSize=]
<type 'javax.swing.JRootPane'>
==============================
winDevTestScreen
<type 'com.inductiveautomation.factorypmi.application.FPMIWindow'>
==============================
VisionApp[Airtec_PRODUCTION]
<type 'com.inductiveautomation.factorypmi.application.FPMIApp'>
==============================
javax.swing.JViewport[,1,1,1851x1028,invalid,layout=javax.swing.ViewportLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=25165832,maximumSize=,minimumSize=,preferredSize=,isViewSizeSet=true,lastPaintPosition=java.awt.Point[x=0,y=0],scrollUnderway=false]
<type 'javax.swing.JViewport'>
==============================
javax.swing.JScrollPane[,0,0,1853x1030,invalid,layout=javax.swing.ScrollPaneLayout$UIResource,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@684ea065,flags=320,maximumSize=,minimumSize=,preferredSize=,columnHeader=,horizontalScrollBar=javax.swing.JScrollPane$ScrollBar[,1,1016,1851x13,hidden,layout=javax.swing.plaf.synth.SynthScrollBarUI,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@5c9f5ee0,flags=4194560,maximumSize=,minimumSize=,preferredSize=,blockIncrement=10,orientation=HORIZONTAL,unitIncrement=1],horizontalScrollBarPolicy=HORIZONTAL_SCROLLBAR_AS_NEEDED,lowerLeft=,lowerRight=,rowHeader=,upperLeft=,upperRight=,verticalScrollBar=javax.swing.JScrollPane$ScrollBar[,1839,1,13x1007,hidden,layout=javax.swing.plaf.synth.SynthScrollBarUI,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@2fb7cea2,flags=4194560,maximumSize=,minimumSize=,preferredSize=,blockIncrement=10,orientation=VERTICAL,unitIncrement=1],verticalScrollBarPolicy=VERTICAL_SCROLLBAR_AS_NEEDED,viewport=javax.swing.JViewport[,1,1,1851x1028,invalid,layout=javax.swing.ViewportLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=25165832,maximumSize=,minimumSize=,preferredSize=,isViewSizeSet=true,lastPaintPosition=java.awt.Point[x=0,y=0],scrollUnderway=false],viewportBorder=javax.swing.plaf.synth.SynthScrollPaneUI$ViewportBorder@219ff1fb]
<type 'javax.swing.JScrollPane'>
==============================
com.inductiveautomation.factorypmi.application.runtime.ClientPanel[,0,27,1853x1030,invalid,layout=java.awt.BorderLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@409d49db,flags=9,maximumSize=,minimumSize=,preferredSize=]
<type 'com.inductiveautomation.factorypmi.application.runtime.ClientPanel'>
==============================
javax.swing.JLayeredPane[null.layeredPane,0,0,1853x1057,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=,optimizedDrawingPossible=true]
<type 'javax.swing.JLayeredPane'>
==============================
javax.swing.JRootPane[,8,31,1853x1057,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@617f516c,flags=16777673,maximumSize=,minimumSize=,preferredSize=]
<type 'javax.swing.JRootPane'>
==============================
javax.swing.JFrame[frame1,59,-8,1869x1096,invalid,layout=java.awt.BorderLayout,title=Airtec_PRODUCTION - Development: Test Screen,resizable,maximized,defaultCloseOperation=DO_NOTHING_ON_CLOSE,rootPane=javax.swing.JRootPane[,8,31,1853x1057,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.plaf.synth.SynthBorder@617f516c,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
<type 'javax.swing.JFrame'>

Turns out that the <type 'javax.swing.JViewport'> gives the exact width and height without update notifications nor scrollbars.
If you would like to include scrollbars, you can also use <type 'javax.swing.JScrollPane'>.

So to fetch these objects I use your script to go from the root back down to the viewport object as following.

from javax.swing import SwingUtilities
from javax.swing import JScrollPane

# Get component
component = event.source

# Get the root JFrame
root = SwingUtilities.getRoot(component)

# Get the ContentPane (Inside the borders)
contentPane = root.getContentPane()

# Get the inner viewport (Inside the borders and inside the scrollpane)
for component in contentPane.getComponents():	
	if type(component) == JScrollPane:
		viewport = component.getViewport()
		viewportSize = viewport.size
		viewportWidth = viewportSize.width
		viewportHeight = viewportSize.height
		print 'Viewport: {} x {}'.format(viewportWidth, viewportHeight)

Do you maybe know if its possible, and how to add some kind of listener to this object that triggers something when the size values change?

This can be done using the internalFrameOpened event handler of the parent window. Here is an example script:

from java.awt.event import ComponentAdapter
from javax.swing import JViewport
class ViewportSizeChangeListener(ComponentAdapter):
	def __init__(self, viewport):
		self.viewport = viewport
	def componentResized(self, event):
		width = self.viewport.size.width
		height = self.viewport.size.height
		print 'Viewport size changed to %d x %d' % (width, height)
def getViewport(component):
	if component.componentCount > 0:
		for subComponent in component.getComponents():
			if isinstance(subComponent, JViewport):
				return subComponent
			else:
				viewport = getViewport(subComponent)
				if viewport is not None:
					return viewport
	return None
viewport = getViewport(event.source)
viewport.addComponentListener(ViewportSizeChangeListener(viewport))

Output during various popup window size changes:

Viewport size changed to 678 x 536
Viewport size changed to 881 x 681
Viewport size changed to 1087 x 840
1 Like