Migrating and upgrading old scripted objects

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
vocx
Posts: 3253
Joined: Thu Oct 18, 2018 9:18 pm

Migrating and upgrading old scripted objects

Postby vocx » Fri Jan 31, 2020 8:21 pm

I still don't know exactly how to use all the functions defined for scripted objects. Maybe this is explained in some old thread but I'd like some clarification.

Basically, I'd like to know how to upgrade a scripted object from an old type to a new type. That is, the new object should be able to read the options of the old object and migrate them to the new object. I believe the attributes and properties of the old object are serialized and stored, and then when the document is opened, these properties are restored. So, how would I get these attributes?

Say that an old object has a variable named my_variable, and a new version of the same object has this variable changed to new_variable. How can the new object read the value of the old variable when the object is restored? Is it possible?

Code: Select all

class SomeClass:
    def __init__():
        self.my_variable = "True"
    # ... rest of the definition
The same class but improved.

Code: Select all

class SomeClass:
    def __init__():
        self.new_variable = "True"
    # ... rest of the definition
If I open the object with the new version of the code, there is a message saying

Code: Select all

<class 'AttributeError'>: 'SomeClass' object has no attribute 'new_variable'
This makes sense because the older object didn't have this variable. So, what is the appropriate test to restore the old variable, and use it in the new class?

Something like this

Code: Select all

class SomeClass:
    def __init__():
    	if self.my_variable:
            self.new_variable = self.my_variable
    # ... rest of the definition
I'm asking this question because I'd like to migrate some classes in Draft.py, which is pretty big and complex, to smaller modules while still being able to open older files.
Always add the important information to your posts if you need help.
To support the documentation effort, and code development, your donation is appreciated: paypal.
User avatar
DeepSOIC
Posts: 7474
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: Migrating and upgrading old scripted objects

Postby DeepSOIC » Fri Jan 31, 2020 9:34 pm

use onDocumentRestored method, that is automatically called after the object has finished reading (including copy-paste and duplicate).
vocx
Posts: 3253
Joined: Thu Oct 18, 2018 9:18 pm

Re: Migrating and upgrading old scripted objects

Postby vocx » Fri Jan 31, 2020 10:30 pm

DeepSOIC wrote:
Fri Jan 31, 2020 9:34 pm
use onDocumentRestored method, that is automatically called after the object has finished reading (including copy-paste and duplicate).
Yeah, but how? An example?
Always add the important information to your posts if you need help.
To support the documentation effort, and code development, your donation is appreciated: paypal.
User avatar
DeepSOIC
Posts: 7474
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: Migrating and upgrading old scripted objects

Postby DeepSOIC » Fri Jan 31, 2020 10:41 pm

vocx wrote:
Fri Jan 31, 2020 10:30 pm
Yeah, but how? An example?
Here's one:
https://github.com/DeepSOIC/Part-o-magi ... st.py#L122

I don't use self.somethingsomething attributes on just about any of my objects, I try to solely rely on properties. But you can use the same approach: use hasattr(self, 'my_variable') to test if the attribute exists/doesn't, and react accordingly.
vocx
Posts: 3253
Joined: Thu Oct 18, 2018 9:18 pm

Re: Migrating and upgrading old scripted objects

Postby vocx » Sun Feb 02, 2020 6:34 pm

DeepSOIC wrote:
Fri Jan 31, 2020 10:41 pm
Here's one:
Thank you for the example. It seems to work. I didn't answer before because I was trying some things, but it seems to work. I'll should probably write a simple example to consider this thread solved.

I still wonder how you would migrate a scripted object based on one Proxy class to a completely different Proxy class. Maybe it'd would have to be done the same, just with a bunch of hasattr()s to check every single property and attribute. Would there be a simpler way?
Always add the important information to your posts if you need help.
To support the documentation effort, and code development, your donation is appreciated: paypal.
User avatar
DeepSOIC
Posts: 7474
Joined: Fri Aug 29, 2014 12:45 am
Location: Saint-Petersburg, Russia

Re: Migrating and upgrading old scripted objects

Postby DeepSOIC » Sun Feb 02, 2020 7:12 pm

vocx wrote:
Sun Feb 02, 2020 6:34 pm
I still wonder how you would migrate a scripted object based on one Proxy class to a completely different Proxy class.
If the changes to the class are significant enough to make all this hasattr rubbish grow too much, I just create a new class, and change the command to create objects based on the new class. And leave the old class in place to let the existing objects in old projects open and still work the way they used to.

For example, in Lattice2, I have two polar arrays: lattice2PolarArray.py and lattice2PolarArray2.py. The former is orphaned, and is kept only to support existing projects. lattice2PolarArray2 is what can be created. So, there is no automatic upgrade, the object can be recreated manually if the upgrade happens to be needed.
vocx
Posts: 3253
Joined: Thu Oct 18, 2018 9:18 pm

Re: Migrating and upgrading old scripted objects

Postby vocx » Sun Feb 02, 2020 7:43 pm

DeepSOIC wrote:
Sun Feb 02, 2020 7:12 pm
...
For example, in Lattice2, I have two polar arrays: lattice2PolarArray.py and lattice2PolarArray2.py. The former is orphaned, and is kept only to support existing projects. ...
I see. We may have to go this route at some point.

I think it would be sensible to provide some commands to migrate the object as possible, but otherwise, yes, it would be a bit complicated to maintain.
Always add the important information to your posts if you need help.
To support the documentation effort, and code development, your donation is appreciated: paypal.
kbwbe
Posts: 939
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

Re: Migrating and upgrading old scripted objects

Postby kbwbe » Sun Feb 02, 2020 8:56 pm

vocx wrote:
Sun Feb 02, 2020 6:34 pm
I still wonder how you would migrate a scripted object based on one Proxy class to a completely different Proxy class.
I have had situations within my A2p-project, where i had to do exactly this.

Here is some code of the A2plus Workbench. Perhaps this helps. (Have a look at the marked code lines)

Code: Select all

class a2p_MigrateProxiesCommand():
    
    def Activated(self):
        flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No
        response = QtGui.QMessageBox.information(
            QtGui.QApplication.activeWindow(), 
            u"Migrate proxies of importedParts to recent version", 
            u"Make sure you have a backup of your files. Proceed?", 
            flags
            )
        if response == QtGui.QMessageBox.Yes:
            doc = FreeCAD.activeDocument()
            for ob in doc.Objects:
                if a2plib.isA2pPart(ob): #<============ Is able to detect obsolete old objects
                    #setup proxies
                    a2p_importedPart_class.Proxy_importPart(ob)     #<======= Change objects proxy...
                    if FreeCAD.GuiUp:
                        a2p_importedPart_class.ImportedPartViewProviderProxy(ob.ViewObject)  #<======= Change objects VP proxy...
                    #delete obsolete properties
                    deleteList = []
                    tmp = ob.PropertiesList
                    for prop in tmp:
                        if prop.startswith('pi_') or prop == 'assembly2Version':
                            deleteList.append(prop)
                    for prop in deleteList:
                        ob.removeProperty(prop)
                        
        QtGui.QMessageBox.information(
            QtGui.QApplication.activeWindow(), 
            u"The proxies have been migrated.", 
            u"Please save and reopen this assembly file" 
            )
        
                

    def GetResources(self):
        return {
            'Pixmap' : ':/icons/a2p_RecursiveUpdate.svg',
            'MenuText': 'Migrate proxies of imported parts',
            'ToolTip': toolTip
            }
    
FreeCADGui.addCommand('a2p_MigrateProxiesCommand', a2p_MigrateProxiesCommand())
P.S. The new viewproviders make use of the function "onDocumentRestored" mentioned by DeepSOIC to add new and missing properties.
KBWBE

https://github.com/kbwbe/A2plus
latest release: v0.4.45c, installable via FreeCAD's addon manager
Tutorial: gripper assembly https://www.youtube.com/watch?v=QMxcQ5tssWk
Documentation: https://www.freecadweb.org/wiki/A2plus_Workbench