【发布时间】:2018-07-15 06:15:21
【问题描述】:
我从一名研究生那里继承了一段用 python 编写的模拟脚本。它每隔 1 秒将其进度打印到终端上,让我们知道它正在运行,偶尔还会对结果进行一些部分总结。一切正常。
然后我们小组决定为这个脚本制作一个简单的 GUI。我已将输出重定向到 textctrl 类,以便进度可以显示在窗口中,而不是停留在终端上。以下是我所写内容的一个非常简化的版本:
import threading
import sys
import wx
import time
def LongSimulation(input_):
# Simulate the behavior of the simulation code that I have
# Actually it returns more than just the progress
# Occasionally it also returns some partial summary result for diagnostic
for i in range(0, 100):
sys.stdout.write('Progress: %3.0f\r' % float(i))
sys.stdout.flush()
time.sleep(1)
# return the simulation result
return 'answer'
def LongSimulationWrapper(q, input_):
result = LongSimulation(input_)
q.put(result)
# subclass of TextCtrl. Just to put the flush method back in
class TextCtrlPipe(wx.TextCtrl):
def __init__(*args, **kargs):
wx.TextCtrl.__init__(*args, **kargs)
def flush(self):
self.Refresh()
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL
sizer = wx.BoxSizer(wx.VERTICAL)
self.log = TextCtrlPipe(panel, wx.ID_ANY, size=(300,100), style=style)
sizer.Add(self.log, 1, wx.ALL|wx.EXPAND, 5)
btn = wx.Button(panel, wx.ID_ANY, 'Start')
self.Bind(wx.EVT_BUTTON, self.onButton, btn)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
def onButton(self, event):
# redirect output to TextCtrl
sys.stdout = self.log
q = queue.Queue()
# simulation input. I just randomly choose a number to demonstrate
input_ = 0
thread = threading.Thread(target=LongSimulationWrapper, args=(q, input_))
thread.setDaemon(True)
thread.start()
# Getting returned result somehow halt the printing of progress
#print(q.get())
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
但是我马上遇到了一些麻烦。当我尝试使用队列来获取返回的结果时,就会出现问题。如果我取消注释行 print(q.get()),则进度不再实时显示。相反,只有在计算完成后才会显示所有内容,这违背了进度条的目的。
我知道 wx.ProgressDialog 类。但是,这不仅仅是我们需要的进展,还需要诊断的部分总结结果。我们仍然需要找到一种方法来重定向所有文本。真正的“LongSimulation”实际上相当复杂,我不完全了解它的内部工作,所以我不愿意改变他们模块的任何部分。鉴于这样的约束,我应该怎么做才能让它发挥作用?
我们还计划在未来整合多处理。 LongSimulation 是一个令人尴尬的并行模拟(具体来说是蒙特卡洛),所以我可以在不同的进程上运行相同的东西并合并结果。假设我们在 n 个核心上运行它,想法是创建 n 个 TextCtrl,每个核心都独立显示每个进程的进度,但我也无法让它工作。我尝试使用多处理并将单个 TextCtrl 传递给 LongSimulationWrapper 以进行标准输出重定向,但 TextCtrl 不能被腌制。有什么建议我可以解决吗?
不涉及更改 LongSimulation 的答案将是首选,但如果不可能或太难实现,请告诉我需要更改哪些内容才能使其正常工作。任何帮助将不胜感激。
我使用的是 python 2.7.9 和 wxPython 3.0.1.1,它在 linux 上运行。
【问题讨论】: