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

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
User avatar
freecadlzh
Posts: 138
Joined: Fri Mar 06, 2020 12:52 pm

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

Post by freecadlzh »

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 1059 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.
User avatar
freecadlzh
Posts: 138
Joined: Fri Mar 06, 2020 12:52 pm

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

Post by freecadlzh »

Waiting for help....
User avatar
Chris_G
Veteran
Posts: 2580
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!?

Post by Chris_G »

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.
User avatar
freecadlzh
Posts: 138
Joined: Fri Mar 06, 2020 12:52 pm

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

Post by freecadlzh »

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: 481
Joined: Fri Jan 17, 2020 1:32 pm

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

Post by edi »

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)
User avatar
freecadlzh
Posts: 138
Joined: Fri Mar 06, 2020 12:52 pm

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

Post by freecadlzh »

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!
User avatar
freecadlzh
Posts: 138
Joined: Fri Mar 06, 2020 12:52 pm

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

Post by freecadlzh »

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
Veteran
Posts: 2580
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!?

Post by Chris_G »

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
Veteran
Posts: 9034
Joined: Fri Nov 09, 2018 5:38 pm
Contact:

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

Post by openBrain »

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
Veteran
Posts: 2580
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!?

Post by Chris_G »

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__()
Post Reply