Launch a panda3d app window from a tkinter based python app

Hi,
i am developing a algorithmic model the interface of which is developed in Tkinter (python 3.6.6).
at some point the interface needs to visualize the 3d-model of a vehicle therefore it opens a panda3d application.
everything is fine until the moment that one needs to click on something on the Tkinter interface. i get:

Fatal Python error: PyEval_RestoreThread: NULL tstate

Current thread 0x000048cc (most recent call first):

  File "W:\Geeglee\ives\venv\lib\site-packages\direct\showbase\ShowBase.py", line 2002 in __igLoop
  File "W:\Geeglee\ives\venv\lib\site-packages\direct\task\Task.py", line 485 in step
  File "W:\Geeglee\ives\venv\lib\site-packages\direct\showbase\ShowBase.py", line 3065 in __tkTimerCallback
  File "p:\python\Lib\tkinter\__init__.py", line 749 in callit
  File "W:\Geeglee\ives\venv\lib\site-packages\Pmw\Pmw_2_0_1\lib\PmwBase.py", line 1776 in __call__
  File "p:\python\Lib\tkinter\__init__.py", line 1283 in mainloop
  File "W:\Geeglee\ives\venv\lib\site-packages\direct\showbase\ShowBase.py", line 3079 in tkRun
  File "W:/Geeglee/ives/main_gui.py", line 138 in ives3d
  File "W:/Geeglee/ives/main_gui.py", line 47 in __init__
  File "W:/Geeglee/ives/main_gui.py", line 507 in <module>

this happens only of both panda3d and Tkinter are open. if one closes the panda3d window everything works fine.

Now, i believe that i need to split Tkinter app and Panda3d app in two different threads. In my mind it is the cleaner way to built this kind of integration but i was not succesfull in doing so.

Right now what i am doing is to add the panda3d app as part of the Tkinter mainloop. I am not sure it is this the cause of my problem or it is related to the general structure of my program. here it is:

from tkinter import *
from tkinter import ttk, Checkbutton, Canvas

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

class TkinterGuiClass(ttk.Frame):
    
    def __init__(self, parent, args_):
    
        ttk.Frame.__init__(self, parent)
        self.app = parent
        self.app3d = None
        
        # create frame
        self.ives = ttk.Frame(parent)  # add a frame to the canvas
        self.ives.grid(column=0, row=0, sticky=(N, W, E, S))
    
        self.launch_panda3d_app()  # launch panda3d app

    def launch_panda3d_app(self):

        # this is in case someone tries to launch a second panda3d app
        try: base.destroy()
        except NameError: pass
    
        self.app3d = Panda3dClass(self.ives)
        
        try:
            self.app3d.run()
        except SystemExit:
            base.destroy()  # so if i close the panda3d window it will not shut down tkinter as well

class Panda3dClass(ShowBase):
    
    def __init__(self, ives_data):
        
        self.ives_data = ives_data
        
        try:
            ShowBase.__init__(self, windowType='none')
        except Exception as e:  # this is an error when it tries to start a new instance of ShowBase
            if str(e) == 'Attempt to spawn multiple ShowBase instances!':
                return
            else:
                raise Exception
        
        # so far it has created a ShowBase with no window, now create a ShowBase instance with a window
        base.destroy()
        ShowBase.__init__(self, windowType='onscreen')
        
        base.startTk()  # start Tk integration
        base.spawnTkLoop()  # make panda3d part of the Tk mainloop
        
        # main jobs of the 3d-viewer follow here
        
if __name__ == '__main__':
    
    args = ()
    
    app = Tk()
    
    TkinterGuiClass(app, args)
    
    app.mainloop()

My software is in quite an advanced state everything else works like a charm and this problem renders my software useless. I appreciate any help!

Hmm, I’ve seen this before when resizing the Tk window:

I think it has to do with Panda’s window loop dispatching events for Tkinter’s window, and Tkinter not properly taking that into account. However, I’ve never seen it happen for regular click events. Does the Particle Panel sample program show the same issue?

Is this an urgent issue for you?

damn, the only answer that wanted to avoid is a bug!
i have deactivated resizing for both Tkinter and Panda3D so this bug is not only related to resizing, that is sure.
it might be solved by separating the 2 apps in different threads. as i said in my original post i just can’t make it work. is there a suggestion on my sample program on how to do this?
in the meantime i will try the Particle Panel sample program as you suggested.
and yes it is relatively urgent that is before christmas.

I am not sure that separating the sample program into threads will help (it might actually make the problem worse).

I’ll see if I can find some kind of workaround. Are you on Windows?

i use Windows10, tkinter from Python 3.6.6 and Panda3D 1.10.4.1
also i use Pycharm for coding and Panda3D is installed with pip though Pycharm

the Particle Panel seems to work fine. i’ll check-out its structure and try to follow the same.
My opinion is that it is a bug BUT related to my programs flow as it does not seem to happen always. anyways i’ll disect the sample program a bit

ok when i look at the Particle Panel i see that Panda3D is using its own build-in Tkinter (e.g., it starts with “from direct.tkwidgets import Dial” etc) and not the Python’s interpreter Tkinter as in my program.
Which Tkinter version is used in 1.10.4.1? is it possible that the Tkinter that i import and the build-in conflict?

We just use whatever Tkinter is shipped with the Python interpreter. We don’t ship our own version of Tkinter.

Hello,

I think I have checked in a workaround for this bug. Download a development version of Panda here:
https://buildbot.panda3d.org/downloads/55f624e073f0eb35b2520ebec872958fd6d9f571/

many thanks for the effort i will get the version.
although just yesterday i have finished the implementation of my solution which of course is not as sofisticated as the one you propose.
the only thing i did was to isolate the problem which comes down to actions on the main Tkinter window, the main frame (e.g., where the title appears). i have noticed that panda3d was crashing when i was clicking on menu options but not inside the inner frame were all my other widgets were located (buttons etc).
so i have removed the main frame from sight (with app.overrideredirect(True) ) and redesign the menu to be located inside. resize is also deactivated since i do not need it.
to be on the safe side i also implemented a solution were all panda3d tasks are paused (since it was one of these that was crashing the app, probably iglloop according to the error message) when tkinter has focus an un-paused when panda3d re-gains focus.
so i’ll just add the dev version at top of these and problem solved.
cheers!

OK, great, let me know your experience with the new version.

I noticed that the above link contains no Windows .exe build, which you can find here if you prefer it over tthe .whl install:
https://buildbot.panda3d.org/downloads/1568d1c6b1b040afcc2c12039bc4a3b32ea70e8c/

i got the wheel, it works fine but anyways the problem was anyhow resolved by implementing my ‘crude’ solution. in any case when the new stable version is out i’ll use that anyway.
many thanks for all the effort!