【问题标题】:Subprocess.check_call and throbber not workingSubprocess.check_call 和 throbber 不工作
【发布时间】:2015-08-15 02:24:41
【问题描述】:

所以我有这部分代码,它做了一件简单的事情:它启动一个脚本,当脚本正在处理时,一个 throbber 被设置。

   def go(self):
        if ui.chk.isChecked():
            self.startThrobber()
            script = subprocess.check_call(r'"C:\Program Files\FME\fme.exe"', shell=False)

            if script == 0: 
                self.stopThrobber() # opens a QMessageBox and stops throbber
        else:
            QMessageBox.information(self.popup(), "Errpr", "Error !")

在尝试了不同的方法(QThread、subprocess.Popen ...)之后,这是我让它工作的最接近的方法。

唯一不起作用的是 throbber 不会在 subprocess 执行之前立即启动,它会在 之后启动,因此它永远不会停止.

那么为什么在执行stopThrobber() 时颤动没有结束? 以及为什么startThrobber 没有在subprocess 之前执行(我很确定这是一个子进程的事情,但我对这一切都很陌生,直到昨天才听说过线程)

编辑: 单引号只是一个打字错误,对不起。还是没有解决问题。

【问题讨论】:

  • 表示原始字符串的单引号包含“shell”参数。将结束单引号移动到结束双引号和逗号之间
  • 这个方法是不是和你的previous method有完全相同的问题,因为subprocess.check_call()是一个阻塞调用?因此,在子进程运行时,由于没有将控制权返回给 Qt 事件循环,因此 throbber 无法正确设置动画?
  • @three_pineapples 是的,还是同样的问题,但由于我使用了不同的方法,我认为最好问一个新问题
  • @JS。是的,抱歉,这只是我的打字错误。在我的代码中它是正确的,但它不起作用

标签: python user-interface pyqt subprocess pyqt5


【解决方案1】:

从主线程对子进程的任何调用,即阻塞(等待返回)都会阻止您的 throbber 正常工作。我的answer 对您关于此主题的其他 SO 问题概述了一种不会导致子进程调用阻塞主线程的方法。我应该指出,解决方案不是创建对子进程的非阻塞调用的唯一方法(例如,请参阅here。您可以创建一个QTimer 来定期轮询子进程poll() 方法,以便您可以检查returncode 查看子进程是否已完成。)

关键主题是您需要在主线程中运行的方法快速返回,以保持 GUI 响应并允许您的 throbber 运行/动画。所以选择一种方式来启动满足这个要求的子流程。

【讨论】:

  • 我没有设法使用 QThread 让它工作,所以我转向子进程(认为它会更容易,唉..)我不认为我可以使用 QTimer,因为我没有'不知道子进程执行需要多长时间我已经尝试过 subprocess.Popen 和 subprocess.check_call :第一个启动 throbber 但不会停止它,而后者则相反....
  • @guy16 对不起,我写的东西有误。您可以配置一个QTimer 来调用子进程的poll() 方法,然后检查子进程返回码是否完成。如果没有,请再次排队QTimer。冲洗,重复。我已经相应地更新了我的答案。
  • 我有一个 while 循环来检查 poll() 是否为 None,如果不是,则发送信号以停止 throbber 但现在 startThrobber() 不起作用.. 我试图放入循环,在循环之前或将其留在主线程中,但它是相同的..
  • @guy16。 while 循环只会阻塞。正如已经建议的那样,使用QTimer 进行轮询。
  • 循环似乎没有阻止它,我放置了一个计时器,如果进程结束,则每秒检查一次。在循环之前,函数 startThrobber 由于某种原因没有执行
【解决方案2】:

表示原始字符串的单引号将“shell”参数括起来。

def go(self):
        if ui.chk.isChecked():
            self.startThrobber()
            script = subprocess.check_call(r"C:\Program Files\FME\fme.exe", shell=False)

            if script == 0: 
                self.stopThrobber() # opens a QMessageBox and stops throbber
        else:
            QMessageBox.information(self.popup(), "Errpr", "Error !")

【讨论】:

  • 您的回答应该说明您对原始代码所做的更改,以及它们解决问题的原因。另请注意,您通过不使用原始字符串作为路径引入了一个错误(即\f 是换页符)。
  • 糟糕,抱歉,这只是打字错误。在我的代码中,它的方式是正确的,但仍然存在问题
【解决方案3】:

所以我尝试了另一件事(不成功..) 当我点击一个按钮时,它会执行 startThrobber() 并向以下函数发送信号:

def go(self):

    self.startThrobber()

    script = subprocess.Popen(r'"C:\Program Files\FME\fme.exe" ', shell=False)

    while script.poll() == None:
        time.sleep(1)
    else:
        p.stopThrobber()

但仍然无法正常工作.. startThrobber 已执行,但 GUI 上没有显示任何内容...我认为 subprocess 的目的是允许同时执行多个任务,那么为什么 throbber 没有出现?

更新:如果我删除了 while 循环,startThrobber 工作:它出现在子进程转动时。那么为什么当有while循环时它不起作用呢?!

【讨论】:

  • 正如我之前所说,while 循环会阻塞 GUI。在函数退出并且控制返回到事件循环之前,不会处理任何排队的事件。您可以通过在 while 循环中调用 QtGui.qApp.processEvents() 来强制处理排队的事件。但首选的解决方案是使用QTimer,它正是为这种用例而设计的。
  • @ekhumoro 如果我做对了,而不是 while 循环,我使用 QTimer 和连接到 stopThrobber 的超时信号?
  • 计时器将被赋予一个时间间隔,例如 200 毫秒。然后它将每 200 毫秒发出 timeout 信号,允许连接到它的插槽轮询脚本(一次)以查看它是否已完成。如果是,它可以停止颤动(当然,也可以停止计时器)。代码的元素实际上与您已经拥有的非常相似,但使用 QTimer 应该可以防止 GUI 阻塞。
猜你喜欢
  • 1970-01-01
  • 2014-01-01
  • 2015-08-12
  • 2016-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
相关资源
最近更新 更多