In the past, when I wrote a Panda program that used a separate Tk window for its GUI, uncaught exceptions behaved the way I expected: they either shut down the program altogether with a Python stack trace, or popped up a Tk window containing the text of the stack trace, and asking if I wanted to continue running the program from the break point. Recently, I’ve noticed that when exceptions occur in code called from a Tk event, execution jumps out of the routine/method where the exception occurred, without any warning, then execution continues as if nothing happened.
I can see how that behavior would be helpful in a delivered game, but it makes errors much harder to track down during development. I suspect it’s a setting that I can change to get the old behavior, but I can’t figure out what that setting might be. Both I and my coworker have Googled for help with this issue, and neither of us has been able to figure out where to find the answer.
Below is a minimal code example demonstrating the problem. Pressing either of the buttons should trigger a “ValueError: math domain error” exception, which you can see by uncommenting the commented-out code, but instead, it skips the “print” line immediately following the square root, and keeps on running.
How can I change Panda’s behavior to give me a stack trace when an exception occurs?
from Tkinter import *
import direct.directbase.DirectStart
from pandac.PandaModules import AmbientLight, DirectionalLight, LightAttrib
from pandac.PandaModules import Vec3, Vec4
from math import sqrt
class TkPanel(Frame):
def __init__(self, callback, master=None):
Frame.__init__(self)
self.grid()
self.button1 = Button(self, text='Push Me',
command=self.onButton1)
self.button1.grid()
self.button2 = Button(self, text='Push Me Too',
command=self.onButton2)
self.button2.grid()
# Store the parent object's callback function
self.callbackFunc = callback
# print "TkPanel constructor is about to compute the square root of -12..."
# dummy = sqrt(-12)
# print "The square root of -12 is", dummy
def onButton1(self):
print "TkPanel.onButton is about to compute the square root of -15..."
dummy = sqrt(-15)
print "The square root of -15 is", dummy
def onButton2(self):
self.callbackFunc()
class World:
def __init__(self):
base.disableMouse()
camera.setPos(0, -8, 0)
self.carousel = loader.loadModel("smiley")
self.carousel.reparentTo(render)
lAttrib = LightAttrib.makeAllOff()
directionalLight = DirectionalLight("directionalLight")
directionalLight.setDirection(Vec3(0, 8, -2.5))
directionalLight.setColor(Vec4(0.9, 0.8, 0.9, 1))
lAttrib = lAttrib.addLight(directionalLight)
render.attachNewNode(directionalLight.upcastToPandaNode())
render.node().setAttrib(lAttrib)
self.GUI = TkPanel(self.panelUpdated)
def panelUpdated(self):
print "World.panelUpdated is about to compute the square root of -13..."
dummy = sqrt(-13)
print "The square root of -13 is", dummy
base.startTk()
w = World()
run()