Hi, I'm using this code to call wxPython Phoenix from within Blender. I need to call up a file open dialog but keep Blender responsive. The entire Blender script is at the end of the message. The actual file load function is triggered by a key press from Blender Game Engine (full project here: https://blenderartists.org/forum/showthread.php?404099-DJ-turntable-platter-physics-simulation-Link-sound-pitch-to-platter-rotation-speed ). ----------- class MainWindow(wx.Frame): def __init__(self, parent): wx.Timer.__init__(self) startWorker(self.openFileConsumer, self.openFileWorker) def openFileWorker (self): print ("openFileWorker started") openFileDialog = wx.FileDialog(None, "Open XYZ file", "", "", "XYZ files (*.xyz)|*.xyz", wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if openFileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed idea... # proceed loading the file chosen by the user # this can be done with e.g. wxPython input streams: input_stream = wx.FileInputStream(openFileDialog.GetPath()) if not input_stream.IsOk(): wx.LogError("Cannot open file '%s'."%openFileDialog.GetPath()) return def openFileConsumer (self): print ("openFilecomsumerStarted") print (path) def openFile (cont): print ("File load triggerred") app = wx.App(False) win = MainWindow(None) app.MainLoop() ------------ Both "openFileWorker" and "openFile" are entered because the print message shows in my console. But after some standard GTK errors on my machine like "(blender:6455): Gtk-WARNING **: Error loading theme icon 'edit-find' for stock: Fatal error reading PNG image file: Invalid IHDR data" (I get that when things are working normally) there's a Segmentation Fault. ------------ # Blender 2.77 (sub 3), Commit date: 2016-08-27 22:07, Hash a5261e0 bpy.ops.transform.translate(value=(0, 0, 0), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='ENABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True) # Operator # backtrace ./blender(BLI_system_backtrace+0x1d) [0x1984c7d] ./blender() [0x1030939] /lib/x86_64-linux-gnu/libc.so.6(+0x354a0) [0x7fe6f30374a0] /home/michaelzfreeman/Downloads/blender-2.77-a5261e0-linux-glibc219-x86_64/2.77/python/lib/python3.5/site-packages/wx/libwx_gtk2u_core-3.0.so.0(_ZNK12wxDialogBase23GetParentForModalDialogEP8wxWindowl+0x57) [0x7fe6cf3f81e7] /home/michaelzfreeman/Downloads/blender-2.77-a5261e0-linux-glibc219-x86_64/2.77/python/lib/python3.5/site-packages/wx/libwx_gtk2u_core-3.0.so.0(_ZN8wxDialog9ShowModalEv+0x9b) [0x7fe6cf35d2ab] /home/michaelzfreeman/Downloads/blender-2.77-a5261e0-linux-glibc219-x86_64/2.77/python/lib/python3.5/site-packages/wx/libwx_gtk2u_core-3.0.so.0(_ZN12wxFileDialog9ShowModalEv+0x30) [0x7fe6cf3628d0] /home/michaelzfreeman/Downloads/blender-2.77-a5261e0-linux-glibc219-x86_64/2.77/python/lib/python3.5/site-packages/wx/_core.cpython-35m-x86_64-linux-gnu.so(+0x48d119) [0x7fe6cfd85119] ./blender(PyCFunction_Call+0xb9) [0x296e189] ./blender(PyEval_EvalFrameEx+0x8766) [0x29e7536] ./blender() [0x29e860e] ./blender(PyEval_EvalCodeEx+0x23) [0x29e86e3] ./blender() [0x294a856] ./blender(PyObject_Call+0x5c) [0x291ffac] ./blender(PyEval_EvalFrameEx+0x1010) [0x29dfde0] ./blender() [0x29e860e] ./blender(PyEval_EvalCodeEx+0x23) [0x29e86e3] ./blender() [0x294a856] ./blender(PyObject_Call+0x5c) [0x291ffac] ./blender(PyEval_EvalFrameEx+0x1010) [0x29dfde0] ./blender(PyEval_EvalFrameEx+0x754c) [0x29e631c] ./blender(PyEval_EvalFrameEx+0x754c) [0x29e631c] ./blender() [0x29e860e] ./blender(PyEval_EvalCodeEx+0x23) [0x29e86e3] ./blender() [0x294a784] ./blender(PyObject_Call+0x5c) [0x291ffac] ./blender() [0x2939e84] ./blender(PyObject_Call+0x5c) [0x291ffac] ./blender(PyEval_CallObjectWithKeywords+0x47) [0x29dece7] ./blender() [0x2a350e2] /lib/x86_64-linux-gnu/libpthread.so.0(+0x76fa) [0x7fe6f46136fa] /lib/x86_64-linux-gnu/libc.so.6(clone+0x6d) [0x7fe6f3108b5d] ------------------- I must admit I'm a bit new to wxPython and Python in general, but at least as far as I can see, it should work. However I have studied https://wiki.wxpython.org/LongRunningTasks and https://wiki.wxpython.org/Non-Blocking%20Gui as well as http://stackoverflow.com/questions/7666368/how-to-implement-a-thread-in-a-wxpython-gui-application ... ... and it suggests that I can't create any wx GUI item in another thread only in the main thread, hence the crash ? -------------------- # DJ Turntable Physics Simulation, Michael Z Freeman, 2016 # See CHANGELOG.TXT, TODO.TXT & README.TXT in text data blog for changelog. # Better name "Blender Game Engine DJ Turntable" ? # This is the main Python file for the project. I decided not to pile everything into a single monolithic Python script without logic blocks. I found the logic blocks helped me think through the problems and keep things fairly simple. I also think the logic blocks make it easier for new comers to understand what is going on. So the script is arranged into "modules" that are referenced in the logic blocks. There is only one at the moment but that will probably change when, for example, file loading is added. Controls are only through the keyboard at the moment and will probably change: #MOUSE CLICK LEFT/RIGHT: nudge/spin platter left/right. # SPACE: start/stop. # ALT: Deck on/off from bge import logic import bge import aud import bpy import wave import contextlib import wx from wx.lib.delayedresult import startWorker backwards_position = None forwards_position = None trap = 0 track_path1 = "/home/michaelzfreeman/DJ_Barney_Docs/Docs/Forge/Music/Beat-Research-Forge/DJ Turntable Platter Phyics Simulation/SEEDS_MASTER_3.wav" track_path2 = "/home/michaelzfreeman/DJ_Barney_Docs/Docs/Forge/Music/Beat-Research-Forge/DJ Turntable Platter Phyics Simulation/Party For Your Right To Fight -- Public Enemy -- It Takes a Nation of Millions to Hold us Back.wav" device = aud.device() track = aud.Factory.file(track_path2) # Find length of track with contextlib.closing(wave.open(track_path2,'r')) as f: frames = f.getnframes() rate = f.getframerate() track_duration = frames / float(rate) print("Duration: ") print(track_duration) # Setup handle to playing track. # MUST be buffered for reverse. Where is this in documentation ? track_buffered_reverse = track.buffer() track_buffered_forward = track.buffer() track_reverse = track_buffered_reverse.reverse() track_forward = track_buffered_forward #track_handle = device.play(track_reverse) #track_handle.pitch = 1 # make sure its stopped because we have not moved the platter yet. #track_handle.stop() #def playTrack (cont): #global track #global track_handle #global track_buffered #global track_reverse #global track_handle_forwards #global track_handle_backwards track_handle_forwards = device.play(track_forward) track_handle_forwards.keep = True track_handle_forwards.pause() track_handle_forwards.loop_count = 0 track_handle_backwards = device.play(track_reverse) track_handle_backwards.keep = True track_handle_backwards.pause() #reverse_handle.reverse #track_handle.pitch = 1 #speed = setPitch #print("Pitch: ") #print(speed) #track = track.pitch(speed) def setPitch (cont): global track global track_handle global track_buffered global track_reverse global track_forward global duration global trap #global backwards_position #global forwards_position #forwards_position = track_handle_forwards.position #backwards_position = track_handle_backwards.position # Get object platter = bge.logic.getCurrentScene().objects['Platter'] rot_speed = platter.worldAngularVelocity.z #print("Rotation speed: ") #print(rot_speed); speed = rot_speed * -1 #print("Pitch: ") #print(speed) if speed >= 0: track_handle_backwards.pause() forwards_position = track_handle_forwards.position backwards_position = track_handle_backwards.position #backwards_position = track_handle_backwards.position #track_handle_forwards.position = forwards_position while (trap == 1): print("Forward trap triggered") track_handle_forwards.position = track_duration - backwards_position trap = 0 track_handle_forwards.resume() # playing/paused/stop status only ever shows as true ? #print("track_handle_backwards.status: ") #print(track_handle_backwards.status) track_handle_forwards.pitch = speed #print("track_handle_forwards.pitch: ") #print(track_handle_forwards.pitch) print("track_handle_forwards.position: ") print(track_handle_forwards.position) else: track_handle_forwards.pause() forwards_position = track_handle_forwards.position backwards_position = track_handle_backwards.position # Only set once until direction changes while (trap == 0): print("Backwards trap triggered") #print("Reverse Set: ") #print(backwards_position - track_duration + forwards_position) track_handle_backwards.position = track_duration - forwards_position trap = 1 #forwards_position = track_handle_forwards.position #track_handle_backwards.position = backwards_position track_handle_backwards.resume() track_handle_backwards.pitch = speed * -1 #print("track_handle_backwards.pitch: ") #print(track_handle_backwards.pitch) print("track_handle_backwards.position: ") print(track_handle_backwards.position) #track = track.pitch(speed) # rot_speed * -1 #track = track.pitch(rot_speed) # Old code for Actuator #sound.pitch = rot_speed * -1 #print("Pitch: ") #print(track.pitch) #return rot_speed; class MainWindow(wx.Frame): def __init__(self, parent): wx.Timer.__init__(self) startWorker(self.openFileConsumer, self.openFileWorker) def openFileWorker (self): print ("openFileWorker started") openFileDialog = wx.FileDialog(None, "Open XYZ file", "", "", "XYZ files (*.xyz)|*.xyz", wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) #if openFileDialog.ShowModal() == wx.ID_CANCEL: # return # the user changed idea... # proceed loading the file chosen by the user # this can be done with e.g. wxPython input streams: #input_stream = wx.FileInputStream(openFileDialog.GetPath()) #if not input_stream.IsOk(): # wx.LogError("Cannot open file '%s'."%openFileDialog.GetPath()) #return def openFileConsumer (self): print ("openFilecomsumerStarted") print (path) def openFile (cont): print ("File load triggerred") app = wx.App(False) win = MainWindow(None) app.MainLoop()