A strange question, In "onChanged" function The change of list data is invalid!?

Need help, or want to share a macro? Post here!
freecadlzh
Posts: 57
Joined: Fri Mar 06, 2020 12:52 pm

A strange question, In "onChanged" function The change of list data is invalid!?

Postby freecadlzh » Mon Jun 29, 2020 4:39 pm

My code is:

Code: Select all


import FreeCAD, FreeCADGui, Part
import pivy
from pivy import coin
import numpy

class AConductor:
  def __init__(self, obj):
     "Add some custom properties to our box feature"
     obj.addProperty("App::PropertyVectorList","PointsList","EM Para", "电荷点集合").PointsList=FreeCAD.PointsList
     obj.Proxy = self

  def execute(self, fp):
     print("class AConductor: execute called")
     
  def onChanged(self, vp, prop):
     print("class AConductor: def onChanged called")

     theLine = FreeCAD.ActiveDocument.getObject("ModelConductor_XXX")
     print("theLine name is:" + theLine.Name)
     
     theLine.PointsList[50].x=111
     theLine.PointsList[50].y=222
     theLine.PointsList[50].z=333

     print("theLine.PointsList[50].x is:" + str(theLine.PointsList[50].x))
     print("theLine.PointsList[50].y is:" + str(theLine.PointsList[50].y))
     print("theLine.PointsList[50].z is:" + str(theLine.PointsList[50].z))
     print("class AConductor: def onChanged end")
     
Out side the class AConductor, I have created a "Mesh::FeaturePython" model named "ModelConductor_XXX" by the following code:

Code: Select all


        d = FreeCAD.activeDocument()
        f = d.addObject("Mesh::FeaturePython", "ModelConductor_XXX") # Create a mesh feature
        f.Mesh = ASubConductorMesh # Assign the mesh object to the internal property

        from CConductor import AConductor,ViewProviderAConductor
        AConductor(f)
        ViewProviderAConductor(f.ViewObject)
        d.recompute()
        
Prog runs:
15.png
15.png (102.08 KiB) Viewed 242 times
In picture can find the error:
In function "def onChanged(self, vp, prop):" I have aready changed the 50th Vector in "PointsList". But the data printed is the original data in 50th Vector in "PointsList"( The original data in 50th Vector is Vector(0,50,20)). That means the code "theLine.PointsList[50].x=111" is "invalid".

My questions are:

1. Why the code "theLine.PointsList[50].x=111" is "invalid"? How to coding to let the change of 50th Vector effect?

2. The code I get the model is "theLine = FreeCAD.ActiveDocument.getObject("ModelConductor_XXX")". I juest can get the model by the model name. This method is very rustic. In the function " def onChanged(self, vp, prop):" have some para such as "self", How to get the model by para "self" ? I tested the code "self.XXXPropertyName" and "self.obj.XXXPropertyName" , can not work.Can give some code sample to get the model in function " def onChanged(self, vp, prop):" ?

Need suggestion, Thanks a lot.
freecadlzh
Posts: 57
Joined: Fri Mar 06, 2020 12:52 pm

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby freecadlzh » Tue Jun 30, 2020 11:08 am

Waiting for help....
User avatar
Chris_G
Posts: 1343
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby Chris_G » Tue Jun 30, 2020 1:22 pm

What I have seen from other *List properties, is that you cannot change an item of the list directly.
You need to copy the list, change the copy, and reassign :

Code: Select all

list_copy = theLine.PointsList
list_copy[50].x = 111
...
theLine.PointsList = list_copy
I have not tested, however.
freecadlzh
Posts: 57
Joined: Fri Mar 06, 2020 12:52 pm

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby freecadlzh » Tue Jun 30, 2020 10:35 pm

Chris_G wrote:
Tue Jun 30, 2020 1:22 pm
What I have seen from other *List properties, is that you cannot change an item of the list directly.
You need to copy the list, change the copy, and reassign :

Code: Select all

list_copy = theLine.PointsList
list_copy[50].x = 111
...
theLine.PointsList = list_copy
I have not tested, however.
Good suggestion.

Follow your code suggestion. Point data changed!
But I still do not know why have to copy the PointList to other list_copy?

And the second question:
The code I get the model is "theLine = FreeCAD.ActiveDocument.getObject("ModelConductor_XXX")". I juest can get the model by the model name. This method is very rustic. In the function " def onChanged(self, vp, prop):" have some para such as "self", How to get the model by para "self" ? I tested the code "self.XXXPropertyName" and "self.obj.XXXPropertyName" , can not work.Can give some code sample to get the model in function " def onChanged(self, vp, prop):" ?

Need help, Thanks a lot.
edi
Posts: 36
Joined: Fri Jan 17, 2020 1:32 pm

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby edi » Wed Jul 01, 2020 9:15 am

But I still do not know why have to copy the PointList to other list_copy?
A list in python is organized as a stack.
You can remove a single item using list.pop(index) and insert a new item at the same position using list.insert(index,Newitem)
freecadlzh
Posts: 57
Joined: Fri Mar 06, 2020 12:52 pm

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby freecadlzh » Wed Jul 01, 2020 9:31 am

edi wrote:
Wed Jul 01, 2020 9:15 am
But I still do not know why have to copy the PointList to other list_copy?
A list in python is organized as a stack.
You can remove a single item using list.pop(index) and insert a new item at the same position using list.insert(index,Newitem)
Good ! Thanks for your reply!
freecadlzh
Posts: 57
Joined: Fri Mar 06, 2020 12:52 pm

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby freecadlzh » Wed Jul 01, 2020 9:33 am

And there still have a question not be answered:

2. The code I get the model is "theLine = FreeCAD.ActiveDocument.getObject("ModelConductor_XXX")". I juest can get the model by the model name. This method is very rustic. In the function " def onChanged(self, vp, prop):" have some para such as "self", How to get the model by para "self" ? I tested the code "self.XXXPropertyName" and "self.obj.XXXPropertyName" , can not work.Can give some code sample to get the model in function " def onChanged(self, vp, prop):" ?

Waiting help...
User avatar
Chris_G
Posts: 1343
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby Chris_G » Wed Jul 01, 2020 1:36 pm

I think you don't have a clear view of how FeaturePython objects work.
FeaturePython objects (I'll call them FPO) are like other FreeCAD document objects.
- they are managed by the document
- it is possible to add properties to them
The very special thing that have FPO objects, compared to other "non-scriptable" objects, is a Proxy property that points to your own python object.
This python "proxy" object that you make (AConductor) will contain some special methods that will be executed by FreeCAD in certain conditions (execute, onChanged, onDelete, etc ...)
These special methods are called by FreeCAD with the FPO object as first argument.
Here is a little example that should help understand :

Code: Select all

class AConductor:
    def execute(self, fpo):
        print("\n-> The FeaturePython Object {}".format(fpo.Label))
        print("has been asked to recompute")
    def onChanged(self, fpo, prop):
        print("\n-> Hello, I am a AConductor object : {}".format(self))
        print("My onChanged method is currently executed")
        print("because the property {}".format(prop))
        print("of the FeaturePython Object {}".format(fpo.Label))
        print("has been changed to {}".format(fpo.getPropertyByName(prop)))
        print("and this FeaturePython Object Proxy points to me : {}".format(fpo.getPropertyByName("Proxy")))

my_fpo = FreeCAD.ActiveDocument.addObject("App::FeaturePython","My_FPO")
my_fpo.addProperty("App::PropertyInteger","Integer")
my_fpo.addProperty("App::PropertyFloat","Float")

my_proxy = AConductor()
my_fpo.Proxy = my_proxy 
# here we don't really need an intermediate variable, it can simply be :
# my_fpo.Proxy = AConductor()

FreeCAD.ActiveDocument.recompute()

Create a new document
Copy / Paste in the python console
Change a property of the FPO object
Look in report view
openBrain
Posts: 4212
Joined: Fri Nov 09, 2018 5:38 pm

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby openBrain » Wed Jul 01, 2020 1:51 pm

Chris_G wrote:
Wed Jul 01, 2020 1:36 pm
I think you don't have a clear view of how FeaturePython objects work.
FeaturePython objects (I'll call them FPO) are like other FreeCAD document objects.
- they are managed by the document
- it is possible to add properties to them
The very special thing that have FPO objects, compared to other "non-scriptable" objects, is a Proxy property that points to your own python object.
This python "proxy" object that you make (AConductor) will contain some special methods that will be executed by FreeCAD in certain conditions (execute, onChanged, onDelete, etc ...)
These special methods are called by FreeCAD with the FPO object as first argument.
Here is a little example that should help understand :
:shock: :shock: :shock:
If you could just add to your example the setup (and eventually change) of a custom property, this would deserve to go straight in the documentation.
I never deep dive in the custom FPO but read some docs and it was very unclear to me. With this simple example you provided, this makes it massively clear. :+1:
User avatar
Chris_G
Posts: 1343
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: A strange question, In "onChanged" function The change of list data is invalid!?

Postby Chris_G » Wed Jul 01, 2020 2:39 pm

openBrain wrote:
Wed Jul 01, 2020 1:51 pm
If you could just add to your example the setup (and eventually change) of a custom property
Do you mean in the __init__() method ? Like the code of the first post :

Code: Select all

  def __init__(self, obj):
     "Add some custom properties to our box feature"
     obj.addProperty("App::PropertyVectorList","PointsList","EM Para", "电荷点集合").PointsList=FreeCAD.PointsList
     obj.Proxy = self
This is not needed.
It is done in my example, outside of the proxy class :

Code: Select all

my_fpo.addProperty("App::PropertyInteger","Integer")
my_fpo.addProperty("App::PropertyFloat","Float")
# or, like above
# fpo.addProperty("App::PropertyVectorList","PointsList","EM Para", "电荷点集合").PointsList=FreeCAD.PointsList
my_fpo.Proxy = AConductor()
The difference between the 2 objects was not clear to me either, at the beginning.
And I think a big part of my confusion was because of this strange use of __init__()