python - 处理外部流程

标签 python user-interface pyqt subprocess external-process

我一直在开发需要管理外部流程的gui应用程序。使用外部流程会导致很多问题,这些问题可能会使程序员的生活变得困难。我觉得对该应用程序的维护花费了无法接受的长时间。我一直在尝试列出使使用外部流程变得困难的事情,以便我提出减轻痛苦的方法。我以为我想把这种话变成我在这里发表的言论,以期获得一些反馈并为任何考虑驶入这些非常浑浊的水域的人提供一些指导。这是到目前为止我得到的:


子级的输出可能与父级的输出混合在一起。这可能会使输出产生误导且难以阅读。很难说是从哪里来的。当事情是异步的时,要弄清楚正在发生什么变得越来越困难。这是一个人为的例子:

import textwrap, os, time
from subprocess import Popen
test_path = 'test_file.py'

with open(test_path, 'w') as file:
    file.write(textwrap.dedent('''
        import time
        for i in range(3):
            print 'Hello %i' % i
            time.sleep(1)'''))

proc = Popen('python -B "%s"' % test_path)

for i in range(3):
    print 'Hello %i' % i
    time.sleep(1)

os.remove(test_path)


输出:

Hello 0
Hello 0
Hello 1
Hello 1
Hello 2
Hello 2


我想我可以让子进程将其输出写入文件。但是每次我想查看打印语句的结果时不得不打开一个文件可能会很烦人。

如果我有用于子进程的代码,则可以添加一个标签,例如print 'child: Hello %i',但是每次打印都会令人讨厌。而且它会给输出增加一些噪声。当然,如果我无法访问代码,我将无法做到。

我可以手动管理流程输出。但是,随后您打开了一大堆蠕虫,其中包含线程,轮询和类似内容。

一个简单的解决方案是将进程像同步函数一样对待,也就是说,直到进程完成,才可以执行其他代码。换句话说,使过程块。但是,如果您正在构建GUI应用程序,那将不起作用。这把我带到下一个问题...
阻止进程会导致GUI无法响应。

import textwrap, sys, os
from subprocess import Popen

from PyQt4.QtGui import *
from PyQt4.QtCore import *

test_path = 'test_file.py'
with open(test_path, 'w') as file:
    file.write(textwrap.dedent('''
        import time
        for i in range(3):
            print 'Hello %i' % i
            time.sleep(1)'''))

app = QApplication(sys.argv)
button = QPushButton('Launch process')
def launch_proc():
    # Can't move the window until process completes
    proc = Popen('python -B "%s"' % test_path)
    proc.communicate()
button.connect(button, SIGNAL('clicked()'), launch_proc)
button.show()
app.exec_() 
os.remove(test_path)


Qt提供了自己的名为QProcess的进程包装,可以对此提供帮助。您可以将功能连接到信号以相对容易地捕获输出。这就是我目前正在使用的。但我发现所有这些信号的行为都像goto语句一样可疑,并可能导致意大利面条式代码。我想我想通过从QProcess调用“完成”信号来获得某种阻塞行为,该函数包含流程调用之后的所有代码。我认为应该可以,但是我仍然对细节有些模糊...
从子进程返回到父进程时,堆栈跟踪会中断。如果正常功能搞砸了,您将获得一个完整的完整堆栈跟踪,包括文件名和行号。如果子流程搞砸了,那么如果您获得任何输出,您将很幸运。每当出现问题时,您最终都必须做更多的侦探工作。
说到这一点,在处理外部流程时,输出会消失。就像您通过Windows的“ cmd”命令运行某些命令一样,控制台将弹出,执行代码,然后消失,然后您才有机会看到输出。您必须传递/ k标志,以使其保持不变。类似的问题似乎一直在出现。

我想问题3和问题4具有相同的根本原因:没有异常处理。异常处理旨在与函数一起使用,不适用于进程。也许有某种方法可以为进程获取诸如异常处理之类的东西?我想这就是stderr的目的吗?但是处理两个不同的流本身可能会很烦人。也许我应该进一步研究...
进程可能会在后台挂起并停留在后台,而您没有意识到这一点。因此,您最终对计算机大吼大叫,因为它运行得如此缓慢,直到您最终调出任务管理器并看到30个相同进程的实例在后台挂出为止。

同样,挂起后台进程可以各种有趣的方式与进程的其他实例进行交互,例如通过持有文件的句柄或类似的东西而导致权限错误。

似乎一个简单的解决方案是,如果子进程没有关闭自身,则让父进程在退出时杀死子进程。但是,如果父进程崩溃,则可能不会调用清理代码,并且子进程可能会挂起。

另外,如果父母等待孩子完成工作,并且孩子陷入无限循环之类的情况,则可能会导致两个挂起进程。

此问题可能使问题2变得多余,使您的gui完全停止响应,并迫使您杀死任务管理器中的所有内容。
F *** ing报价

通常需要将参数传递给流程。这本身就是一个头痛。特别是在处理文件路径时。说...“ C:/我的文档/任何/”。如果没有引号,则该字符串通常会在空格处分割并解释为两个参数。如果需要嵌套的引号,则可以使用'和'。但是,如果需要使用两层以上的引号,则必须进行一些讨厌的转义,例如:“ cmd / k'python \'path 1 \'\'路径2 \''“。

一个很好的解决方案是将参数作为列表而不是单个字符串传递。子流程允许您执行此操作。
无法轻松地从子流程返回数据。

您当然可以使用标准输出。但是,如果您想在那里打印打印以进行调试,该怎么办?如果期望以某种方式格式化输出,那将会使父级变糟。在函数中,您可以打印一个字符串,然后返回另一个字符串,一切正常。
模糊的命令行标志和基于糟糕的终端的帮助系统。

这些是我在使用OS级应用程序时经常遇到的问题。就像我提到的/ k标志一样,打开cmd窗口是谁的呢?在这方面,Unix应用程序并不总是友好得多。希望您可以使用google或StackOverflow来找到所需的答案。但是如果没有的话,您将有很多无聊的阅读和令人沮丧的反复试验。
外在因素。

这有点模糊。但是,当您离开相对避风港的自己的脚本港口来处理外部流程时,您发现自己不得不在更大程度上处理“外部世界”。那是一个可怕的地方。各种各样的事情都可能出错。仅举一个随机的例子:在其中运行进程的cwd可以修改其行为。


可能还有其他问题,但这是我到目前为止已写下的问题。您还想添加其他标签吗?对解决这些问题有什么建议吗?

最佳答案

签出子流程模块。它应该有助于输出分离。我看不到围绕单独的输出流或单个流中的某种输出标记的任何方法。

悬挂过程问题也很困难。我唯一能做的解决方案是在外部进程上放置一个计时器,如果在指定的时间内未返回,则将其杀死。粗暴,令人讨厌,如果其他人有一个好的解决方案,我很想听听它,以便我也可以使用它。

要解决完全不受管理的关机问题,您可以做的一件事是保留pid文件目录。每当您启动外部进程时,请使用该进程的pid的名称将文件写入pid文件目录。当您知道进程已干净退出时,请擦除该pid文件。您可以使用pid目录中的内容来帮助清除崩溃或重新启动。

这可能不会提供任何令人满意的或有用的答案,但这也许是一个开始。

关于python - 处理外部流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2892756/

相关文章:

python - 将 python 3.4 嵌入到 C++ Qt 应用程序中?

Qt 绘图应用程序

python - 如何重新开始游戏?

python - 深度优先搜索在 python 错误 : Key Error 7

java - 我在java代码中发现了变量声明 "UI variableName;"。那是什么意思?

java - 具有出色 UI 的桌面应用程序 - 平台决策

qt - PyQt:keyPressEvent 和 keyReleaseEvent - 按住不反弹

Python 更新字典中键名的所有值

Python( Pandas )错误 'the label [Algeria] is not in the [index]'

java - 向 JButton 添加操作事件