Catch - 'c4d.BaseDraw' is not alive
-
Dear Developers;
Whats the best practice to handle these conditions where the
c4d.BaseDraw
is not alive anymore or the plugin probably has old data ?!?My hunch is I have to reinitialize on restore / reload but I am clueless how to tackle this.
My environment:
User closes all documents (new unsaved document is displayed)
or the user creates new document - then clicks on plugin.Code snipet:
def __init__(self): self.doc = GetActiveDocument() try: self.rval_bd = self.doc.GetActiveBaseDraw() except ReferenceError as e: print("Error: ", e) """ more code """ def Restore(self, pluginid, sec_ref) : # Set the pointers to the specific sub dialog here # rather than in the CommandData section of this plugin if sec_ref['subid'] == 1: return self.sub_dialog1.Restore(pluginid, sec_ref) else: return super(MainDialog, self).Restore(pluginid, sec_ref)
Where the error occurs:
self.AddCheckbox(ID_SAVEFRAME, flags=c4d.BFH_LEFT, initw=270, inith=0, name="Show Save Frame") self.SetBool(ID_SAVEFRAME, self.rval_bd[c4d.BASEDRAW_DATA_SHOWSAFEFRAME])
ReferenceError: the object 'c4d.BaseDraw' is not alive
The Try test above does nothing in this regard.
The only thing that works is reloading the python plugins ...There is more code than this but this might be relevant ?
class MenuCommand(c4d.plugins.CommandData): dialog = None def Execute(self, doc): if self.dialog is None: self.dialog = MainDialog() return self.dialog.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, defaulth=100, defaultw=300) def RestoreLayout(self, sec_ref): if self.dialog is None: self.dialog = MainDialog() return self.dialog.Restore(PLUGIN_ID, sec_ref)
Kind regards
mogh -
Hey @mogh,
Thank you for reaching out to us. The idea of a node, a
c4d.C4DAtom
not being alive means that the Python layer cannot find the C++C4DAtom
which the Python layerc4d.C4DAtom
was referencing, usually because something has been reallocated in the background while someone was long-term storing ac4d.C4DAtom
instance. We talked about it here in more detail.Sometimes you then must write elaborate interfaces which recapture the same thing over their UUID, e.g., get hold of the same shader once its Python reference has gone bad. But that does not seem necessary here since you are interested only in the active viewport of the active document. It is in this case also not only the case that the viewport could have been reallocated, but also what is the active viewport could have changed (although that will likely cause viewports to be reallocated).
So, when you want to modify the active base draw, in fact any node, make sure to get a fresh reference when possible.
doc: c4d.document.BaseDocument = c4d.documents.GetActiveDocument() bd: c4d.BaseDraw = doc.GetActiveBaseDraw() self.AddCheckbox(ID_SAVEFRAME, flags=c4d.BFH_LEFT, initw=270, inith=0, name="Show Save Frame") self.SetBool(ID_SAVEFRAME, bd[c4d.BASEDRAW_DATA_SHOWSAFEFRAME])
If you need access to the active viewport of the active document very often, you could make it a property with some mild caching.
import c4d import typing class Foo: """Provides cached access to the active document and active viewport. """ def __init__(self) -> None: self._activeDocument: typing.Optional[c4d.documents.BaseDocument] = None self._activeViewport: typing.Optional[c4d.BaseDraw] = None @property def ActiveDocument(self) -> c4d.documents.BaseDocument: """Returns the active document. """ if self._activeDocument is None or not self._activeDocument.IsAlive(): self._activeDocument = c4d.documents.GetActiveDocument() return self._activeDocument @property def ActiveBaseDraw(self) -> c4d.BaseDraw: """Returns the active viewport in the active document. """ if self._activeViewport is None or not self._activeViewport.IsAlive(): self._activeViewport = self.ActiveDocument.GetActiveBaseDraw() return self._activeViewport def Bar(self) -> None: """Makes use of the #ActiveBaseDraw property. """ self.ActiveBaseDraw[c4d.BASEDRAW_DATA_TEXTURES] = True
Although this also suffers from the problem that what is the active viewport could have changed. Caching anything that is "active" is a bad idea as what is active can change. Active entities should always be retrieved when required unless one can guarantee that they cannot change (by being in a modal dialog for example). Cheers,
Ferdinand