Frame rate drop after tkinter integration

After putting my panda3d window inside tkinter, the frame rate drops from 60 fps to 30 fps. Is this normal or there is something I am doing wrong?

from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties

class PandaApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self,windowType='none')

        self.startTk()

        wp = WindowProperties()
        wp.setParentWindow(self.tkRoot.winfo_id())
        self.makeDefaultPipe()
        wp.set_origin(0, 0)
        wp.set_size(400, 500)
        self.openDefaultWindow(props=wp)

        self.tkRoot.bind("<Configure>", self.resize)

        self.setFrameRateMeter(True)

    def resize(self, event):
        props = WindowProperties()
        props.set_origin(0, 0)
        props.set_size(self.tkRoot.winfo_width(), self.tkRoot.winfo_height())
        self.win.request_properties(props)

app = PandaApp()
app.run()

I think the problem is trivial, since tkinter is a graphical shell, the refresh rate of the program cycle has been intentionally increased. This is not a statement, this is an assumption, I think it was done to offload the processor.

@serega-kkz Well, it’s not trivial in my case. That reduction in the frame rate is on a relatively decent computer. On lower-end devices, it’s worse. When I add a few widgets such as TopLevel windows, the program gets very laggy especially when dragging the TopLevel windows.

I think it’s trivial, since this is a tkinter problem, either it’s artificially done to save CPU time, or just poor widget rendering performance. Judging by your message, rather the latter, it explains a lot.

It should be understood here that the delay on the tkinter side is summed up with the rendering time of the Panda3D frame. This definitely cannot be solved on the Panda3D side, so this is normal, since the problem is the performance of tkinter itself.

I got much better performance after setting the configuration variable tk-main-loop to false, i.e letting panda3d handle the program loop. Apparently it is set to true by default.

However, after doing this, I can nolonger opt out of the program loop by closing the window. Even after I close the window, the programming is still running, somehow. Any hints?

The good news is, I think now you need to intercept the tkinter window closing event and execute userExit.

Here is an example. However, it works for me without it, the application closes by default, I do not change any configuration for Tk.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties

class Tkinter_window(ShowBase):
    def __init__(self):
        ShowBase.__init__(self, windowType = 'none')

        self.startTk()
        self.tkRoot.geometry("800x600")
        self.tkRoot.update()

        props = WindowProperties()
        props.setParentWindow(self.tkRoot.winfo_id())
        props.setOrigin(0, 0)
        props.setSize(self.tkRoot.winfo_width(), self.tkRoot.winfo_height())

        self.win = base.makeDefaultPipe()
        base.openDefaultWindow(props = props)

        scene = self.loader.loadModel("environment")
        scene.reparentTo(render)

        self.setFrameRateMeter(True)

        self.tkRoot.bind("<Configure>", self.resize)
        self.tkRoot.protocol("WM_DELETE_WINDOW", self.on_closing)

    def on_closing(self):
        self.userExit()

    def resize(self, event):
        props = WindowProperties()
        props.set_origin(0, 0)
        props.set_size(self.tkRoot.winfo_width(), self.tkRoot.winfo_height())
        self.win.request_properties(props)

tkinter_window = Tkinter_window()
tkinter_window.run()

At the same time, performance drops by only three frames.

The good news is, I think now you need to intercept the tkinter window closing event and execute userExit .

With tk-main-loop #f in the Config.prc, userExit() still can’t kill the loop, I don’t now why. What has worked in place of it is:

self.destroy(self)
os._exit(0)

I am not comfortable with it because it doesn’t look standard.

Here is an example. However, it works for me without it, the application closes by default, I do not change any configuration for Tk.

If I allow tkinter to handle the loop, opting out of the program is not a problem anymore and I do not need to override the “WM_DELETE_WINDOW” protocol. The main problem would then be the original problem of reduced frame rate.

I checked the effect of tk-main-loop #f, yes it creates the problem you described. Perhaps you are on a Mac OS and have specific problems for the OS, I am on Windows for me there are no problems, as I said earlier, the frame rate drops by a maximum of three frames.

Thus, I cannot help in testing this problem. I think it is not possible to close the Tk window after changing the tk-main-loop settings, there must be a problem that should be published in the error tracker. This is the only solution that will allow us to move forward in solving this problem.

Perhaps there are Mac OS users who can help, if my conclusions are correct that this problem is peculiar only to Mac OS.

I used the tk-main-loop variable to reassign the loop. Faced with the problem of not being able to complete the panda loop, I am publishing a solution that works for me. I think there is nothing unusual about this.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties

class Tkinter_window(ShowBase):
    def __init__(self):
        ShowBase.__init__(self, windowType = 'none')

        self.startTk()
        self.tkRoot.geometry("800x600")
        self.tkRoot.update()

        props = WindowProperties()
        props.setParentWindow(self.tkRoot.winfo_id())
        props.setOrigin(0, 0)
        props.setSize(self.tkRoot.winfo_width(), self.tkRoot.winfo_height())

        self.win = base.makeDefaultPipe()
        base.openDefaultWindow(props = props)

        scene = self.loader.loadModel("environment")
        scene.reparentTo(render)

        self.setFrameRateMeter(True)

        self.tkRoot.bind("<Configure>", self.resize)
        self.tkRoot.protocol("WM_DELETE_WINDOW", self.on_closing)

    def on_closing(self):
        taskMgr.destroy()

    def resize(self, event):
        props = WindowProperties()
        props.set_origin(0, 0)
        props.set_size(self.tkRoot.winfo_width(), self.tkRoot.winfo_height())
        self.win.request_properties(props)

tkinter_window = Tkinter_window()
tkinter_window.run()

It is worth noting that embedding on Windows Panda3D in the Tk window does not significantly decrease the frame rate, within three frames. But when switching tk-main-loop #f, there is a performance recovery.

@serega-kkz I am on windows as well. I have not tested on mac yet. But your last solution looks okay and I am going to go with that. Thank you so much.

1 Like