我制作了一个可运行的示例,演示了错误的行为:http://pastebin.com/8KpzD4pw
这个问题非常严重。我在保存文件时有一个wx.ProgressDialog
,在IOError
时,我想关闭进度对话框并显示错误消息。不幸的是,这似乎不可能。在消息框关闭之前,进度对话框公然拒绝关闭:
如您所见,进度对话框下方会显示消息框,因此用户必须手动将焦点切换到消息框以查看其内容。消息框关闭后,进度对话框也会消失。以下是保存功能的代码:
def save(self, path=None):
# Show a loading dialog while the document is staving.
progress = shared.show_loading(self, 'Saving document')
try:
raise IOError('Error message')
if not path:
self.document.save()
else:
self.document.save_to_file(path)
except IOError as e:
progress.done()
message = 'Failed to save file:\n\n{}'.format(e.message)
wx.MessageBox(message, 'Error', wx.OK | wx.ICON_ERROR)
progress.done()
show_loading
和progress.done
功能只是使用wx.ProgressDialog
(source)的快捷方式。
我也尝试使用wx.CallAfter
打开消息框,但无济于事:
# ...
except IOError as e:
message = 'Failed to save file:\n\n{}'.format(e.message)
def show_error():
wx.MessageBox(message, 'Error', wx.OK | wx.ICON_ERROR)
progress.done()
wx.CallAfter(show_error)
# ...
我还尝试在关闭进度对话框和使用wx.MicroSleep
打开消息框之间休眠100毫秒但没有成功。
我在尝试破坏进度对话框后立即尝试调用wx.Yield()
和wx.WakeUpIdle()
,但都没有任何效果。
答案 0 :(得分:4)
出于好奇......你有没有在调用progressdialog.Destroy()后立即尝试使用wx.SafeYield()或wx.Yield()或者wx.YieldIfNeeded()?
你的样品不能直播,所以我只是在黑暗中拍摄。
答案 1 :(得分:4)
我认为Infinity77在这里有正确的答案。人们忘记了GUI调用不是同步的 - 它们在返回时没有完成。 “完成”调用向窗口发送消息,并且响应于此,窗口可能排队几个消息以清理自己。当您启动模型消息框时,它会创建其OWN消息循环,同时将原始消息循环保留为暂停动画。因此,在消息框返回并且主消息循环再次运行之前,无法处理清除消息。 Yield调用将允许那些排队的消息消失。
答案 2 :(得分:1)
wxPython演示展示了如何中断ProgressDialog。它还表明你需要Destroy()而不是Close()它,这是摆脱对话框的常用方法。在您的异常处理程序中,您将要停止ProgressDialog跟踪和Destroy()它的任何内容。然后显示你的MessageBox。
答案 3 :(得分:1)
我找到了一个解决方法。事实证明,我无法在创建后立即删除本机Windows进度对话框。在我被允许销毁它之前,我必须等待一段时间,可能是为了完全初始化对话框。我添加了这段代码:
wx.MilliSleep(50)
进入我的进度对话框快捷键功能,它在打开进度对话框后引入了一个不明显的延迟,并允许我在需要时销毁进度对话框。
完整的快捷功能:
def show_loading(parent, title, message=None, maximum=100):
if not message:
message = title
# A class for the return value.
class LoadingObject(object):
def __init__(self, dialog):
self.dialog = dialog
self.is_done = False
def done(self):
if not self.is_done:
self.dialog.Destroy()
self.is_done = True
def pulse(self, message):
self.dialog.Pulse(message)
def progress(self, current, message=None):
# Don't allow the progress to reach 100%, since it will freeze the
# dialog.
if current >= maximum:
current = current - 1
if message is not None:
self.dialog.Update(current, message)
else:
self.dialog.Update(current)
# Create the progress dialog.
dlg_style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME
dlg = wx.ProgressDialog(
title, message, parent=parent, style=dlg_style, maximum=maximum
)
dlg.Pulse()
# Wait just a little bit to allow the progress dialog to initialize.
wx.MilliSleep(50)
# Return an instance of the LoadingDialog with the progress dialog attached.
return LoadingObject(dlg)
最终保存功能:
def save(self, path=None):
# Show a loading dialog while the document is staving.
progress = shared.show_loading(self, 'Saving document')
try:
if not path:
self.document.save()
else:
self.document.save_to_file(path)
except IOError as e:
message = 'Failed to save file:\n\n{}'.format(e.message)
wx.MessageBox(message, 'Error', wx.OK | wx.ICON_ERROR)
finally:
progress.done()
答案 4 :(得分:1)
我有一个类似的案例,我终于通过致电解决了该问题:
dlg.Update( dlg.GetRange( ) )
似乎,至少当您将进度对话框置于“脉冲”模式时,它不会立即响应销毁呼叫。销毁之前或之后的大量睡眠或屈服不会说服我的进度对话框停止显示。但是,通过简单地将值更新为最大值,似乎可以立即自动销毁(或至少隐藏)自身。
答案 5 :(得分:-1)
我对这个问题略有不同。有一个线性顺序的多个函数调用,并希望在函数调用完成时显示整体进度。我只是将进度条包装在一个函数中,将函数调用及其参数。在异常时我销毁进度条,并引发异常。示例如下:
def _progress_wrap(self, func, *args, **kwargs):
self.count += 1
self.progress.Update(self.count)
res = None
try:
res = func(*args, **kwargs)
except Exception:
self.progress.Destroy()
raise
return(res)