onchange and execute functions of python object.

Need help, or want to share a macro? Post here!
User avatar
Joel_graff
Posts: 1717
Joined: Fri Apr 28, 2017 4:23 pm
Contact:

Re: onchange and execute functions of python object.

Postby Joel_graff » Mon Sep 09, 2019 12:56 pm

Unfortunately, I'm out of my depth when it comes to GDML. I really have never used it.

That said, it might be helpful to post links to the code you're dealing with (I found what I think is the file, but not sure).

Also, have you looked inside the GDML file to verify that your data is being correctly serialized?

The only other thing I can recommend at this point is to create a test case that is stripped down and focused on just this one issue. A bare-minimum example can go a long way to sorting out the issue, especially for others who aren't familiar with your code or thought process.

Nevermind the fact I often find I discover the problem myself when I do that. :)
FreeCAD Trails workbench for transportation engineering: https://www.github.com/joelgraff/freecad.trails

pivy_trackers 2D coin3D library: https://www.github.com/joelgraff/pivy_trackers
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: onchange and execute functions of python object.

Postby keithsloan52 » Mon Sep 09, 2019 3:16 pm

Hi Joel

Don't think it is anything to do with GDML. My import creates a number of objects implemented as FreeCAD Python Objects.
Normally I would expect people to export to GDML, but somebody tested saving as a FreeCAD file FCStd.
When I/they do a reload of the file I get the messages and nothing displayed.
Its something to do with my implementation of Python Objects.

The code is all at https://github.com/KeithSloan/FreeCAD_Python_GDML

the relevant object for the screen shot and messages is GDMLXtru

Test file loaded is xtru3.gdml in SampleFiles

The Objects are in Mod/GDMLObjects.py

Think it is all to do with the Restore of the Python Objects. A number are nested so the file looks like
screenshot.png
screenshot.png (229.31 KiB) Viewed 216 times
This is the state after gdml has been imported and about to be saved
as a FC file using Save as

The saved FC file
testA.FCStd
(14.12 KiB) Downloaded 7 times
Keith
User avatar
Joel_graff
Posts: 1717
Joined: Fri Apr 28, 2017 4:23 pm
Contact:

Re: onchange and execute functions of python object.

Postby Joel_graff » Mon Sep 09, 2019 3:59 pm

ok, so I got it working on my work machine, only I can't download FCStd's, since they're blocked on the network (zip files...)

I'll try to get it working at some point this week, but I can't really promise anything, atm I'm afraid.

It might be in your best interests to try to write a self-contained macro / example. That way it doesn't have any code in it which isn't directly related to this problem and doesn't require installing your wb.
FreeCAD Trails workbench for transportation engineering: https://www.github.com/joelgraff/freecad.trails

pivy_trackers 2D coin3D library: https://www.github.com/joelgraff/pivy_trackers
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: onchange and execute functions of python object.

Postby keithsloan52 » Fri Mar 27, 2020 7:41 am

Joel_graff wrote:
Thu Sep 05, 2019 3:49 pm
keithsloan52 wrote:
Thu Sep 05, 2019 3:36 pm
Also printing out fp.State gives ['Touched', 'Recompute'] on each on onChange without Restore
So if reassigning the shape is what's causing it, the quick and dirty solution is to set a flag in your fp (e.g. self.no_recompute) and set it to true when reassigning shape.

Then, in your execute function:

Code: Select all

def execute(self, obj):

    if self.no_recompute:
        self.no_recompute = False
        return
    
    ...
Are you getting any 'nested recompute' errors in the Report View?

I'm mostly guessing here, without looking at the actual code.
@Joel_graff I am revisiting this

How do I set no_recompute in class __init__?
Or in my case UpdateSet

I have

Code: Select all

class GDMLBox :
   def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)



But when restoring a file I get

Code: Select all

Traceback (most recent call last):
  File "/home/keith/.FreeCAD/Mod/GDML/freecad/gdml/GDMLObjects.py", line 191, in onChanged
    print(self.UpdateSet)
<class 'AttributeError'>: 'GDMLBox' object has no attribute 'UpdateSet'
onekk
Posts: 348
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: onchange and execute functions of python object.

Postby onekk » Fri Mar 27, 2020 8:09 am

Your code is wrong.

When you instatiate a class, only the instatiate objects have the porpety assigned.

Code: Select all

class GDMLBox :
   def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)


Code: Select all

class GDMLBox :
   UpdateSet = False
   some_other_porp = 0.0
     
def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)

so every object of class GDML_Box will have the attribute UpdateSet, even if it set to the default false,

The method that load and create the objects have to retrieve the saved attributed and then reassign them.

Regards

Carlo D.
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: onchange and execute functions of python object.

Postby keithsloan52 » Fri Mar 27, 2020 8:44 am

onekk wrote:
Fri Mar 27, 2020 8:09 am
Your code is wrong.

When you instatiate a class, only the instatiate objects have the porpety assigned.

Code: Select all

class GDMLBox :
   def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)


Code: Select all

class GDMLBox :
   UpdateSet = False
   some_other_porp = 0.0
     
def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)

so every object of class GDML_Box will have the attribute UpdateSet, even if it set to the default false,

The method that load and create the objects have to retrieve the saved attributed and then reassign them.

Regards

Carlo D.
But I thought there were TWO types of class variable
  • Class Variable
  • Instance Variable
And what you have suggested is a Class Variable.
Why does an Instance Variable not work?

This link https://wiki.freecadweb.org/FeaturePython_Objects uses Type in a similar way to what I am trying to do.
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: onchange and execute functions of python object.

Postby keithsloan52 » Fri Mar 27, 2020 9:37 am

Related question in my case.

If you have an Object which is a Part::FeaturePython and one of the properties is a PropertyPartShape
obj.addProperty("Part::PropertyPartShape","Shape","GDMLBox", "Shape of the Box")

When loading a Saved copy of the file, what is the correct way to code to have the Object displayed?

With

Code: Select all

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
Loading a saved file gives

Code: Select all

GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : Proxy
GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : Visibility
GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : lunit
GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : material
GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : x
GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : y
GDMLBox_world0x55d123d0daa0 State : ['Touched', 'Restore'] prop : z
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : Proxy
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : Visibility
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : lunit
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : material
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : x
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : y
GDMLBox_box0x55d123dd2a40 State : ['Touched', 'Restore'] prop : z
GDMLBox_world0x55d123d0daa0 State : ['Touched'] prop : Visibility
GDMLBox_box0x55d123dd2a40 State : ['Touched'] prop : Visibility
GDMLBox_world0x55d123d0daa0 State : ['Touched'] prop : Shape
File was not written with this version of the topology
GDMLBox_world0x55d123d0daa0 State : ['Touched'] prop : Shape1
GDMLBox_box0x55d123dd2a40 State : ['Touched'] prop : Shape
File was not written with this version of the topology
GDMLBox_box0x55d123dd2a40 State : ['Touched'] prop : Shape1
GDMLBox_world0x55d123d0daa0 State : ['Touched'] prop : ExpressionEngine
GDMLBox_box0x55d123dd2a40 State : ['Touched'] prop : ExpressionEngine
GDMLBox_world0x55d123d0daa0 State : ['Up-to-date'] prop : Visibility
GDMLBox_box0x55d123dd2a40 State : ['Up-to-date'] prop : Visibility
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: onchange and execute functions of python object.

Postby keithsloan52 » Fri Mar 27, 2020 9:40 am

keithsloan52 wrote:
Fri Mar 27, 2020 8:44 am
onekk wrote:
Fri Mar 27, 2020 8:09 am
Your code is wrong.

When you instatiate a class, only the instatiate objects have the porpety assigned.

Code: Select all

class GDMLBox :
   def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)


Code: Select all

class GDMLBox :
   UpdateSet = False
   some_other_porp = 0.0
     
def __init__(self, obj, x, y, z, lunit, material, flag = False):
      ....
      self.UpdateSet = flag
      obj.Proxy = self

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       print(self.UpdateSet)

so every object of class GDML_Box will have the attribute UpdateSet, even if it set to the default false,

The method that load and create the objects have to retrieve the saved attributed and then reassign them.

Regards

Carlo D.
But I thought there were TWO types of class variable
  • Class Variable
  • Instance Variable
And what you have suggested is a Class Variable.
Why does an Instance Variable not work?

This link https://wiki.freecadweb.org/FeaturePython_Objects uses Type in a similar way to what I am trying to do.
Okay I think the problem is when opening a saved file with FeaturePython_Objects the Instance Variables are not reset
as the class __init__ is I assume not being executed.

So if onChanged see's a Restore I am now setting any Instance Variables
i.e.

Code: Select all

def onChanged(self, fp, prop):
       '''Do something when a property has changed'''
       print(fp.Label+" State : "+str(fp.State)+" prop : "+prop)
       # Changing Shape in createGeometry will redrive onChanged
       if ('Restore' in fp.State) :
          self.UpdateSet = False
onekk
Posts: 348
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: onchange and execute functions of python object.

Postby onekk » Fri Mar 27, 2020 10:18 am

It depends on how you create the class.

When you create your object you have to define some actions, one action id the __init__ and this is invoked when creating the object.

But when you load ad object, maybe there will be some onLoad() class, where the appropriate instance variables have to be set when the object is loaded and not created.

Carlo D.
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: onchange and execute functions of python object.

Postby keithsloan52 » Fri Mar 27, 2020 12:34 pm

According to this wiki page https://wiki.freecadweb.org/Creating_a_ ... x,_Part_II there is

onDocumentRestored(self, obj)
Called after a document is restored or a FeaturePython object is copied / duplicated.

Have added to code

Code: Select all

def OnDocumentRestored(self,obj) :
       print('Doc Restored')