wxPython +子进程。 ProgressDialog 在 Windows 下不起作用

发布于 2024-12-11 10:53:17 字数 3165 浏览 0 评论 0原文

我有一个 GUI 应用程序,它使用 subprocess 启动一些命令,然后通过读取 subprocess.Popen.stdout 并使用 wx.ProgressDialog 显示该命令的进度。我已经在 Linux 下编写了该应用程序,并且运行完美,但我现在正在 Windows 下进行一些测试,似乎尝试更新进度对话框会导致应用程序挂起。没有错误消息或任何东西,所以我很难弄清楚发生了什么。下面是一个简化的代码:

在主线程中通过此方法在单独的线程中启动子进程:

def onOk(self,event):        
    """ Starts processing """
    self.infotxt.Clear()
    args = self.getArgs()
    self.stringholder = args['outfile']                

    if (args):                       
        cmd = self.buildCmd(args, True)

        if (cmd):
            # Make sure the output directory is writable.
            if not self.isWritable(args['outfile']):
                print "Cannot write to %s. Make sure you have write permission or select a different output directory." %os.path.dirname(args['outfile'])
            else:                
                try:
                    self.thread = threading.Thread(target=self.runCmd,args=(cmd,))
                    self.thread.setDaemon(True)
                    self.thread.start()                    
                except Exception:
                    sys.stderr.write('Error starting thread')

这是 runCmd 方法:

def runCmd(self, cmd):
    """ Runs a command line provided as a list of arguments """
    temp = []
    aborted = False
    dlg = None        
    for i in cmd:
        temp.extend(i.split(' '))

    # Use wx.MutexGuiEnter()/MutexGuiLeave() for anything that accesses GUI from another thread                       
    wx.MutexGuiEnter() 
    max = 100
    stl = wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME
    dlg = wx.ProgressDialog("Please wait", "Processing...", maximum = max, parent = self.frame, style=stl)                               
    wx.MutexGuiLeave()             

    # This is for windows to not display the black command line window when executing the command
    if os.name == 'nt':
        si = subprocess.STARTUPINFO()
        si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        si.wShowWindow = subprocess.SW_HIDE
    else:
        si = None        

    try:
        proc = subprocess.Popen(temp, shell=False, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)            
    except Exception:
        sys.stderr.write('Error executing a command. ')           

    # Progress dialog    
    count = 0

    while True: 
        line=proc.stdout.readline()
        count += 1           

        wx.MutexGuiEnter()
        if dlg.Update(count) == (True, False):
            print line.rstrip()
            wx.MutexGuiLeave()
            if not line: break
        else:
            print "Processing cancelled."
            aborted = True
            wx.MutexGuiLeave()
            proc.kill()
            break 

    wx.MutexGuiEnter()
    dlg.Destroy()
    wx.GetApp().GetTopWindow().Raise()           
    wx.MutexGuiLeave() 

    if aborted:            
        if os.path.exists(self.stringholder):
            os.remove(self.stringholder)

    dlg.Destroy()
    proc.wait()

同样,这在 Linux 下工作正常,但在 Windows 上冻结。如果我删除 dlg.Update() 行,它也可以正常工作。子进程输出在主窗口中打印出来,并显示 ProgressDialog,只是进度条不移动。我缺少什么?

I have a GUI application which launches some commands using subprocess and then shows the progress of this commands by reading from subprocess.Popen.stdout and using wx.ProgressDialog. I've written the app under Linux and it works flawlessly there, but I'm now doing some testing under windows and it seems that trying to update the progress dialog causes the app to hang. There are no error messages or anything, so it's difficult for me to figure out what's happening. Below is a simplified code:

The subprocess is launched in separate thread by this method in main thread:

def onOk(self,event):        
    """ Starts processing """
    self.infotxt.Clear()
    args = self.getArgs()
    self.stringholder = args['outfile']                

    if (args):                       
        cmd = self.buildCmd(args, True)

        if (cmd):
            # Make sure the output directory is writable.
            if not self.isWritable(args['outfile']):
                print "Cannot write to %s. Make sure you have write permission or select a different output directory." %os.path.dirname(args['outfile'])
            else:                
                try:
                    self.thread = threading.Thread(target=self.runCmd,args=(cmd,))
                    self.thread.setDaemon(True)
                    self.thread.start()                    
                except Exception:
                    sys.stderr.write('Error starting thread')

And here's the runCmd method:

def runCmd(self, cmd):
    """ Runs a command line provided as a list of arguments """
    temp = []
    aborted = False
    dlg = None        
    for i in cmd:
        temp.extend(i.split(' '))

    # Use wx.MutexGuiEnter()/MutexGuiLeave() for anything that accesses GUI from another thread                       
    wx.MutexGuiEnter() 
    max = 100
    stl = wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME
    dlg = wx.ProgressDialog("Please wait", "Processing...", maximum = max, parent = self.frame, style=stl)                               
    wx.MutexGuiLeave()             

    # This is for windows to not display the black command line window when executing the command
    if os.name == 'nt':
        si = subprocess.STARTUPINFO()
        si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        si.wShowWindow = subprocess.SW_HIDE
    else:
        si = None        

    try:
        proc = subprocess.Popen(temp, shell=False, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)            
    except Exception:
        sys.stderr.write('Error executing a command. ')           

    # Progress dialog    
    count = 0

    while True: 
        line=proc.stdout.readline()
        count += 1           

        wx.MutexGuiEnter()
        if dlg.Update(count) == (True, False):
            print line.rstrip()
            wx.MutexGuiLeave()
            if not line: break
        else:
            print "Processing cancelled."
            aborted = True
            wx.MutexGuiLeave()
            proc.kill()
            break 

    wx.MutexGuiEnter()
    dlg.Destroy()
    wx.GetApp().GetTopWindow().Raise()           
    wx.MutexGuiLeave() 

    if aborted:            
        if os.path.exists(self.stringholder):
            os.remove(self.stringholder)

    dlg.Destroy()
    proc.wait()

Again this works fine under Linux, but freezes on Windows. If I remove dlg.Update() line it also works fine. The subprocess output is printed out in main window and ProgressDialog is shown, just progressbar doesn't move. What am I missing?

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

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

发布评论

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

评论(1

喜爱纠缠 2024-12-18 10:53:17

尽量不要使用wx.MutexGuiEnterwx.MutexGuiLeave。您可以使用 wx.CallAfter 从另一个线程更新 GUI。我以前从未见过有人在 wx 应用程序中使用这些互斥体,甚至在使用线程的教程或示例中也没有见过。

Try not using wx.MutexGuiEnter and wx.MutexGuiLeave. You can handle updating the GUI from another thread using wx.CallAfter. I have never seen anyone using those mutexes in wx application before and not even in tutorials or examples of using threads.

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