将subprocess.call的输出输出到进度条

时间:2009-08-16 12:28:41

标签: python multithreading progress-bar pygtk subprocess

我正在使用growisofs通过我的Python应用程序刻录iso。我在两个不同的文件中有两个类; GUI()(main.py)和Boxblaze()(core.py)。 GUI()构建窗口并处理所有事件和东西,Boxblaze()具有GUI()调用的所有方法。

现在当用户选择要刻录的设备和要刻录的文件时,我需要调用一个调用以下命令的方法:`

growisofs -use-the-force-luke=dao -use-the-force-luke=break:1913760 -dvd-compat -speed=2 -Z /burner/device=/full/path/to.iso

此命令应提供类似于此的输出:

Executing 'builtin_dd if=/home/nevon/games/Xbox 360 isos/The Godfather 2/alls-tgod2.iso of=/dev/scd0 obs=32k seek=0'
/dev/scd0: "Current Write Speed" is 2.5x1352KBps.
#more of the lines below, indicating progress.
7798128640/7835492352 (99.5%) @3.8x, remaining 0:06 RBU 100.0% UBU  99.8%
7815495680/7835492352 (99.7%) @3.8x, remaining 0:03 RBU  59.7% UBU  99.8%
7832862720/7835492352 (100.0%) @3.8x, remaining 0:00 RBU   7.9% UBU  99.8%
builtin_dd: 3825936*2KB out @ average 3.9x1352KBps
/dev/burner: flushing cache
/dev/burner: closing track
/dev/burner: closing disc

此命令在Boxblaze()中名为burn()的方法中运行。看起来很简单:

def burn(self, file, device):
    subprocess.call(["growisofs", '-dry-run', "-use-the-force-luke=dao", "-use-the-force-luke=break:1913760", "-dvd-compat", "-speed=2", "-Z",  device +'='+ file])

现在我的问题如下:

  1. 如何从输出中获得进度(括号中的百分比)并将进度条设置为“跟随”该进度?我的进度条在GUI()类中调用,如下:

    get = builder.get_object

    self.progress_window = get(“progressWindow”)

    self.progressbar = get(“progressbar”)

  2. 我是否必须在单独的线程中运行此命令才能使GUI保持响应(以便我可以更新进度条并允许用户取消刻录)?如果是这样,我怎么能这样做,仍然可以将进度传递到进度条?


  3. 如果您有兴趣,可以使用完整代码on Launchpad。如果你安装了bazaar,只需运行:

    bzr branch lp:boxblaze
    

    哦,如果你想知道,这个应用程序只适用于Linux - 所以不要担心跨平台兼容性。

4 个答案:

答案 0 :(得分:3)

您可以使用glib.io_add_watch()来监视连接到子进程对象中stdout和stderr的管道的输出。

proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_id = glib.io_add_watch(proc.stdout, glib.IO_IN|glib.IO_HUP, stdout_cb)
stderr_id = glib.io_add_watch(proc.stderr, glib.IO_IN|glib.IO_HUP, stderr_cb)

然后,当调用回调时,它应该检查条件并从管道读取所有数据并处理它以获取更新ProgressBar的信息。如果应用程序缓冲io,那么你可能不得不使用pty来欺骗它认为它连接到终端,所以它会一次输出一行。

答案 1 :(得分:1)

要获得输出,您需要使用subprocess.Popen来电。 (stdout = subprocess.PIPE

(第二个问题)

除非GUI框架可以在正常循环中选择文件描述符,否则您可能需要一个单独的线程。

您可以让后台线程读取管道,处理它(以提取进度),将其传递给GUI线程。

## You might have to redirect stderr instead/as well
proc = sucprocess.Popen(command,stdout=subprocess.PIPE)
for line in proc.stdout.readlines():
    ## might not work - not sure about reading lines
    ## Parse the line to extract progress
    ## Pass progress to GUI thread

编辑:

我担心我不想浪费大量的CD来测试它,所以我没有运行它,但是你评论它看起来好像它没有输出信息到stdout,而是stderr。

我建议直接在命令行上运行示例命令,并将stdout和stderr重定向到不同的文件。

growisofs [options] >stdout 2>stderr

然后你可以找出stdout上出现的东西以及stderr上的内容。

如果您想要的东西是stderr,请将stdout=subprocess.PIPE更改为stderr=subprocess.PIPE并查看其是否有效。

EDIT2:

你没有正确使用线程 - 你应该启动它 - 不直接运行它。

此外:

gtk.gdk.threads_init()
threading.Thread.__init__(self)

非常奇怪 - 初始化调用应该在初始化程序中 - 我认为你不需要将它作为一个gtk线程吗?

调用run()方法的方式本身很奇怪:

core.Burning.run(self.burning, self.filechooser.get_filename(), self.listofdevices[self.combobox.get_active()])

通过对象调用实例方法:

self.burning.run(self.filechooser.get_filename(), self.listofdevices[self.combobox.get_active()])

(但你应该有一个__init__()方法)

在我看来,在你走路之前,你正试图奔跑。尝试编写一些简单的线程代码,然后编写一些简单的代码来运行growisofs并解析输出,然后是一些简单的gtk +背景线程代码,然后尝试将它们组合在一起。 实际上,首先要开始编写一些简单的面向对象的代码,以便首先理解方法和对象。

e.g。您在python中创建的所有类都应该是新式类,您应该从初始化程序中调用超类初始化程序等。

答案 2 :(得分:0)

你可以通过超时来读取子进程吗?我想你可以因为my subProcess module被用作它的设计输入。你可以使用它来growiso.read(.1),然后从outdata(或者errdata)解析并显示百分比。

答案 3 :(得分:0)

您需要从单独的线程运行命令,并使用gobject.idle_add调用更新gui。现在你有一个类“燃烧”,但你使用它错了,它应该像这样使用:

self.burning = core.Burning(self.filechooser.get_filename(), self.listofdevices[self.combobox.get_active()], self.progressbar)
self.burning.start()

显然你必须修改core.Burning。 然后您将可以访问进度条,以便您可以创建这样的函数:

def set_progress_bar_fraction(self, fraction): 
    self.progress_bar.set_fraction(fraction)

然后,每个百分比更新都会调用它:gobject.idle_add(self.set_progress_bar_fraction, fraction)

有关线程here的pygtk的更多信息。