【问题标题】:Real Time updating executable subprocess console output in Python在 Python 中实时更新可执行子进程控制台输出
【发布时间】:2019-08-26 18:38:58
【问题描述】:

我一直在阅读this questionand this one 并注意到那里提出的解决方案并没有达到我想要实现的目的。这个想法类似于this question,但我在这里给出了一个更好的例子。

我需要在 Python 3.7 中构建一个 GUI 来监视和控制用 C 编写的遗留软件的执行。现在,我正试图简单地从 Python 调用一个测试可执行文件并将其打印在 GUI 上,但是现在,在与 python 相同的控制台上打印就足够了。问题是可执行文件需要很长时间才能完全执行,但同时会打印控制台消息,我需要我的 GUI 及时读取这些消息。

这是一个工作示例:

在 Python 3.7 中:

import sys
from threading import Thread
import time
import subprocess

class HandleTarget(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        subprocess.Popen(["hello.exe"], stdout=sys.stdout, bufsize=1)

class Printer(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        for i in range(1, 15):
            sys.stdout.write("Hi back ("+str(i)+")!\n")
            time.sleep(1.0)

thread_1 = HandleTarget()
thread_2 = Printer()

# Launching threads:
thread_1.start()
thread_2.start()

现在,示例可执行文件(“hello.exe”)可以像这样在 C 中构建:

#include <stdio.h>
#include <time.h>

int main() {
    struct timespec time;
    double tic = 0;
    double toc = 0;

    for( int ii = 0; ii < 3 ; ii++){
        clock_gettime(CLOCK_MONOTONIC, &time);
        tic = time.tv_sec;
        toc = tic;

        while(toc-tic<3) {
        clock_gettime(CLOCK_MONOTONIC, &time);
        toc    = time.tv_sec;
        }
        printf("Hello #%d \n",ii);
    }
    return(0); 
}

理想情况下,我需要将消息“Hello”和“Hi back”交错。但是如果我运行上面的 python 脚本,我会得到:

Hi back (1)!
Hi back (2)!
Hi back (3)!
Hi back (4)!
Hi back (5)!
Hi back (6)!
Hi back (7)!
Hi back (8)!
Hi back (9)!
Hello #0 
Hello #1 
Hello #2 
Hi back (10)!
Hi back (11)!
Hi back (12)!
Hi back (13)!
Hi back (14)!

显然,可执行文件的输出仅在执行完成时打印。这意味着如果遗留可执行文件正在运行,只有在完成后才会显示任何内容。

编辑:我对此没有严格的实时限制,如果打印实际上需要几分钟,那很好,问题是如果一个进程需要运行几天,那么每隔几个小时(最好是分钟)GUI 需要使用可执行文件中的控制台打印来更新。

【问题讨论】:

  • 当有“\n”时打印可执行文件的输出。但是 Python 不会同时运行线程 - 它运行其中一个线程一段时间,然后运行第二个线程一段时间,然后它首先运行一段时间,等等。有时线程上运行的时间可能比另一个线程长,你可以得到一次来自一个线程的所有文本。我会使用stdout=PIPE 从子进程读取行(当行有“\n”时)并在 GUI 中显示它,然后等待子进程的下一行。
  • 你的意思是倒置,而不是"hi back",它会打印"hello",反之亦然?
  • @Xilpex :我实际上写了“intervened”,这意味着 hello 消息不应该全部在一起,而是应该在两个 之间有一个 hi back你好.
  • @Mefitico 我认为正确的术语是交错的。
  • @Massimo 根据Dictionary.com 干预“2. 发生或介于两件事之间。3 发生或发生在其他事件或时期之间”。但我同意“交错”会更清晰。

标签: python-3.x console-application executable


【解决方案1】:

考虑阅读Popen.stdout

https://docs.python.org/3/library/subprocess.html?highlight=subprocess#subprocess.Popen.stdout

这是更新程序的输出:

$ python test.py
Hi back (1)!
Hi back (2)!
Hi back (3)!
Hello #0
Hi back (4)!
Hi back (5)!
Hi back (6)!
Hello #1
Hi back (7)!
Hi back (8)!
Hi back (9)!
Hello #2
Hi back (10)!
Hi back (11)!
Hi back (12)!
Hi back (13)!
Hi back (14)!

更新程序:

import sys
from threading import Thread
import time
import subprocess

class HandleTarget(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        proc = subprocess.Popen(["hello.exe"], stdout=subprocess.PIPE)
        for line in proc.stdout:
            print(line.strip().decode('utf-8'))


class Printer(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        for i in range(1, 15):
            sys.stdout.write("Hi back ("+str(i)+")!\n")
            time.sleep(1.0)

thread_1 = HandleTarget()
thread_2 = Printer()

# Launching threads:
thread_1.start()
thread_2.start()

编辑:

我还修改了 C 程序以在打印后刷新 stdout,缓冲的 stdout 似乎是问题的根本原因:

    printf("Hello #%d \n",ii);
    fflush(stdout);

【讨论】:

  • 从好的方面来说,这也适用。但正如问题中所提到的,我几乎不能对遗留软件做任何事情,hello 函数是一个最小的工作示例。我可能会尝试重新定义 printf 以始终执行 fflush 并预加载自定义库或其他内容。尽管如此,如果没有更好的答案,您就有资格获得赏金。
  • 我检查了它是否也可以添加setvbuf(stdout, NULL, _IONBF, 0);(完全防止缓冲)。这可能对我的要求更容易,您可以考虑测试/添加到您的答案中。
  • @Mefitico 我在 Linux 中重新编译了 C 程序,它每秒打印“Hello #%d \n”。当程序结束时,在 Windows 上编译的相同源会立即刷新缓冲区。因此,我认为 Windows 终端中的缓冲区比 Python 更有问题。
猜你喜欢
  • 2014-03-13
  • 2018-05-28
  • 2018-03-10
  • 2017-02-07
  • 1970-01-01
  • 2019-05-02
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多