【问题标题】:moviepy progress bar in wxPython statusbarwxPython状态栏中的moviepy进度条
【发布时间】:2020-05-30 23:00:49
【问题描述】:

在 Windows 10 上的 Python 3.x 上运行

我正在编写一个脚本来帮助将 .tiff 图像序列自动编译成视频。我正在使用 wxPython 来构建 GUI。首先我创建窗口类并为窗口设置一个全局变量。

global main_window
main_window = self

然后我有一个函数用于写入状态栏并将值打印到控制台(有时我还添加代码以从我发送到此函数的文本中写入日志文件)。

def update_status_bar(window, text):
    status = str(text)
    window.statusbar.SetStatusText(status)
    print(status)
    window.Refresh()
    window.Update()
    wx.SafeYield(win=None, onlyIfNeeded=False)

这是我写的moviePy函数,用来将图像序列转换成ta视频。

def video_from_sequence(image_sequence, video, fps):
    img = []
    update_status_bar(main_window, 'Getting Image Directory')
    path = os.path.dirname(os.path.realpath(image_sequence))
    print(path)
    update_status_bar(main_window, 'Getting List of Image Files')
    for root, dirs, files in os.walk(path):
        for file in files:
            if file.endswith('.tiff'):
                img.append(file)
    os.chdir(path)
    update_status_bar(main_window, 'Creating Video From Image Sequence')
    clip = ImageSequenceClip(img, fps=fps)
    update_status_bar(main_window, clip.write_videofile(video, fps=fps))

打印到控制台确实显示了进度,但是状态栏没有填充当前进程的进度。因为我希望最终将此作为 .pyw 运行,其中状态栏显示进度,对我来说,让进度条显示在状态栏中很重要,但是我很难找到一种方法来做到这一点.

更新(2020 年 6 月 1 日):

我已经设法使用 2 个函数来启动和停止进度条,并使用 2 个面板而不是一个面板创建状态栏。

我在 MainWindow 类中的状态栏代码已更改为:

self.statusbar = self.CreateStatusBar(2)
self.progress_bar = wx.Gauge(self.statusbar, -1, size=(280,25), style=wx.GA_PROGRESS)
self.progress_bar_active = False
self.Show()
self.progress_bar.SetRange(50)
self.progress_bar.SetValue(0)

我的启动动画的函数:

def start_busy_statusbar(window):
    window.count = 0
    window.proc = subprocess.Popen(['ping', '127.0.0.1', '-i', '0.2'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    while True:
        wx.Yield()
        try:
            list_data = window.proc.stdout.readline()
            wx.Yield()
        except:
            break
        if len(list_data) == 0:
            break
        window.progress_bar.Pulse()
        wx.Yield()
        window.count += 1

还有我停止动画的功能:

def stop_busy_statusbar(window):
    window.progress_bar.Destroy()
    window.progress_bar = wx.Gauge(window.statusbar, -1, size=(280, 25), style=wx.GA_PROGRESS)

感觉有点粗糙,但确实有效。

所以调用 video_from_sequence() 函数的 convert() 函数如下所示:

    def convert(self, event):
        start_busy_statusbar(main_window)
        image_sequence = str(self.text_image_sequence_dir.GetValue())
        original_video = str(self.text_original_video_dir.GetValue())
        if image_sequence.endswith('.tiff') and original_video.endswith('.mkv'):
            try:
                new_video = str(original_video)[:-4] + '_1080p.mkv'
                temp_video = str(original_video)[:-4] + '_temp.mkv'
                print(image_sequence)
                print(original_video)
                print(new_video)
                fps = get_frame_rate(original_video)
                print(fps)
                video_from_sequence(image_sequence, temp_video, fps)

            except FileNotFoundError as e:
                e = str(e).replace('Errno 2] ', '')
                e = e.replace('directory:', 'directory:\n')
                warning(e)
            update_status_bar(self, 'Finished')
        else:
            warning('You must enter valid paths for both a tiff sequence and original video.')
        stop_busy_statusbar(main_window)

我现在遇到的是,窗口在处理图像序列时被锁定。有人建议我使用单独的线程或使用 wx.Yield。我不太确定如何执行这些事情。我试过 CallAfter(function(vars)) 但这似乎不起作用。

我复制了代码 sn-ps 来演示它在 wxPython 中的工作原理,但我并不真正理解它是如何工作的,也无法让它在我的脚本中工作。

【问题讨论】:

    标签: python python-3.x wxpython moviepy


    【解决方案1】:

    这是在状态栏中实现进度条的一个非常古老的示例。
    它应该给你足够的工作。

    import wx
    import subprocess
    class MainFrame(wx.Frame):
    
        def __init__(self):
            wx.Frame.__init__(self, None, title='Statusbar progress')
            panel = wx.Panel(self)
            self.start_btn = wx.Button(panel, label='Start')
            self.start_btn.Bind(wx.EVT_BUTTON, self.OnStart)
            self.stop_btn = wx.Button(panel, label='Stop')
            self.stop_btn.Bind(wx.EVT_BUTTON, self.OnStop)
            self.Bind(wx.EVT_CLOSE, self.OnExit)
            self.text = wx.TextCtrl(panel, -1, 'Type text here')
            btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
            btn_sizer.Add(self.start_btn)
            btn_sizer.Add(self.stop_btn)
            btn_sizer.Add(self.text)
            panel.SetSizer(btn_sizer)
            self.statusbar = self.CreateStatusBar(3)
            self.text1 = wx.StaticText(self.statusbar,-1,("Static text"))
            self.text2 = wx.StaticText(self.statusbar,-1,("Count Text"))
            self.progress_bar = wx.Gauge(self.statusbar, -1, style=wx.GA_HORIZONTAL|wx.GA_SMOOTH)
            sizer = wx.BoxSizer(wx.HORIZONTAL)
            sizer.Add(self.text1, 0, wx.ALL, 5)
            sizer.Add(self.text2, 1, wx.ALL, 5)
            sizer.Add(self.progress_bar, 2, wx.ALL, 5)
            self.statusbar.SetSizer(sizer)
            self.Show()
            self.progress_bar.SetRange(50)
            self.progress_bar.SetValue(0)
    
        def OnStart(self,event):
            self.start_btn.Enable(False)
            self.text1.SetLabelText("Started")
            self.count = 0
            self.proc = subprocess.Popen(['ping','127.0.0.1','-i','0.2'],stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            while True:
                try:
                    list_data = self.proc.stdout.readline()
                except:
                    break
                if len(list_data) == 0:
                    break
                self.progress_bar.Pulse()
                self.count+=1
                self.text2.SetLabelText("Count "+str(self.count))
                wx.GetApp().Yield()
    
        def OnStop(self,event):
            self.start_btn.Enable(True)
            self.text1.SetLabelText("Stopped")
            try:
                self.proc.kill()
            except:
                pass
    
        def OnExit(self,event):
            try:
                self.proc.kill()
            except:
                pass
            self.Destroy()
    
    if __name__ == '__main__':
        app = wx.App()
        frame = MainFrame()
        app.MainLoop()
    

    【讨论】:

    • 谢谢。我不确定如何在启动进程和进程结束之间的函数期间调用它。我是否将def __init__(self) 添加到我拥有的当前窗口中?或者也许只是将该类的功能添加到我目前为窗口所拥有的类中?
    • 好的,我已经成功启动了进度条动画,但是我无法让它停止。 .proc.kill() 实际上似乎并没有杀死状态栏。我还注意到,当我开始一个较长的过程(在这种情况下将帧编译成视频)时,进度条会冻结。
    • 如果无法看到您的代码,几乎不可能发表评论。然而proc.kill 只是终止一个子进程,所以你的等价物就是停止图像转换。当您处理long running task 时,整个 gui 都会发生冻结。您可以在适当的时候wx.Yield(),或者如果您正在处理进度条,则可以定期wx.Yield 并为该线程创建一个stop 方法。
    • 我发现我可以使用window.progress_bar.Destroy()window.progress_bar = wx.Gauge(window.statusbar, -1, size=(280, 25), style=wx.GA_PROGRESS) 让动画停止。我不确定wx.Yield()wx.Yield 的合适位置是什么。不过我会继续玩它,如果可以的话,我非常想避免冻结。或者也许我只需要学习如何在另一个线程中运行它。
    • 我已更新页面以包含更多要评估的代码。
    猜你喜欢
    • 2014-04-14
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多