gpt4 book ai didi

python - wx.Gauge在Windows中无法更新超过25%,在Linux中有效

转载 作者:可可西里 更新时间:2023-11-01 09:31:21 25 4
gpt4 key购买 nike

我似乎对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)来替换打印件,但是即使 sleep 时间很短,该过程也几乎停滞不前,而且如果我进一步降低打印速度,问题会再次出现,所以这几乎没有帮助。

我不知道发生了什么或如何解决它,但是我想事情在Windows下会以某种方式移动得太快,以至于 progress在一段时间后无法正确更新,而是保持固定值(〜25)。我不知道为什么会这样,但是,对我而言这毫无意义。当然, printsleep都不是好的解决方案。即使我打印“无”,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()

最佳答案

因此,实际上,您正在通过长时间运行的任务来阻止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的功能。我不得不在性能选项中关闭“动画控件和Windows内的元素”才能摆脱这种现象。

    关于python - wx.Gauge在Windows中无法更新超过25%,在Linux中有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6003340/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com