Browse Source

Fix wxPython and tkinter issues on Mac OS X

rdb 9 years ago
parent
commit
12873c0d7e

+ 22 - 5
direct/src/showbase/ShowBase.py

@@ -205,7 +205,9 @@ class ShowBase(DirectObject.DirectObject):
         ## This is used to store the wx.Application object used when want-wx is
         ## This is used to store the wx.Application object used when want-wx is
         ## set or base.startWx() is called.
         ## set or base.startWx() is called.
         self.wxApp = None
         self.wxApp = None
+        self.wxAppCreated = False
         self.tkRoot = None
         self.tkRoot = None
+        self.tkRootCreated = False
 
 
         # This is used for syncing multiple PCs in a distributed cluster
         # This is used for syncing multiple PCs in a distributed cluster
         try:
         try:
@@ -262,6 +264,17 @@ class ShowBase(DirectObject.DirectObject):
             random.seed(seed)
             random.seed(seed)
             #whrandom.seed(seed & 0xff, (seed >> 8) & 0xff, (seed >> 16) & 0xff)
             #whrandom.seed(seed & 0xff, (seed >> 8) & 0xff, (seed >> 16) & 0xff)
 
 
+        # For some reason, wx needs to be initialized before the graphics window
+        if sys.platform == "darwin":
+            if self.config.GetBool("want-wx", 0):
+                import wx
+                self.wxApp = wx.App()
+
+            # Same goes for Tk, which uses a conflicting NSApplication
+            if self.config.GetBool("want-tk", 0):
+                import Pmw
+                self.tkRoot = Pmw.initialise()
+
         # Open the default rendering window.
         # Open the default rendering window.
         if self.windowType != 'none':
         if self.windowType != 'none':
             props = WindowProperties.getDefault()
             props = WindowProperties.getDefault()
@@ -2804,15 +2817,16 @@ class ShowBase(DirectObject.DirectObject):
         updated, but wxPython owns the main loop (which seems to make
         updated, but wxPython owns the main loop (which seems to make
         it happier than the other way around). """
         it happier than the other way around). """
 
 
-        if self.wxApp:
+        if self.wxAppCreated:
             # Don't do this twice.
             # Don't do this twice.
             return
             return
 
 
         init_app_for_gui()
         init_app_for_gui()
 
 
         import wx
         import wx
-        # Create a new base.wxApp.
-        self.wxApp = wx.PySimpleApp(redirect = False)
+        if not self.wxApp:
+            # Create a new base.wxApp.
+            self.wxApp = wx.PySimpleApp(redirect = False)
 
 
         if ConfigVariableBool('wx-main-loop', True):
         if ConfigVariableBool('wx-main-loop', True):
             # Put wxPython in charge of the main loop.  It really
             # Put wxPython in charge of the main loop.  It really
@@ -2847,6 +2861,7 @@ class ShowBase(DirectObject.DirectObject):
                 return task.again
                 return task.again
 
 
             self.taskMgr.add(wxLoop, 'wxLoop')
             self.taskMgr.add(wxLoop, 'wxLoop')
+        self.wxAppCreated = True
 
 
     def __wxTimerCallback(self, event):
     def __wxTimerCallback(self, event):
         if Thread.getCurrentThread().getCurrentTask():
         if Thread.getCurrentThread().getCurrentTask():
@@ -2881,7 +2896,7 @@ class ShowBase(DirectObject.DirectObject):
         updated, but Tkinter owns the main loop (which seems to make
         updated, but Tkinter owns the main loop (which seems to make
         it happier than the other way around). """
         it happier than the other way around). """
 
 
-        if self.tkRoot:
+        if self.tkRootCreated:
             # Don't do this twice.
             # Don't do this twice.
             return
             return
 
 
@@ -2889,7 +2904,8 @@ class ShowBase(DirectObject.DirectObject):
         import Pmw
         import Pmw
 
 
         # Create a new Tk root.
         # Create a new Tk root.
-        self.tkRoot = Pmw.initialise()
+        if not self.tkRoot:
+            self.tkRoot = Pmw.initialise()
         builtins.tkroot = self.tkRoot
         builtins.tkroot = self.tkRoot
 
 
         init_app_for_gui()
         init_app_for_gui()
@@ -2925,6 +2941,7 @@ class ShowBase(DirectObject.DirectObject):
                 return task.again
                 return task.again
 
 
             self.taskMgr.add(tkLoop, 'tkLoop')
             self.taskMgr.add(tkLoop, 'tkLoop')
+        self.tkRootCreated = True
 
 
     def __tkTimerCallback(self):
     def __tkTimerCallback(self):
         if not Thread.getCurrentThread().getCurrentTask():
         if not Thread.getCurrentThread().getCurrentTask():

+ 20 - 0
panda/src/cocoadisplay/cocoaPandaApp.mm

@@ -26,4 +26,24 @@
     [super sendEvent: event];
     [super sendEvent: event];
   }
   }
 }
 }
+
+- (void) _setup: (void *) interp {
+  // This is called by Tk when it launches and naively assumes that it is
+  // the first to create an NSApplication.  We can't do anything about it
+  // at this point except display an error message.
+
+  cocoadisplay_cat.error()
+    << "Detected attempt to initialize Tk after creating a Panda window.  "
+       "This will likely cause a crash.\n"
+       "To fix this, set 'want-tk true' in Config.prc to force "
+       "initialization of Tk before opening the Panda window.\n";
+}
+
+- (void) _setupEventLoop {
+  NSAutoreleasePool *pool = [NSAutoreleasePool new];
+  [self finishLaunching];
+  [self setWindowsNeedUpdate:YES];
+  [pool drain];
+}
+
 @end
 @end

+ 4 - 3
samples/particles/particle_panel.py

@@ -17,13 +17,14 @@ try:
 except:
 except:
     sys.exit("Please install Python megawidgets")
     sys.exit("Please install Python megawidgets")
 
 
+# Makes sure that Panda is configured to play nice with Tkinter
+from panda3d.core import *
+loadPrcFileData("", "want-tk true")
+
 # Open the Panda window
 # Open the Panda window
 from direct.showbase.ShowBase import ShowBase
 from direct.showbase.ShowBase import ShowBase
 base = ShowBase()
 base = ShowBase()
 
 
-# Makes sure that Panda is configured to play nice with Tkinter
-base.startTk()
-
 from direct.tkpanels.ParticlePanel import ParticlePanel
 from direct.tkpanels.ParticlePanel import ParticlePanel
 
 
 pp = ParticlePanel()             # Create the panel
 pp = ParticlePanel()             # Create the panel