async def __cbObjectOnSetupFinished(self, name): #FreeCAD python object often get new properties with new versions. This is usually handled in the #"onDocumentRestored" by checking if all properties are available and adding them if not. To enable #cross version compatibility we need to call this function to ensure all relevant properties are available #Note: ideally we would not disable the the doc observer to just catch the new probs. However, it is highly # likely that some other parallel running coroutine did this. Hence we need to figure out the new properties # ourself try: obj = self.onlineDoc.document.getObject(name) self.logger.debug(f"Object ({name}): Finish Setup") if hasattr(obj, "Proxy") and hasattr(obj.Proxy, "onDocumentRestored"): with Observer.blocked(self.onlineDoc.document): props = obj.PropertiesList obj.Proxy.onDocumentRestored(obj) newprops = set(obj.PropertiesList) - set(props) oobj = self.onlineDoc.objects[name] for prop in newprops: oobj.createDynamicProperty(prop) oobj.changeProperty(prop) except Exception as e: self.logger.error( f"Object ({name}): Version upgrade after setup failed: {e}")
async def __cbViewProviderOnSetupFinished(self, name): #see object equivalent for explanation try: obj = self.onlineDoc.document.getObject(name).ViewObject self.logger.debug(f"ViewProvider ({name}): Finish Setup") if hasattr(obj, "Proxy") and hasattr(obj.Proxy, "onDocumentRestored"): with Observer.blocked(self.onlineDoc.document): props = obj.PropertiesList obj.Proxy.onDocumentRestored(obj) newprops = set(obj.PropertiesList) - set(props) ovp = self.onlineDoc.viewproviders[name] for prop in newprops: ovp.createDynamicProperty(prop) ovp.changeProperty(prop) except Exception as e: self.logger.error( f"Object ({name}): Version upgrade after setup failed: {e}") finally: Observer.activateFor(self.onlineDoc.document)
async def asyncLoad(self): # loads the online doc into the freecad doc try: #first we need to get into view mode for the document, to have a steady picture of the current state of things and #to not get interrupted await self.connection.api.call(f"ocp.documents.{self.id}.view", True) #create all document objects! uri = f"ocp.documents.{self.id}.content.Document.Objects.GetObjectTypes" objs = await self.connection.api.call(uri) tasks = [] with Observer.blocked(self.document): for name, objtype in objs.items(): if hasattr(self.document, name): self.document.removeObject(name) # create the FC object fcobj = self.document.addObject(objtype, name) if fcobj.Name != name: raise Exception("Cannot setup object, name wrong") # create and load the online object oobj = OnlineObject(fcobj, self) self.objects[name] = oobj tasks.append(oobj.download(fcobj)) # create and load the online viewprovider if fcobj.ViewObject: ovp = OnlineViewProvider(fcobj.ViewObject, self.objects[name], self) self.viewproviders[name] = ovp tasks.append(ovp.download(fcobj.ViewObject)) #TODO: load document properties # we do this outside of the observer blocking context, as the object loads block themself if tasks: await asyncio.gather(*tasks) except Exception as e: self.logger.error(f"Unable to load document: {e}") traceback.print_exc() finally: await self.connection.api.call(f"ocp.documents.{self.id}.view", False)
async def __cbNewObject(self, name, typeID): try: self.logger.debug(f"Object ({name}): New ({typeID})") #maybe the object exists already (e.g. auto created by another added object like App::Part Origin) if hasattr(self.onlineDoc.document, name): #TODO: check if typeid matches return #we do not add App origins, lines and planes, as they are only Autocreated from parts and bodies #hence they will be created later then the parent is added if typeID in ["App::Origin", "App::Line", "App::Plane"]: return #add the object we want with Observer.blocked(self.onlineDoc.document): obj = self.onlineDoc.document.addObject(typeID, name) #remove touched status. could happen that other objects like origins have been created automatically for added in Observer.createdObjectsWhileDeactivated( self.onlineDoc.document): if added.TypeId == "App::Origin": added.recompute( ) #recompute origin to get viewprovider size correctly (auto updated without change callback) added.purgeTouched() oobj = OnlineObject(obj, self.onlineDoc) self.onlineDoc.objects[obj.Name] = oobj #create the online view provider for that object if obj.ViewObject: ovp = OnlineViewProvider(obj.ViewObject, oobj, self.onlineDoc) self.onlineDoc.viewproviders[obj.Name] = ovp except Exception as e: self.logger.error( f"Object ({name}): Add object online callback failed: {e}")
async def __cbRemoveObject(self, name): try: self.logger.debug(f"Object ({name}): Remove") #remove FC object first with Observer.blocked(self.onlineDoc.document): self.onlineDoc.document.removeObject(name) #remove online object oobj = self.onlineDoc.objects[name] await oobj.close() del (self.onlineDoc.objects[name]) #remove online viewprovider (we do not intercept the special viewprovider removed event) if name in self.onlineDoc.viewproviders: del self.onlineDoc.viewproviders[name] #and our own runner. cannot call from here, as we are running in this runner ourself. hence waitTillCloseout would block asyncio.ensure_future(self.closeRunner(name)) except Exception as e: self.logger.error( f"Object ({name}): Remove object online callback failed: {e}")
def __fcobject_processing(obj): with Observer.blocked(obj.Document) as a, __fcobject_cleanup(obj) as b: yield (a, b)
import asyncio, txaio txaio.config.loop = asyncio.get_event_loop( ) #workaround as component.start(loop=) does not propagate the loop correctly # setup all the collaboration infrastructure # ****************************************** import os from PySide import QtCore from Manager import Manager import Documents.Observer as Observer from OCP import OCPConnection #The Collaboration module provides functions to work on documents with others #for now use simple global variables! connection = OCPConnection() manager = Manager(os.path.dirname(__file__), connection) # bring the UI out of setup mode Interface.uiWidget.setup(manager, connection) #initialize the global FreeCAD document observer Observer.initialize(manager) if os.getenv('OCP_TEST_RUN', "0") == "1": #connect to test server import Test tester = Test.Handler(connection, manager) else: Interface.uiWidget.setMissingPackages(importfail)