我的计划不断扩展。
在MainPanel类的MainProgram方法中,我正在使用其他程序中的一些函数。在使用它时,GUI会一直挂起,直到完成,我想通过为此方法使用新线程来解决此问题。
执行此操作时,执行OnRun时出错。它说:
Unhandled exception in thread started by <bound method MainPanel.OnIndex of <__main__.MainPanel; proxy of <Swig Object of type 'wxPanel *' at 0x526e238> >>
它认为这与onIndex设置som值与self.textOutput有关。现在,我该如何解决我的这个小问题?
非常感谢帮助! =)
import wx, thread
ID_EXIT = 110
class MainPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.buttonRun = wx.Button(self, label="Run")
self.buttonRun.Bind(wx.EVT_BUTTON, self.OnRun )
self.buttonExit = wx.Button(self, label="Exit")
self.buttonExit.Bind(wx.EVT_BUTTON, self.OnExit)
self.labelChooseRoot = wx.StaticText(self, label ="Root catalog: ")
self.labelScratchWrk = wx.StaticText(self, label ="Scratch workspace: ")
self.labelMergeFile = wx.StaticText(self, label ="Merge file: ")
self.textChooseRoot = wx.TextCtrl(self, size=(210, -1))
self.textChooseRoot.Bind(wx.EVT_LEFT_UP, self.OnChooseRoot)
self.textScratchWrk = wx.TextCtrl(self, size=(210, -1))
self.textMergeFile = wx.TextCtrl(self, size=(210, -1))
self.textOutput = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
self.sizerF = wx.FlexGridSizer(3, 2, 5, 5)
self.sizerF.Add(self.labelChooseRoot) #row 1, col 1
self.sizerF.Add(self.textChooseRoot) #row 1, col 2
self.sizerF.Add(self.labelScratchWrk) #row 2, col 1
self.sizerF.Add(self.textScratchWrk) #row 2, col 2
self.sizerF.Add(self.labelMergeFile) #row 3, col 1
self.sizerF.Add(self.textMergeFile) #row 3, col 2
self.sizerB = wx.BoxSizer(wx.VERTICAL)
self.sizerB.Add(self.buttonRun, 1, wx.ALIGN_RIGHT|wx.ALL, 5)
self.sizerB.Add(self.buttonExit, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
self.sizer1 = wx.BoxSizer()
self.sizer1.Add(self.sizerF, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL, 10)
self.sizer1.Add(self.sizerB, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.sizer2 = wx.BoxSizer()
self.sizer2.Add(self.textOutput, 1, wx.EXPAND | wx.ALL, 5)
self.sizerFinal = wx.BoxSizer(wx.VERTICAL)
self.sizerFinal.Add(self.sizer1, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.sizerFinal.Add(self.sizer2, 1, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.SetSizerAndFit(self.sizerFinal)
def OnChooseRoot(self, event):
dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
root_path = dlg.GetPath()
self.textChooseRoot.SetValue(root_path)
dlg.Destroy()
def OnRun(self, event):
#Check first if input values are
thread.start_new_thread(self.OnIndex, ())
def OnIndex(self):
#Do something and post to self.textOutput what you do.
def OnExit(self, event):
self.GetParent().Close()
class MainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="IndexGenerator", size=(430, 330),
style=((wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE |
wx.STAY_ON_TOP) ^ wx.RESIZE_BORDER))
self.CreateStatusBar()
self.fileMenu = wx.Menu()
self.fileMenu.Append(ID_EXIT, "E&xit", "Exit the program")
self.menuBar = wx.MenuBar()
self.menuBar.Append(self.fileMenu, "&File")
self.SetMenuBar(self.menuBar)
wx.EVT_MENU(self, ID_EXIT, self.OnExit)
self.Panel = MainPanel(self)
self.CentreOnScreen()
self.Show()
def OnExit(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainWindow()
app.MainLoop()
[编辑:]这是OnRun和OnIndex方法的摘录。有没有()到很多或者是,?
def OnRun(self, Event=None):
#---Check input values, continue if not wrong
if self.CheckValid() == 0:
#Get root directory
root_path = self.textChooseRoot.GetValue()
#Get scratch workspace
scratch_workspace =self.textScratchWrk.GetValue()
#Get merge file
merge_fil = self.textMergeFile.GetValue()
thread.start_new_thread(self.OnIndex, (root_path,scratch_workspace,merge_fil))
def showmsg(self, msg):
self.textOutput.AppendText(msg + "\n")
def OnIndex(self,root_path,scratch_workspace,merge_fil):
#---PUBSUB - publishes a message to the "show.statusbar"
## msg = "Please wait...Program is running..."
## Publisher().sendMessage(("show.statusbar"), msg)
#---START INDEX GENERATOR CODE
gp.overwriteoutput = 1
gp.OutputMFlag = "DISABLED"
gp.OutputZFlag = "DISABLED"
fc_List = {}
#Get log file. For now a constant. Needs to be changed.
logfil = open("C:\\Python26\\Code\\log.txt", mode = "w")
fold_nr = 0
for root_fold, dirs, files in os.walk(root_path):
root_fold_low = root_fold.lower()
if not root_fold_low.find(".gdb") > -1:
fold_nr += 1
tot_t = time.clock()
wx.CallAfter(self.textOutput.AppendText, ("Mappe : " + str(fold_nr) + " : " + root_fold + "\n"))
答案 0 :(得分:2)
与wx对象的所有交互都应该在主线程中。
一个简单的解决方法是使用wx.CallAfter(self.textOutput.SetValue, "output")
而不是self.textOutput.SetValue("output")
。
wx.CallAfter发送到main even循环后,一旦它到达它就会执行什么,因为主循环在主线程中,一切正常。
更新:合并代码片段:
import wx, thread, os, time
ID_EXIT = 110
class Dummy:
pass
gp = Dummy()
class MainPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.buttonRun = wx.Button(self, label="Run")
self.buttonRun.Bind(wx.EVT_BUTTON, self.OnRun )
self.buttonExit = wx.Button(self, label="Exit")
self.buttonExit.Bind(wx.EVT_BUTTON, self.OnExit)
self.labelChooseRoot = wx.StaticText(self, label ="Root catalog: ")
self.labelScratchWrk = wx.StaticText(self, label ="Scratch workspace: ")
self.labelMergeFile = wx.StaticText(self, label ="Merge file: ")
self.textChooseRoot = wx.TextCtrl(self, size=(210, -1))
self.textChooseRoot.Bind(wx.EVT_LEFT_UP, self.OnChooseRoot)
self.textScratchWrk = wx.TextCtrl(self, size=(210, -1))
self.textMergeFile = wx.TextCtrl(self, size=(210, -1))
self.textOutput = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
self.sizerF = wx.FlexGridSizer(3, 2, 5, 5)
self.sizerF.Add(self.labelChooseRoot) #row 1, col 1
self.sizerF.Add(self.textChooseRoot) #row 1, col 2
self.sizerF.Add(self.labelScratchWrk) #row 2, col 1
self.sizerF.Add(self.textScratchWrk) #row 2, col 2
self.sizerF.Add(self.labelMergeFile) #row 3, col 1
self.sizerF.Add(self.textMergeFile) #row 3, col 2
self.sizerB = wx.BoxSizer(wx.VERTICAL)
self.sizerB.Add(self.buttonRun, 1, wx.ALIGN_RIGHT|wx.ALL, 5)
self.sizerB.Add(self.buttonExit, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
self.sizer1 = wx.BoxSizer()
self.sizer1.Add(self.sizerF, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL, 10)
self.sizer1.Add(self.sizerB, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.sizer2 = wx.BoxSizer()
self.sizer2.Add(self.textOutput, 1, wx.EXPAND | wx.ALL, 5)
self.sizerFinal = wx.BoxSizer(wx.VERTICAL)
self.sizerFinal.Add(self.sizer1, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.sizerFinal.Add(self.sizer2, 1, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.SetSizerAndFit(self.sizerFinal)
def OnChooseRoot(self, event):
dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
root_path = dlg.GetPath()
self.textChooseRoot.SetValue(root_path)
dlg.Destroy()
def CheckValid(self):
return 0
def OnRun(self, Event=None):
#---Check input values, continue if not wrong
if self.CheckValid() == 0:
#Get root directory
root_path = self.textChooseRoot.GetValue()
#Get scratch workspace
scratch_workspace =self.textScratchWrk.GetValue()
#Get merge file
merge_fil = self.textMergeFile.GetValue()
thread.start_new_thread(self.OnIndex, (root_path,scratch_workspace,merge_fil))
def showmsg(self, msg):
self.textOutput.AppendText(msg + "\n")
def OnIndex(self,root_path,scratch_workspace,merge_fil):
#---PUBSUB - publishes a message to the "show.statusbar"
## msg = "Please wait...Program is running..."
## Publisher().sendMessage(("show.statusbar"), msg)
#---START INDEX GENERATOR CODE
gp.overwriteoutput = 1
gp.OutputMFlag = "DISABLED"
gp.OutputZFlag = "DISABLED"
fc_List = {}
#Get log file. For now a constant. Needs to be changed.
#logfil = open("C:\\Python26\\Code\\log.txt", mode = "w")
fold_nr = 0
for root_fold, dirs, files in os.walk(root_path):
root_fold_low = root_fold.lower()
if not root_fold_low.find(".gdb") > -1:
fold_nr += 1
tot_t = time.clock()
wx.CallAfter(self.textOutput.AppendText, ("Mappe : " + str(fold_nr) + " : " + root_fold + "\n"))
def OnExit(self, event):
self.GetParent().Close()
class MainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="IndexGenerator", size=(430, 330),
style=((wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE |
wx.STAY_ON_TOP) ^ wx.RESIZE_BORDER))
self.CreateStatusBar()
self.fileMenu = wx.Menu()
self.fileMenu.Append(ID_EXIT, "E&xit", "Exit the program")
self.menuBar = wx.MenuBar()
self.menuBar.Append(self.fileMenu, "&File")
self.SetMenuBar(self.menuBar)
wx.EVT_MENU(self, ID_EXIT, self.OnExit)
self.Panel = MainPanel(self)
self.CentreOnScreen()
self.Show()
def OnExit(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainWindow()
app.MainLoop()
答案 1 :(得分:1)
您只需使用线程安全方法将信息发送回主线程。正如其他人所说,你不能直接与主线程交互,否则会发生奇怪的事情。这是一篇关于你可以通过各种方式完成这一壮举的非常好的文章:
http://wiki.wxpython.org/LongRunningTasks
这是关于这个主题的另一篇文章:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
答案 2 :(得分:0)
您无法与其他线程的GUI进行交互。你的回调应该是这样的:
def OnRun(self, event):
<get any data you need from the GUI>
thread.start_new_thread(self.WorkerThread, (parameter1, parameter2, ...))
def WorkerThread(self, parameter1, parameter2, ...):
# do time-consuming work here. To send data to
# the GUI use CallAfter:
wx.CallAfter(self.textOutput.AppendText, "whatever")
关键是在生成工作线程之前,在主线程中进行所有非耗时的GUI交互。一旦工作线程启动,它应该拥有它需要的所有数据。然后,它可以继续完成其工作,并使用CallAfter
与主线程进行通信。