我似乎只有wxPython和跨平台兼容性的麻烦:(
我有以下功能。当用户单击按钮时调用它,它会执行一些可能需要一段时间的工作,在此期间状态栏中会显示进度表。
def Go(self, event):
progress = 0
self.statbar.setprogress(progress)
self.Update()
# ...
for i in range(1, numwords + 1):
progress = int(((float(i) / float(numwords)) * 100) - 1)
self.wrdlst.Append(words.next())
self.statbar.setprogress(progress)
self.Update()
self.wrdlst.Refresh()
# ...
progress = 100
self.PushStatusText(app.l10n['msc_genwords'] % numwords)
self.statbar.setprogress(progress)
在Linux下显然需要调用self.Update()
,否则测量仪在函数退出之前不会更新,这使得它有点无意义。这些调用似乎在Windows下无效(至少Win 7)。
整个过程在Linux下完美运行(调用Update()),但在Windows 7上,仪表似乎停止在20-25%左右,在函数退出前一段时间。所以它应该移动到它应该达到~25%,然后仪表停止移动没有明显的原因,但功能继续正常,并以适当的输出退出。
在我试图找出问题时,我尝试在更新循环内的量规之前插入print progress
行,认为progress
的值可能不是我认为的应该是。令我惊讶的是,仪表现在可以正常工作,但是当我删除print
它停止工作的那一刻。我也可以通过调用time.sleep(0.001)
来替换打印,但即使睡眠时间过短,这个过程仍然会停止,如果我进一步降低它,问题就会恢复,所以它几乎没有用处。 / p>
我无法弄清楚发生了什么或者如何修复它,但是我觉得在Windows下移动速度太快以至于progress
在一段时间后没有正确更新并且只是停留在固定值(~25)。我不知道为什么会这样,但对我来说没有意义。当然,print
和sleep
都不是好的解决方案。即使我打印出“没有”,Windows仍会为不存在的输出打开另一个窗口,这很烦人。
如果您需要更多信息或代码,请与我们联系。
编辑:好的,这是一个有效的应用程序(至少对我来说)有问题。它仍然很长,但我试图删除与手头问题无关的一切。
它适用于Linux,就像完整的应用程序一样。在Windows下,它会失败或工作,具体取决于Go函数中numwords
的值。如果我将其值增加到1000000(100万),问题就会消失。我怀疑这可能取决于系统,因此如果它适用于您,请尝试调整numwords
的值。也许是因为我更改了它,因此它Append()
是一个静态文本而不是像原始代码中那样调用生成器。
但是,使用当前值numwords
(100000),它确实在Windows上失败了。
import wx
class Wordlist(wx.TextCtrl):
def __init__(self, parent):
super(Wordlist, self).__init__(parent,
style=wx.TE_MULTILINE|wx.TE_READONLY)
self.words = []
self.SetValue("")
def Get(self):
return '\r\n'.join(self.words)
def Refresh(self):
self.SetValue(self.Get())
def Append(self, value):
if isinstance(value, list):
value = '\r\n'.join(value)
self.words.append(unicode(value))
class ProgressStatusBar(wx.StatusBar):
def __init__(self, *args, **kwargs):
super(ProgressStatusBar, self).__init__(*args, **kwargs)
self._changed = False
self.prog = wx.Gauge(self, style=wx.GA_HORIZONTAL)
self.prog.Hide()
self.SetFieldsCount(2)
self.SetStatusWidths([-1, 150])
self.Bind(wx.EVT_IDLE, lambda evt: self.__reposition())
self.Bind(wx.EVT_SIZE, self.onsize)
def __reposition(self):
if self._changed:
lfield = self.GetFieldsCount() - 1
rect = self.GetFieldRect(lfield)
prog_pos = (rect.x + 2, rect.y + 2)
self.prog.SetPosition(prog_pos)
prog_size = (rect.width - 8, rect.height - 4)
self.prog.SetSize(prog_size)
self._changed = False
def onsize(self, evt):
self._changed = True
self.__reposition()
evt.Skip()
def setprogress(self, val):
if not self.prog.IsShown():
self.showprogress(True)
if val == self.prog.GetRange():
self.prog.SetValue(0)
self.showprogress(False)
else:
self.prog.SetValue(val)
def showprogress(self, show=True):
self.__reposition()
self.prog.Show(show)
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(*args, **kwargs)
self.SetupControls()
self.statbar = ProgressStatusBar(self)
self.SetStatusBar(self.statbar)
self.panel.Fit()
self.SetInitialSize()
self.SetupBindings()
def SetupControls(self):
self.panel = wx.Panel(self)
self.gobtn = wx.Button(self.panel, label="Go")
self.wrdlst = Wordlist(self.panel)
wrap = wx.BoxSizer()
wrap.Add(self.gobtn, 0, wx.EXPAND|wx.ALL, 10)
wrap.Add(self.wrdlst, 0, wx.EXPAND|wx.ALL, 10)
self.panel.SetSizer(wrap)
def SetupBindings(self):
self.Bind(wx.EVT_BUTTON, self.Go, self.gobtn)
def Go(self, event):
progress = 0
self.statbar.setprogress(progress)
self.Update()
numwords = 100000
for i in range(1, numwords + 1):
progress = int(((float(i) / float(numwords)) * 100) - 1)
self.wrdlst.Append("test " + str(i))
self.statbar.setprogress(progress)
self.Update()
self.wrdlst.Refresh()
progress = 100
self.statbar.setprogress(progress)
class App(wx.App):
def __init__(self, *args, **kwargs):
super(App, self).__init__(*args, **kwargs)
framestyle = wx.MINIMIZE_BOX|wx.CLOSE_BOX|wx.CAPTION|wx.SYSTEM_MENU|\
wx.CLIP_CHILDREN
self.frame = MainFrame(None, title="test", style=framestyle)
self.SetTopWindow(self.frame)
self.frame.Center()
self.frame.Show()
if __name__ == "__main__":
app = App()
app.MainLoop()
编辑2 :下面是一个更简单的代码版本。我认为我不能让它变小。它对我来说仍然存在问题。我可以在IDLE中运行它,或者直接双击Windows中的.py文件,无论哪种方式都是一样的。
我尝试了numwords
的各种值。看起来这个问题实际上并没有像我刚才所说的那样消失,相反,当我增加numwords
时,测量仪会在调用print
之前越来越远。当前值为1.000.000,这个较短版本达到约50%。在上面的较长版本中,1.000.000的值达到约90%,100.000的值达到约25%,而10.000的值仅达到约10%。
在下面的版本中,一旦调用了print
,即使循环必须在那时结束,进度仍会继续并达到99%。在原始版本中,对self.wrdlst.Refresh()
的调用(当numwords为高时需要几秒钟)必须导致仪表暂停。所以我认为发生的事情是这样的:在环路中,仪表仅到达某一点,当环路退出时,功能继续,而仪表保持静止,当功能退出时,仪表继续,直到达到99%。因为打印声明不需要花费很多时间,所以下面的版本使得仪表看起来像是从0%平滑移动到99%,但print
建议不这样做。
import wx
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(*args, **kwargs)
self.panel = wx.Panel(self)
self.gobtn = wx.Button(self.panel, label="Go")
self.prog = wx.Gauge(self, style=wx.GA_HORIZONTAL)
wrap = wx.BoxSizer()
wrap.Add(self.gobtn, 0, wx.EXPAND|wx.ALL, 10)
wrap.Add(self.prog, 0, wx.EXPAND|wx.ALL, 10)
self.panel.SetSizer(wrap)
self.panel.Fit()
self.SetInitialSize()
self.Bind(wx.EVT_BUTTON, self.Go, self.gobtn)
def Go(self, event):
numwords = 1000000
self.prog.SetValue(0)
for i in range(1, numwords + 1):
progress = int(((float(i) / float(numwords)) * 100) - 1)
self.prog.SetValue(progress)
print "Done"
if __name__ == "__main__":
app = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()
答案 0 :(得分:2)
因此,实际上,您正在阻止GUI线程由您长时间运行的任务。它可能会在某些平台和/或计算机上正常运行。
import wx
from wx.lib.delayedresult import startWorker
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(*args, **kwargs)
self.panel = wx.Panel(self)
self.gobtn = wx.Button(self.panel, label="Go")
self.prog = wx.Gauge(self, style=wx.GA_HORIZONTAL)
self.timer = wx.Timer(self)
wrap = wx.BoxSizer()
wrap.Add(self.gobtn, 0, wx.EXPAND|wx.ALL, 10)
wrap.Add(self.prog, 0, wx.EXPAND|wx.ALL, 10)
self.panel.SetSizer(wrap)
self.panel.Fit()
self.SetInitialSize()
self.Bind(wx.EVT_BUTTON, self.Go, self.gobtn)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
def Go(self, event):
# Start actual work in another thread and start timer which
# will periodically check the progress and draw it
startWorker(self.GoDone, self.GoCompute)
self.progress = 0
self.timer.Start(100)
def OnTimer(self, event):
# Timer draws the progress
self.prog.SetValue(self.progress)
def GoCompute(self):
# This method will run in another thread not blocking the GUI
numwords = 10000000
self.prog.SetValue(0)
for i in range(1, numwords + 1):
self.progress = int(((float(i) / float(numwords)) * 100) - 1)
def GoDone(self, result):
# This is called when GoCompute finishes
self.prog.SetValue(100)
self.timer.Stop()
print "Done"
if __name__ == "__main__":
app = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()
另请注意您的例子:
根据经验,每个看起来像这个def Something(self, event)
的方法应该只运行几毫秒。
编辑:我在Windows 7上观察到的另一件事情。在您调用self.prog.SetValue()
时,衡量标准开始增长并在一段时间内增长指定值。它不会“跳”到该值,而是慢慢增长以达到设定值。它似乎是Windows 7的功能。我不得不在性能选项中关闭“Animate控件和窗口内的元素”来摆脱这种行为。