On Friday, June 19, 2020 at 8:33:43 PM UTC-5, Félix wrote:
>
> I'm doing preliminary tests with the "close" method in order to support
> multiple Leo files in leoInteg.
>
This is an excellent question. I think you understand the overall situation
quite well.
I wrote a preliminary answer without realizing you had added the addendum.
I don't see the addendum in my email, presumably because your edit didn't
trigger another email. In future, please reply to yourself, rather than
doing a major edit.
> def closeFile(self, p_paramUnused):
> '''Closes a leo file. A file can then be opened with "openFile"'''
> print("Trying to close opened file " + str(self.commander.changed))
> if self.commander:
> if self.commander.changed:
> return self.sendLeoBridgePackage('closed', False)
> else:
> self.commander.close()
> return self.sendLeoBridgePackage('closed', True)
> return self.sendLeoBridgePackage() # Just send empty as 'did nothing'
>
> After going through the second branch and actually calling the .close()
> method on the controller that was made with the leoBridge, since I didnt
> change anything else to support a 'closed' commander, I expected the
> leoInteg view to fatally crash because all subsequent calls to
> 'getChildren' etc. would obviously fail because the commander was closed.
>
> Surprising thing is : There is no crash and I can seemingly do any
> browsing and editing as I could before the .close() call...
>
> I suspect it's me who's not understanding what the self.commander.close
> method is actually expected to do, or maybe I should be calling something
> like "self.commander.file.close()" or something like that... Or maybe that
> just lowered an "opened" flag somewhere and all objects are still valid
> pointers until python does garbage collecting... i dunno :/
>
> If anyone can shed some light on this it would be greatly appreciated! :)
>
See below.
*EDIT / ADDENDUM WHILE STEPPING THROUGH WITH THE DEBUGGER*
> First it goes through this:
>
> @g.commander_command('close-window')
> def close(self, event=None, new_c=None):
> """Close the Leo window, prompting to save it if it has been changed."""
> g.app.closeLeoWindow(self.frame, new_c=new_c)
>
>
Yes. This is the code that corresponds to c.close(). The decorator injects
this method into to all open commanders!
and then it goes through this without problems ...
> def closeLeoWindow(self, frame, new_c=None, finish_quit=True):
> """
> Attempt to close a Leo window.
>
> Return False if the user veto's the close.
>
> finish_quit - usually True, close Leo when last file closes, but
> False when closing an already-open-elsewhere file
> during initial load, so UI remains for files
> further along the command line.
> """
> c = frame.c
> if 'shutdown' in g.app.debug:
> g.trace(f"changed: {c.changed} {c.shortFileName()}")
> c.endEditing() # Commit any open edits.
> if c.promptingForClose:
> # There is already a dialog open asking what to do.
> return False
> g.app.recentFilesManager.writeRecentFilesFile(c)
> # Make sure .leoRecentFiles.txt is written.
> if c.changed:
> c.promptingForClose = True
> veto = frame.promptForSave()
> c.promptingForClose = False
> if veto: return False
> g.app.setLog(None) # no log until we reactive a window.
> g.doHook("close-frame", c=c)
> #
> # Save the window state for *all* open files.
> g.app.saveWindowState(c)
> g.app.saveEditorDockState(c)
> g.app.commander_cacher.commit()
> # store cache, but don't close it.
> # This may remove frame from the window list.
> if frame in g.app.windowList:
> g.app.destroyWindow(frame)
> g.app.windowList.remove(frame)
> else:
> # #69.
> g.app.forgetOpenFile(fn=c.fileName(), force=True)
> if g.app.windowList:
> c2 = new_c or g.app.windowList[0].c
> g.app.selectLeoWindow(c2)
> elif finish_quit and not g.app.unitTesting:
> g.app.finishQuit()
> return True # The window has been closed.
>
>
Exactly.
Again, since I've got no gui and the commander I'm closing was opened
> through leoBridge, I guess I should just then 'pop' the commander from the
> array I was keeping it in, and consider it "closed" and its 'ressources'
> will be freed eventually? ( as this reference seems to indicate?
> https://stackoverflow.com/questions/49065803/python-delete-objects-and-free-up-space
> )
>
The key fragment in g.app.closeWindow is:
if frame in g.app.windowList:
g.app.destroyWindow(frame)
g.app.windowList.remove(frame)
else:
# #69.
g.app.forgetOpenFile(fn=c.fileName(), force=True)
g.app.destroyWindow is:
def destroyWindow(self, frame):
"""Destroy all ivars in a Leo frame."""
if 'shutdown' in g.app.debug:
g.pr(f"destroyWindow: {frame.c.shortFileName()}")
if g.app.externalFilesController:
g.app.externalFilesController.destroy_frame(frame)
if frame in g.app.windowList:
# g.pr('destroyWindow', (g.app.windowList)
g.app.forgetOpenFile(frame.c.fileName())
# force the window to go away now.
# Important: this also destroys all the objects of the commander.
frame.destroySelf()
*However*, the bridge uses a nullFrame, and nullFrame.destroySelf is a
do-nothing.
So I conclude that the commander is *not* destroyed immediately. Otoh, the
(null) frame *is* removed from g.app.windowList, so there is one less
reference to the commander. It's possible that the gc could remove the
commander later.
*Summary*
I think you understand the situation quite well.
c.close does the following:
- Prompts for save if the commander has changed.
- Clears c.frame from g.app.windowList.
- (Supposedly) completely destroys c.frame and c. But this doesn't happen
when using a nullFrame.
My advice is not to worry about whether c has been garbage collected. You
can safely access c if and *only *if c.frame exists in g.app.windowList.
However, there is no guarantee that this advice is correct.
HTH.
Edward
--
You received this message because you are subscribed to the Google Groups
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/leo-editor/4286a47d-9ce3-49e2-95df-20b194fb142bo%40googlegroups.com.