将 subprocess.call 的输出通过管道传送到进度条

发布于 2024-08-01 23:08:44 字数 1869 浏览 1 评论 0原文

我正在通过我的Python应用程序使用growisofs刻录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 保持响应(以便我可以更新进度栏并允许用户根据需要取消刻录)? 如果是这样,我怎样才能做到这一点,并且仍然能够将进度传递到进度栏?


如果您有兴趣,可以在 Launchpad 上获取完整代码。 如果您安装了 bazaar,只需运行:

bzr branch lp:boxblaze

哦,如果您想知道,该应用程序仅适用于 Linux - 所以不必担心跨平台兼容性。

I'm using growisofs to burn an iso through my Python application. I have two classes in two different files; GUI() (main.py) and Boxblaze() (core.py). GUI() builds the window and handles all the events and stuff, and Boxblaze() has all the methods that GUI() calls.

Now when the user has selected the device to burn with, and the file to be burned, I need to call a method that calls the following command:`

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

This command should give an output similar to this:

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

This command is run in a method called burn() in Boxblaze(). It looks simply like this:

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])

Now my questions are the following:

  1. How can I get the progress from the output (the percentage in brackets) and have my progress bar be set to "follow" that progress? My progress bar is called in the GUI() class, as such:

    get = builder.get_object

    self.progress_window = get("progressWindow")

    self.progressbar = get("progressbar")

  2. Do I have to run this command in a separate thread in order for the GUI to remain responsive (so that I can update the progress bar and allow the user to cancel the burn if they want to)? If so, how can I do that and still be able to pass the progress to the progress bar?


The full code is available on Launchpad if you are interested. If you have bazaar installed, just run:

bzr branch lp:boxblaze

Oh, and in case you were wondering, this application is only meant to work in Linux - so don't worry about cross-platform compatibility.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清醇 2024-08-08 23:08:44

您可以使用 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 来欺骗它,让它认为它已连接到终端,以便它一次输出一行。

You can use glib.io_add_watch() to watch for output on the pipes connected to stdout and stderr in the subprocess object.

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)

Then when the callback is called it should check for the condition and reads all the data from the pipe and processes it to get the info to update the ProgressBar. If the app buffers io then you may have to use a pty to fool it into thinking it's connected to a terminal so it will output a line at a time.

与酒说心事 2024-08-08 23:08:44

要获取输出,您需要使用 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+后台线程代码,然后才尝试将它们全部组合在一起。
事实上,首先开始编写一些简单的面向对象代码,以便您首先了解方法和对象。

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

To get the output you need to use the subprocess.Popen call. (stdout = subprocess.PIPE)

(Second Question)

You probably need a separate thread, unless the GUI framework can select on a filedescriptor in the normal loop.

You can have a background thread read the pipe, process it (to extract the progress), the pass that to the GUI thread.

## 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

Edit:

I'm afraid I don't want to waste lots of CDs testing it out, so I haven't run it, but by you're comment it looks like it's not outputing the info to stdout, but to stderr.

I suggest running a sample command directly on the command-line, and redirecting stdout and stderr to different files.

growisofs [options] >stdout 2>stderr

Then you can work out which things come out on stdout and which on stderr.

If the stuff you want come on stderr, change stdout=subprocess.PIPE to stderr=subprocess.PIPE and see if that works any better.

Edit2:

You're not using threads correctly - you should be starting it - not running it directly.

Also:

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

is very weird - the initialiser calls should be in the initialiser - and I don't think you need to make it a gtk thread?

The way you call the run() method, is weird itself:

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

Call instance methods through the object:

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

(But you should have an __init__() method)

It seems to me that you are trying to run before you can walk. Try writing some simple threading code, then some simple code to run growisofs and parse the output, then some simple gtk+background threading code, and only then try combining them all together.
In fact first start writing some simple Object oriented code, so that you understand methods and object first.

e.g. All classes you create in python should be new-style classes, you should call super-class initialisers from your initialiser etc.

别再吹冷风 2024-08-08 23:08:44

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

Can you pass a timeout to reading from subprocess? I guess you can because my subProcess module was used as design input for it. You can use that to growiso.read(.1) and then parse and display the percentage from the outdata (or maybe errdata).

路弥 2024-08-08 23:08:44

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

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)

有关带有线程的pygtk的更多信息< a href="http://unpythonic.blogspot.com/2007/08/using-threads-in-pygtk.html" rel="nofollow noreferrer">此处。

You need to run the command from a separate thread, and update the gui with gobject.idle_add calls. Right now you have a class "Burning" but you are using it wrong, it should be used like this:

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

Obviously you will have to modify core.Burning.
Then you will have access to the progressbar so you could make a function like this:

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

Then every percentage update call it like this: gobject.idle_add(self.set_progress_bar_fraction, fraction)

More info on pygtk with threads here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文