Losing control of GUI upon playing a wav file
I can't understand why I am loosing control of my GUI even though I am implementing a thread to play a .wav file. Can someone pin point what is incorrect?
#!/usr/bin/env python
import wx, pyaudio, wave, easygui, thread, time, os, sys, traceback, threading
import wx.lib.delayedresult as inbg
isPaused = False
isStopped = False
class Frame(wx.Frame):
def __init__(self):
print 'Frame'
wx.Frame.__init__(self, parent=None, id=-1, title="Jasmine", size=(720, 300))
#initialize panel
panel = wx.Panel(self, -1)
#initialize grid bag
sizer = wx.GridBagSizer(hgap=20, vgap=20)
#initialize buttons
exitButton = wx.Button(panel, wx.ID_ANY, "Exit")
pauseButton = wx.Button(panel, wx.ID_ANY, 'Pause')
prevButton = wx.Button(panel, wx.ID_ANY, 'Prev')
nextButton = wx.Button(panel, wx.ID_ANY, 'Next')
stopButton = wx.Button(panel, wx.ID_ANY, 'Stop')
#add widgets to sizer
sizer.Add(pauseButton, pos=(1,10))
sizer.Add(prevButton, pos=(1,11))
sizer.Add(nextButton, pos=(1,12))
sizer.Add(stopButton, pos=(1,13))
sizer.Add(exitButton, pos=(5,13))
#initialize song time gauge
#timeGauge = wx.Gauge(panel, 20)
#sizer.Add(timeGauge, pos=(3,10), span=(0, 0))
#initialize menuFile widget
menuFile = wx.Menu()
menuFile.Append(0, "L&oad")
menuFile.Append(1, "E&xit")
menuBar = wx.MenuBar()
menuBar.Append(menuFile, "&File")
menuAbout = wx.Menu()
开发者_StackOverflow中文版 menuAbout.Append(2, "A&bout...")
menuAbout.AppendSeparator()
menuBar.Append(menuAbout, "Help")
self.SetMenuBar(menuBar)
self.CreateStatusBar()
self.SetStatusText("Welcome to Jasime!")
#place sizer on panel
panel.SetSizer(sizer)
#initialize icon
self.cd_image = wx.Image('cd_icon.png', wx.BITMAP_TYPE_PNG)
self.temp = self.cd_image.ConvertToBitmap()
self.size = self.temp.GetWidth(), self.temp.GetHeight()
wx.StaticBitmap(parent=panel, bitmap=self.temp)
#set binding
self.Bind(wx.EVT_BUTTON, self.OnQuit, id=exitButton.GetId())
self.Bind(wx.EVT_BUTTON, self.pause, id=pauseButton.GetId())
self.Bind(wx.EVT_BUTTON, self.stop, id=stopButton.GetId())
self.Bind(wx.EVT_MENU, self.loadFile, id=0)
self.Bind(wx.EVT_MENU, self.OnQuit, id=1)
self.Bind(wx.EVT_MENU, self.OnAbout, id=2)
#Load file using FileDialog, and create a thread for user control while running the file
def loadFile(self, event):
foo = wx.FileDialog(self, message="Open a .wav file...", defaultDir=os.getcwd(), defaultFile="", style=wx.FD_MULTIPLE)
foo.ShowModal()
self.queue = foo.GetPaths()
self.threadID = 1
while len(self.queue) != 0:
self.song = myThread(self.threadID, self.queue[0])
self.song.start()
while self.song.isAlive():
time.sleep(2)
self.queue.pop(0)
self.threadID += 1
def OnQuit(self, event):
self.Close()
def OnAbout(self, event):
wx.MessageBox("This is a great cup of tea.", "About Jasmine", wx.OK | wx.ICON_INFORMATION, self)
def pause(self, event):
global isPaused
isPaused = not isPaused
def stop(self, event):
global isStopped
isStopped = not isStopped
class myThread (threading.Thread):
def __init__(self, threadID, wf):
self.threadID = threadID
self.wf = wf
threading.Thread.__init__(self)
def run(self):
global isPaused
global isStopped
self.waveFile = wave.open(self.wf, 'rb')
#initialize stream
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format = self.p.get_format_from_width(self.waveFile.getsampwidth()), channels = self.waveFile.getnchannels(), rate = self.waveFile.getframerate(), output = True)
self.data = self.waveFile.readframes(1024)
isPaused = False
isStopped = False
#main play loop, with pause event checking
while self.data != '':
# while isPaused != True:
# if isStopped == False:
self.stream.write(self.data)
self.data = self.waveFile.readframes(1024)
# elif isStopped == True:
# self.stream.close()
# self.p.terminate()
self.stream.close()
self.p.terminate()
class App(wx.App):
def OnInit(self):
self.frame = Frame()
self.frame.Show()
self.SetTopWindow(self.frame)
return True
def main():
app = App()
app.MainLoop()
if __name__=='__main__':
main()
Your loadFile
method, quite independently of the fact that it delegates song-playing to many threads (which it waits for in strange ways, but, that's another issue), is still monopolizing the wx event loop until it returns. Where you currently have a time.sleep
, try adding app.Yield(True)
(of course you need to make app
visible at that point in your code: simplest though inelegant is to add a global app
at the start of main
.
Event-driven systems typically serve the event loop only when your various event handler methods return: if you have a long-running event handler, you need to explicitly yield control to the event loop once in a while. Various event systems offer different ways to do it: in wx
, it's the Yield
method which I just recommended you try. See the brief description un the docs.
精彩评论