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()