Skipping past exceptions when called from Tk events

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()

Hmm, what code is generating the Tk events? Is this in Tk itself? Are you using the Tk event look in TkGlobal.py (and enabled by base.startTk() or by want-tk 1), or some system of your own devising? If you’re using our system, I don’t see anything in there that’s doing exception squelching, so this change in behavior might be something within Tk, not within Panda, but you can try to put print statements in the tkLoop function to help track this down.

David

I’ll try adding print statements to the tkLoop function. Thanks for the hint on where to look.