使用生成器作为进度通知程序

发布于 2024-07-29 23:27:29 字数 1076 浏览 1 评论 0原文

我目前正在使用生成器作为获取长进程进度的快速方法,我想知道它通常是如何完成的,因为我发现它不是很优雅......

首先让我解释一下,我有一个 engine.py 模块,它可以执行以下操作:一些视频处理(分割、背景/背景减法等)需要花费大量时间(从几秒到几分钟)。

我通过 wxpython 和控制台脚本编写的 GUI 使用此模块。 当我查看如何在 wxpython 中实现进度对话框时,我发现我必须以某种方式获取进度值来更新我的对话框,您会承认这是纯粹的逻辑...... 因此,我决定使用引擎函数中处理的帧数,每 33 帧生成当前帧数,并在处理完成时生成 None 。

通过这样做,它看起来像这样:

dlg = wx.ProgressDialog("Movie processing", "Movie is being written...",
                           maximum = self.engine.endProcessingFrame,self.engine.startProcessingFrame,
                           parent=self,
                           style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_SMOOTH | wx.PD_CAN_ABORT)
state = self.engine.processMovie()
f = state.next()
while f != None:
    c, s = dlg.Update(f, "Processing frame %d"%f)
    if not c:break
    f = state.next()
dlg.Destroy()

效果很好,绝对没有明显的速度损失,但我希望能够调用 processMovie() 函数,而不必处理生成器(如果我不想)。

例如,我使用引擎模块的控制台脚本不关心进度,我可以使用它,但它注定要在没有显示的环境中执行,所以我真的不关心进度......

有人有我想出的另一种设计吗? (使用线程、全局变量、进程等)

我认为必须有一个设计可以干净地完成这项工作:-)

I am currently using generators as a quick way to get the progress of long processes and I'm wondering how is it done usually as I find it not very elegant...

Let me explain first, I have a engine.py module that do some video processing (segmentation, bg/fg subtraction, etc) which takes a lot of time (from seconds to several minutes).

I use this module from a GUI written in wxpython and a console script.
When I looked at how to implement progress dialogs in wxpython, I saw that I must get somehow a progress value to update my dialog, which is pure logic you'll admit...
So I decided to use the number of frame processed in my engine functions, yield the current frame number every 33 frames and yield None when the processing is finished.

by doing that here's what it looks like:

dlg = wx.ProgressDialog("Movie processing", "Movie is being written...",
                           maximum = self.engine.endProcessingFrame,self.engine.startProcessingFrame,
                           parent=self,
                           style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_SMOOTH | wx.PD_CAN_ABORT)
state = self.engine.processMovie()
f = state.next()
while f != None:
    c, s = dlg.Update(f, "Processing frame %d"%f)
    if not c:break
    f = state.next()
dlg.Destroy()

That works very well, there is absolutely no noticeable speed loss, but I would like to be able to call processMovie() function without having to deal with generators if I don't want to.

For instance my console script which uses the engine module doesn't care of the progress, I could use it but it is destined to be executed in an environment where there is no display so I really don't care about the progress...

Anyone with another design that the one I came up with? (using threads, globals, processes, etc)

There must be a design somewhere that does this job cleany I think :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

七堇年 2024-08-05 23:27:31

这听起来像是一个完美的事件案例。 该进程发送一个“状态更新事件”,任何想知道的人(在本例中为对话框)都会监听该事件。

This sounds like a perfect case for events. The process sends a "status update event", and anyone who wants to know (in this case the dialog) listens to that event.

热血少△年 2024-08-05 23:27:31

首先,如果你使用生成器,你不妨将它用作迭代器:

state = self.engine.processMovie()

for f in state:
    c, s = dlg.Update(f, "Processing frame %d"%f)
    if not c:
        break

dlg.Destroy()

并且不要产生 None; 完成后停止让步并离开该函数; 或者引发 StopIteration。 这是结束生成的正确方法(当使用 for 循环时,这是必要的)。

除此之外,我喜欢这个主意。 在我看来,这是生成器的一种非常有效的用途。

您可能希望使 33 可配置(即可作为参数传递给 processMovie); 33 似乎是一个任意的选择,如果您处理一部两小时的电影,我想没有必要每 33 帧更新一次进度条。

First of all, if you use a generator, you might as well use it as an iterator:

state = self.engine.processMovie()

for f in state:
    c, s = dlg.Update(f, "Processing frame %d"%f)
    if not c:
        break

dlg.Destroy()

And don't yield None; stop yielding when you're done and leave the function; alternatively raise StopIteration. This is the correct way of ending generation (and when using a for loop, it's necessary).

Other than that, I like the idea. In my opinion, this is a very valid use of generators.

You might want to make the 33 configurable (i.e. passable to processMovie as a parameter); 33 seems like an arbitrary choice, and if your process a two-hour movie, there's no need to update the progress bar every 33 frames I guess.

○愚か者の日 2024-08-05 23:27:30

使用生成器对此很好,但使用生成器的全部意义在于您可以使用内置语法:

for f in self.engine.processMovie():
    c, s = dlg.Update(f, "Processing frame %d"%f)
    if not c: break

如果您不关心这一点,那么您可以说:

for f in self.engine.processMovie(): pass

或公开一个函数(例如engine.processMovieFull)来执行给你的。

您还可以使用简单的回调:

def update_state(f):
    c, s = dlg.Update(f, "Processing frame %d"%f)
    return c
self.engine.processMovie(progress=update_state)

...但如果您想零碎地处理数据,那就不太好; 回调模型更喜欢一次性完成所有工作——这就是生成器的真正好处。

Using a generator is fine for this, but the whole point of using generators is so you can builtin syntax:

for f in self.engine.processMovie():
    c, s = dlg.Update(f, "Processing frame %d"%f)
    if not c: break

If you don't care about that, then you can either say:

for f in self.engine.processMovie(): pass

or expose a function (eg. engine.processMovieFull) to do that for you.

You could also use a plain callback:

def update_state(f):
    c, s = dlg.Update(f, "Processing frame %d"%f)
    return c
self.engine.processMovie(progress=update_state)

... but that's not as nice if you want to process the data piecemeal; callback models prefer to do all the work at once--that's the real benefit of generators.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文