【问题标题】:realtime output from program in python in GUIGUI中python程序的实时输出
【发布时间】:2018-12-05 21:08:17
【问题描述】:

我正在使用 GTK 为 python 中的命令行程序编写图形 shell。主程序有这样的输出:

Starting Tractor:

Dec 04 22:10:34.000 [notice] Bootstrapped 0%: Starting
Dec 04 22:10:34.000 [notice] Bootstrapped 80%: Connecting to the Tor network
Dec 04 22:10:35.000 [notice] Bootstrapped 85%: Finishing handshake with first hop
Dec 04 22:10:36.000 [notice] Bootstrapped 90%: Establishing a Tor circuit
Dec 04 22:10:37.000 [notice] Bootstrapped 100%: Done
Tractor is conneted.

我有一个启动按钮,它通过子进程启动程序。因为我希望主窗口在启动过程中运行,所以我使用了 thrading。这是我的代码:

def on_start_clicked(self, button):
    spinner = Gtk.Spinner()
    self.props.icon_widget = spinner
    spinner.start()
    self.show_all()
    header_bar = self.get_parent()
    if self.is_running():
        def task_thread():
            task = Popen(command + "stop", stdout=PIPE, shell=True)
            task.wait()
            spinner.stop()
            header_bar.show_progress_button(False)
            self.update_label()
    else:
        def task_thread():
            header_bar.show_progress_button(True)
            task = Popen(command + "start", stdout=PIPE, shell=True)
            while True:
                output = task.stdout.readline().decode("utf-8")
                if output == '' and task.poll() is not None:
                    break
                if output and '%' in output:
                    print(output.split()[5][:-2])
            task.wait()
            spinner.stop()
            self.update_label()

    thread = Thread(target=task_thread)
    thread.daemon = True
    thread.start()

问题是日志输出不是实时的,而是等到整个过程完成后再打印整个输出!

我想要当时的实际百分比,以便将其传递到进度条,显示完成了多少任务。我怎样才能做到这一点?

编辑

感谢theGtknerd,我将代码更改为以下代码,但在该过程完成后,提要功能仍然有效,并且只打印整个输出的第一行。我认为这是触发 IO_IN 时的故障。

def thread_finished (self, stdout, condition):
    GLib.source_remove(self.io_id)
    stdout.close()
    self.spinner.stop()
    self.update_label()
    print("heeey")
    return False

def feed (self, stdout, condition):
    line = stdout.readline()
    line = line.decode("utf-8")
    print(line)
    return True

def on_start_clicked(self, button):
    self.spinner = Gtk.Spinner()
    self.props.icon_widget = self.spinner
    self.spinner.start()
    self.show_all()
    header_bar = self.get_parent()
    if self.is_running():
        header_bar.show_progress_button(False)
        task = Popen(command + "stop", stdout=PIPE, shell=True)
    else:
        header_bar.show_progress_button(True)
        task = Popen(command + "start", stdout=PIPE, shell=True)
    self.io_id = GLib.io_add_watch(task.stdout, GLib.IO_IN, self.feed)
    GLib.io_add_watch(task.stdout, GLib.IO_HUP, self.thread_finished)

【问题讨论】:

    标签: python subprocess gtk3 python-multithreading


    【解决方案1】:

    这里有一个小例子供你学习。所以在我的 Python 文件中,我有:

    #!/usr/bin/env python3
    
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk, GLib, GObject
    from subprocess import Popen, PIPE, STDOUT
    import os, sys
    
    
    class GUI:
        def __init__(self):
    
            window = Gtk.Window()
            self.label = Gtk.Label()
            window.add(self.label)
            window.show_all()
            window.connect("destroy", self.on_window_destroy)
    
            p = Popen(['./long_run.sh'], stdout = PIPE,stderr = STDOUT,stdin = PIPE)
            self.io_id = GObject.io_add_watch(p.stdout, GObject.IO_IN, self.feed)
            GObject.io_add_watch(p.stdout, GObject.IO_HUP, self.thread_finished)
    
        def feed (self, stdout, condition):
            line = stdout.readline()
            line = line.decode(encoding="utf-8", errors="strict")
            self.label.set_label(line)
            return True
    
        def thread_finished (self, stdout, condition):
            GObject.source_remove(self.io_id)
            stdout.close()
            self.label.set_label("Hurray, all done!")
    
        def on_window_destroy(self, window):
            Gtk.main_quit()
            print ("shutdown")
    
    def main():
        app = GUI()
        Gtk.main()
    
    if __name__ == "__main__":
        sys.exit(main())
    

    在文件 long_run.sh 我有:

    #!/bin/bash
    
    echo "Loading, please wait..."
    sleep 5
    echo "10%"
    sleep 5
    echo "20%"
    sleep 5
    echo "30%"
    sleep 5
    echo "40%"
    sleep 5
    echo "50%"
    sleep 5
    echo "60%"
    sleep 5
    echo "70%"
    sleep 5
    echo "80%"
    sleep 5
    echo "90%"
    sleep 5
    echo "100%"
    sleep 5
    

    【讨论】:

    • 好方法。我更改了this 之类的函数,并且对整体代码进行了一些改进,但是在该过程完成后,feed 函数仍然有效,并且只打印了整个输出的第一行。你对此有什么想法吗?我认为触发 IO_IN 时出现故障。
    • 你可以试试task = Popen([command, "start"], stdout=PIPE, stderr = STDOUT)
    • 我知道我已经看到了您描述自己的问题,但那是很久以前的事了,我忘记了我是如何解决的。
    • 我通过在print 命令中设置flush=True 在源应用程序中每次打印后刷新缓冲区来修复它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    相关资源
    最近更新 更多