If I try to set a builtin property on a widget to the same value it has, the propertyChange event does not fire.
If I try to set a builtin property on a widget to python’s None, the type check fails and nothing happens.
If I try to set a custom property on a widget to the same value it has, the propertyChange event does not fire.
If I try to set a custom property on a widget to python’s None, the propertyChange event fires even if the property’s value was already None.
I’ve added checks for None to prevent creating an infinite loop like the example below does, but I’m guessing this is not intended behavior?
Here is some xml you can paste into a vision window that does a proof of concept.
<?xml version="1.0" encoding="UTF-8"?>
<objects fpmi.archive.type="components" framework.version="8.1.14.2022012711" com.inductiveautomation.vision.version="11.1.14.0" timestamp="Wed Feb 09 15:10:12 CST 2022">
<arraylist len="6">
<o cls="com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter">
<o-c m="setEventSet" s="1;java.beans.EventSetDescriptor">
<o id="1" cls="java.beans.EventSetDescriptor">
<o-ctor s="5;str;java.lang.Class;[java.beans.MethodDescriptor;java.lang.reflect.Method;java.lang.reflect.Method">
<str>action</str>
<class>java.awt.event.ActionListener</class>
<array cls="java.beans.MethodDescriptor" len="1">
<o id="0" cls="java.beans.MethodDescriptor">
<o-ctor s="2;java.lang.reflect.Method;[java.beans.ParameterDescriptor">
<method>java.awt.event.ActionListener|actionPerformed|1;java.awt.event.ActionEvent</method>
<null/>
</o-ctor>
</o>
</array>
<method>javax.swing.AbstractButton|addActionListener|1;java.awt.event.ActionListener</method>
<method>javax.swing.AbstractButton|removeActionListener|1;java.awt.event.ActionListener</method>
</o-ctor>
</o>
</o-c>
<o-c m="setJythonCode" s="1;str"><str>event.source.parent.getComponent('Numeric Text Field 1').intValue = 0
event.source.parent.getComponent('Numeric Text Field').intValue = 0
event.source.parent.getComponent('Numeric Text Field 1').myCustomProp = 0</str></o-c>
<o-c m="setMethodDescriptor" s="1;java.beans.MethodDescriptor"><ref>0</ref></o-c>
<o-c m="setScopeStyle" s="1;com.inductiveautomation.ignition.common.script.ScriptScopeStyle"><enum id="2" cls="com.inductiveautomation.ignition.common.script.ScriptScopeStyle" n="Python25"/></o-c>
<o-c m="setTarget" s="1;java.awt.Component">
<c id="9" cls="com.inductiveautomation.factorypmi.application.components.PMIButton">
<c-comm>
<p2df>71.0;27.0</p2df>
<r2dd>376.0;300.0;71.0;27.0</r2dd>
<str>Button</str>
<lc>376.0;300.0;16;0;-;-</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<str id="3">vision.custom.functions</str>
<o id="4" cls="java.util.HashMap"/>
</c-c>
<c-c m="setButtonBG" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>Reset</str></c-c>
</c>
</o-c>
</o>
<o cls="com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter">
<o-c m="setEventSet" s="1;java.beans.EventSetDescriptor"><ref>1</ref></o-c>
<o-c m="setJythonCode" s="1;str"><str>event.source.parent.getComponent('Numeric Text Field 1').myCustomProp = None
#event.source.parent.getComponent('Numeric Text Field 1').intValue = None
</str></o-c>
<o-c m="setMethodDescriptor" s="1;java.beans.MethodDescriptor"><ref>0</ref></o-c>
<o-c m="setScopeStyle" s="1;com.inductiveautomation.ignition.common.script.ScriptScopeStyle"><ref>2</ref></o-c>
<o-c m="setTarget" s="1;java.awt.Component">
<c id="12" cls="com.inductiveautomation.factorypmi.application.components.PMIButton">
<c-comm>
<p2df id="5">146.0;27.0</p2df>
<r2dd>510.0;106.0;146.0;27.0</r2dd>
<str>btnNoWork 3</str>
<lc>510.0;106.0;16;0;0.08219178;0.44444445</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<ref>3</ref>
<ref>4</ref>
</c-c>
<c-c m="setButtonBG" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>This Breaks</str></c-c>
<c-c m="setPath" s="1;str"><str id="6"></str></c-c>
</c>
</o-c>
</o>
<o cls="com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter">
<o-c m="setEventSet" s="1;java.beans.EventSetDescriptor"><ref>1</ref></o-c>
<o-c m="setJythonCode" s="1;str"><str>event.source.parent.getComponent('Numeric Text Field 1').myCustomProp = 3
#event.source.parent.getComponent('Numeric Text Field 1').intValue = None
</str></o-c>
<o-c m="setMethodDescriptor" s="1;java.beans.MethodDescriptor"><ref>0</ref></o-c>
<o-c m="setScopeStyle" s="1;com.inductiveautomation.ignition.common.script.ScriptScopeStyle"><ref>2</ref></o-c>
<o-c m="setTarget" s="1;java.awt.Component">
<c id="13" cls="com.inductiveautomation.factorypmi.application.components.PMIButton">
<c-comm>
<ref>5</ref>
<r2dd>342.0;109.0;146.0;27.0</r2dd>
<str>btnNoWork 2</str>
<lc>342.0;109.0;16;0;0.08219178;0.44444445</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<ref>3</ref>
<ref>4</ref>
</c-c>
<c-c m="setButtonBG" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>This This is also Fine</str></c-c>
<c-c m="setPath" s="1;str"><ref>6</ref></c-c>
</c>
</o-c>
</o>
<o cls="com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter">
<o-c m="setEventSet" s="1;java.beans.EventSetDescriptor"><ref>1</ref></o-c>
<o-c m="setJythonCode" s="1;str"><str>#event.source.parent.getComponent('Numeric Text Field 1').myCustomProp = 2
event.source.parent.getComponent('Numeric Text Field 1').intValue = 2
</str></o-c>
<o-c m="setMethodDescriptor" s="1;java.beans.MethodDescriptor"><ref>0</ref></o-c>
<o-c m="setScopeStyle" s="1;com.inductiveautomation.ignition.common.script.ScriptScopeStyle"><ref>2</ref></o-c>
<o-c m="setTarget" s="1;java.awt.Component">
<c id="14" cls="com.inductiveautomation.factorypmi.application.components.PMIButton">
<c-comm>
<ref>5</ref>
<r2dd>343.0;64.0;146.0;27.0</r2dd>
<str>btnNoWork 1</str>
<lc>343.0;64.0;16;0;0.08219178;0.44444445</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<ref>3</ref>
<ref>4</ref>
</c-c>
<c-c m="setButtonBG" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>This Is Fine</str></c-c>
<c-c m="setPath" s="1;str"><ref>6</ref></c-c>
</c>
</o-c>
</o>
<o cls="com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter">
<o-c m="setEventSet" s="1;java.beans.EventSetDescriptor"><ref>1</ref></o-c>
<o-c m="setJythonCode" s="1;str"><str>#event.source.parent.getComponent('Numeric Text Field 1').myCustomProp = 2
event.source.parent.getComponent('Numeric Text Field 1').intValue = None
</str></o-c>
<o-c m="setMethodDescriptor" s="1;java.beans.MethodDescriptor"><ref>0</ref></o-c>
<o-c m="setScopeStyle" s="1;com.inductiveautomation.ignition.common.script.ScriptScopeStyle"><ref>2</ref></o-c>
<o-c m="setTarget" s="1;java.awt.Component">
<c id="15" cls="com.inductiveautomation.factorypmi.application.components.PMIButton">
<c-comm>
<ref>5</ref>
<r2dd>509.0;66.0;146.0;27.0</r2dd>
<str>btnNoWork</str>
<lc>509.0;66.0;16;0;0.08219178;0.44444445</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<ref>3</ref>
<ref>4</ref>
</c-c>
<c-c m="setButtonBG" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>This Won't Work</str></c-c>
<c-c m="setPath" s="1;str"><ref>6</ref></c-c>
</c>
</o-c>
</o>
<o cls="com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter">
<o-c m="setEventSet" s="1;java.beans.EventSetDescriptor">
<o cls="java.beans.EventSetDescriptor">
<o-ctor s="5;str;java.lang.Class;[java.beans.MethodDescriptor;java.lang.reflect.Method;java.lang.reflect.Method">
<str>propertyChange</str>
<class>java.beans.PropertyChangeListener</class>
<array cls="java.beans.MethodDescriptor" len="1">
<o id="7" cls="java.beans.MethodDescriptor">
<o-ctor s="2;java.lang.reflect.Method;[java.beans.ParameterDescriptor">
<method>java.beans.PropertyChangeListener|propertyChange|1;java.beans.PropertyChangeEvent</method>
<null/>
</o-ctor>
</o>
</array>
<method>java.awt.Container|addPropertyChangeListener|1;java.beans.PropertyChangeListener</method>
<method>java.awt.Component|removePropertyChangeListener|1;java.beans.PropertyChangeListener</method>
</o-ctor>
</o>
</o-c>
<o-c m="setInvokeLater" s="1;b"><true/></o-c>
<o-c m="setJythonCode" s="1;str"><str>print('{}: {}->{}'.format(event.propertyName, event.oldValue, event.newValue))
if event.propertyName == 'myCustomProp':
if event.newValue is None:
print('is none!')
event.source.myCustomProp = event.newValue
if event.propertyName == 'intValue':
event.source.intValue = event.newValue
event.source.parent.getComponent('Numeric Text Field').intValue = event.source.parent.getComponent('Numeric Text Field').intValue + 1 </str></o-c>
<o-c m="setMethodDescriptor" s="1;java.beans.MethodDescriptor"><ref>7</ref></o-c>
<o-c m="setScopeStyle" s="1;com.inductiveautomation.ignition.common.script.ScriptScopeStyle"><ref>2</ref></o-c>
<o-c m="setTarget" s="1;java.awt.Component">
<c id="16" cls="com.inductiveautomation.factorypmi.application.components.PMINumericTextField">
<c-comm>
<p2df id="10">64.0;22.0</p2df>
<r2dd>128.0;77.0;64.0;22.0</r2dd>
<str>Numeric Text Field 1</str>
<lc>128.0;77.0;16;0;0.1875;0.54545456</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<ref>3</ref>
<ref>4</ref>
</c-c>
<c-c m="setDynamicProps" s="1;java.util.TreeMap">
<o cls="java.util.TreeMap">
<o-ctor s="1;java.util.Comparator"><null/></o-ctor>
<o-c m="put" s="2;O;O">
<str id="8">myCustomProp</str>
<o cls="com.inductiveautomation.factorypmi.application.binding.DynamicPropertyDescriptor">
<o-c m="setDisplayName" s="1;str"><ref>8</ref></o-c>
<o-c m="setName" s="1;str"><ref>8</ref></o-c>
<o-c m="setPropertyType" s="1;java.lang.Class"><class>F</class></o-c>
<o-c m="setShortDescription" s="1;str"><ref>6</ref></o-c>
<o-c m="setValue" s="1;O"><flt>0.0</flt></o-c>
</o>
</o-c>
</o>
</c-c>
</c>
</o-c>
</o>
</arraylist>
<ref>9</ref>
<c cls="com.inductiveautomation.factorypmi.application.components.PMILabel">
<c-comm>
<p2df>212.0;31.0</p2df>
<r2dd>215.0;248.0;212.0;31.0</r2dd>
<str>Label 4</str>
<lc>215.0;248.0;16;0;0.056603774;0.38709676</lc>
</c-comm>
<c-c m="setBackground" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>Number Of PropertyChange Events</str></c-c>
</c>
<c cls="com.inductiveautomation.factorypmi.application.components.PMINumericTextField">
<c-comm>
<ref>10</ref>
<r2dd>438.0;253.0;64.0;22.0</r2dd>
<str>Numeric Text Field</str>
<lc>438.0;253.0;16;0;0.1875;0.54545456</lc>
</c-comm>
<c-c m="putClientProperty" s="2;O;O">
<ref>3</ref>
<ref>4</ref>
</c-c>
<c-c m="setValue" s="1;O"><int>6</int></c-c>
</c>
<c cls="com.inductiveautomation.factorypmi.application.components.PMILabel">
<c-comm>
<p2df id="11">100.0;34.0</p2df>
<r2dd>230.0;100.0;100.0;34.0</r2dd>
<str>Label 3</str>
<lc>230.0;100.0;16;0;0.12;0.3529412</lc>
</c-comm>
<c-c m="setBackground" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>Custom Property</str></c-c>
</c>
<c cls="com.inductiveautomation.factorypmi.application.components.PMILabel">
<c-comm>
<ref>11</ref>
<r2dd>232.0;58.0;100.0;34.0</r2dd>
<str>Label 2</str>
<lc>232.0;58.0;16;0;0.12;0.3529412</lc>
</c-comm>
<c-c m="setBackground" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>Builtin Property</str></c-c>
</c>
<c cls="com.inductiveautomation.factorypmi.application.components.PMILabel">
<c-comm>
<ref>11</ref>
<r2dd>533.0;19.0;100.0;34.0</r2dd>
<str>Label 1</str>
<lc>533.0;19.0;16;0;0.12;0.3529412</lc>
</c-comm>
<c-c m="setBackground" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>Set To None</str></c-c>
</c>
<c cls="com.inductiveautomation.factorypmi.application.components.PMILabel">
<c-comm>
<ref>11</ref>
<r2dd>365.0;20.0;100.0;34.0</r2dd>
<str>Label</str>
<lc>365.0;20.0;16;0;0.12;0.3529412</lc>
</c-comm>
<c-c m="setBackground" s="1;clr"><clr>-328965</clr></c-c>
<c-c m="setText" s="1;str"><str>Set To integer</str></c-c>
</c>
<ref>12</ref>
<ref>13</ref>
<ref>14</ref>
<ref>15</ref>
<ref>16</ref>
</objects>