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
         ## set or base.startWx() is called.
         self.wxApp = None
+        self.wxAppCreated = False
         self.tkRoot = None
+        self.tkRootCreated = False
 
         # This is used for syncing multiple PCs in a distributed cluster
         try:
@@ -262,6 +264,17 @@ class ShowBase(DirectObject.DirectObject):
             random.seed(seed)
             #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.
         if self.windowType != 'none':
             props = WindowProperties.getDefault()
@@ -2804,15 +2817,16 @@ class ShowBase(DirectObject.DirectObject):
         updated, but wxPython owns the main loop (which seems to make
         it happier than the other way around). """
 
-        if self.wxApp:
+        if self.wxAppCreated:
             # Don't do this twice.
             return
 
         init_app_for_gui()
 
         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):
             # Put wxPython in charge of the main loop.  It really
@@ -2847,6 +2861,7 @@ class ShowBase(DirectObject.DirectObject):
                 return task.again
 
             self.taskMgr.add(wxLoop, 'wxLoop')
+        self.wxAppCreated = True
 
     def __wxTimerCallback(self, event):
         if Thread.getCurrentThread().getCurrentTask():
@@ -2881,7 +2896,7 @@ class ShowBase(DirectObject.DirectObject):
         updated, but Tkinter owns the main loop (which seems to make
         it happier than the other way around). """
 
-        if self.tkRoot:
+        if self.tkRootCreated:
             # Don't do this twice.
             return
 
@@ -2889,7 +2904,8 @@ class ShowBase(DirectObject.DirectObject):
         import Pmw
 
         # Create a new Tk root.
-        self.tkRoot = Pmw.initialise()
+        if not self.tkRoot:
+            self.tkRoot = Pmw.initialise()
         builtins.tkroot = self.tkRoot
 
         init_app_for_gui()
@@ -2925,6 +2941,7 @@ class ShowBase(DirectObject.DirectObject):
                 return task.again
 
             self.taskMgr.add(tkLoop, 'tkLoop')
+        self.tkRootCreated = True
 
     def __tkTimerCallback(self):
         if not Thread.getCurrentThread().getCurrentTask():

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

@@ -26,4 +26,24 @@
     [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

+ 4 - 3
samples/particles/particle_panel.py

@@ -17,13 +17,14 @@ try:
 except:
     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
 from direct.showbase.ShowBase import ShowBase
 base = ShowBase()
 
-# Makes sure that Panda is configured to play nice with Tkinter
-base.startTk()
-
 from direct.tkpanels.ParticlePanel import ParticlePanel
 
 pp = ParticlePanel()             # Create the panel