I need a reliable method of detecting when the user closes the app, an error happens or simply when the process is being terminated. From what I found, the advised method is to catch a window-event and then ask whether base.win.isClosed. That works fine, unless:
You subclass ShowBase
Use directtools
For whatever reason, the direct.showbase.ShowBase is not under direct control of the app.
from pandac.PandaModules import loadPrcFileData
from direct.showbase.ShowBase import ShowBase
from direct.showbase.DirectObject import DirectObject
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# Closing event is not sent/catched when I use any of these lines:
#loadPrcFileData("", "want-directtools #t")
#base = MyApp()
base = ShowBase() # It only works when ShowBase is under control
class ExitCatcher(DirectObject):
def __init__(self):
DirectObject.__init__(self)
self.accept('window-event', self._onExit)
def _onExit(self, *args):
print "some window event catched" # Other events work just fine
if base.win.isClosed():
print "Closing event catched, app can properly clean up."
ExitCatcher()
base.run()
I’m not sure if this is a bug or I’m simply missing something…
Note: You will probably have to run it in some IDE that allows you to see the console even after the app was closed.
This is not a bug. When writing your code you did not consider that - given there are two subscribers to an event - there is no guarantee on the order in which these subscribers are called.
In your case there are two subscribers to the event ‘window-event’:
ShowBase.windowEvent
ExitCatcher._onExit
The tricky thing is that if ShowBase.window is called first then the application will be shut down, so your own listener is not called. But ShowBase does a neat thing - it calls base.exitFunc() before finally shutting down.
Setting base.exitFunc is the reliable way of running cleanup code provided by Panda3D.
(Please note that base.exitFunc has an uppercase ‘F’. There is also base.exitfunc with lowercase ‘f’, which can be set by a user too. But base.exitfunc is assigned to sys.exitfunc, and thus will be invoked at a later point of time, i.e. from inside sys.exit(). Superfluous in my opinion, since a user could also set sys.exit directly)