【问题标题】:button Stop/Cancel progressBar from subprocess PYGTK来自子进程 PYGTK 的按钮停止/取消进度条
【发布时间】:2013-12-24 11:59:03
【问题描述】:

我一直试图在 pygtk 的进度条中添加一个取消按钮,但现在可以获取该功能。该函数的作用是执行和脚本,输出中的每一行都对其进行计数并添加到进度条中。我的问题是如何取消该子进程,有办法吗?

def stop(self, widget):
    print "cancelar"
    os.killpg(self.p.pid, signal.SIGTERM) #=> not working
    self.p.kill() #=> not working


count = 0
command = "script.sh"
self.p = subprocess.Popen(command, shell=True, bufsize=0, stdout=subprocess.PIPE, universal_newlines=True)


for line in iter(self.p.stdout.readline, ""):
    count = count + 1

    while gtk.events_pending():
        gtk.mainiteration()
        gobject.timeout_add(1000, self.update)
        porcentaje = float(count) / float(1400)
        print porcentaje
        self.progress.set_text(str(int(porcentaje * 100)) + "%")
        self.progress.set_fraction(porcentaje)

【问题讨论】:

    标签: python progress-bar pygtk


    【解决方案1】:

    下面的例子应该能完成你所要求的一切(希望如此),它在两个文本视图中打印一个长时间运行的命令的输出,顶部是标准输出,底部是标准错误。

    您可以使用停止按钮终止进程,进度将停止跳动。

    与此同时,UI 不应该在不使用线程的情况下冻结,它也应该在 Windows 上工作(不太确定,但我很有信心它会)但是你当然必须在其他东西中更改“du”。

    它使用 GTK3,所以 pygobject 具有自省功能(你也应该使用它......)但是如果你真的想使用 PyGTK,它应该很容易移植。

    from gi.repository import Gtk, GLib
    import os, signal
    
    class MySpawned(Gtk.Window):
        def __init__(self):
            Gtk.Window.__init__(self)
            self.set_default_size(600,600)
    
            vb = Gtk.VBox(False, 5)
    
            self.tw_out = Gtk.TextView()
    
            sw = Gtk.ScrolledWindow()
            vb.pack_start(sw, True, True, 0)
            sw.add(self.tw_out)
    
            self.tw_err = Gtk.TextView()
    
            sw = Gtk.ScrolledWindow()
            vb.pack_start(sw, True, True, 0)
            sw.add(self.tw_err)
    
            self.progress = Gtk.ProgressBar()
            vb.pack_start(self.progress, False, True, 0)
    
            bt = Gtk.Button('Run')
            bt.connect('clicked', self.process)
            vb.pack_start(bt, False, False, 0)
    
            bt = Gtk.Button('Stop')
            bt.connect('clicked', self.kill)
            vb.pack_start(bt, False, False, 0)
    
            self.add(vb)
            self.set_size_request(200, 300)
            self.connect('delete-event', Gtk.main_quit)
            self.show_all()
    
        def run(self):
            Gtk.main()
    
        def update_progress(self, data=None):
            self.progress.pulse()
            return True
    
        def kill(self, widget, data=None):
            os.kill(self.pid, signal.SIGTERM)
    
        def process(self, widget, data=None):
            params = ['du', '--si', '/']
    
            def scroll_to_end(textview):
                i = textview.props.buffer.get_end_iter()
                mark = textview.props.buffer.get_insert()
                textview.props.buffer.place_cursor(i)
                textview.scroll_to_mark(mark, 0.0, True, 0.0, 1.0)
    
            def write_to_textview(io, condition, tw):
                if condition is GLib.IO_HUP:
                    GLib.source_remove(self.source_id_out)
                    GLib.source_remove(self.source_id_err)
                    return False
    
                line = io.readline()
                tw.props.buffer.insert_at_cursor(line)
                scroll_to_end(tw)
    
                while Gtk.events_pending():
                    Gtk.main_iteration_do(False)
    
                return True
    
            self.pid, stdin, stdout, stderr = GLib.spawn_async(params,
                flags=GLib.SpawnFlags.SEARCH_PATH|GLib.SpawnFlags.DO_NOT_REAP_CHILD,                                       
                standard_output=True,
                standard_error=True)
    
            self.progress.set_text('Running du --si')
    
            io = GLib.IOChannel(stdout)
            err = GLib.IOChannel(stderr)
    
            self.source_id_out = io.add_watch(GLib.IO_IN|GLib.IO_HUP,
                                     write_to_textview,
                                     self.tw_out,
                                     priority=GLib.PRIORITY_HIGH)
    
            self.source_id_err = err.add_watch(GLib.IO_IN|GLib.IO_HUP,
                                     write_to_textview,
                                     self.tw_err,
                                     priority=GLib.PRIORITY_HIGH)
    
            timeout_id = GLib.timeout_add(100, self.update_progress)
    
            def closure_func(pid, status, data):
                GLib.spawn_close_pid(pid)
                GLib.source_remove(timeout_id)
                self.progress.set_fraction(0.0)
    
            GLib.child_watch_add(self.pid, closure_func, None)
    
    if __name__ == '__main__':
        s = MySpawned()
        s.run()
    

    【讨论】:

    • 感谢代码,但我不想要脉冲条。我想要做的是取消循环,并且函数“os.killpg(self.p.pid,signal.SIGTERM)”和“self.p.kill()”不起作用。你知道如何在循环中停止/取消子进程吗?提前致谢
    • 嗨@ruben,我认为它不起作用,我认为您在运行子进程时阻塞了UI(主循环),并且在子进程完成之前不会释放控件,但它是通过查看您发布的代码部分只是我的猜测。这就是为什么在示例中我使用 async 函数来生成进程的原因。如果我弄错了,请发布一个可以显示问题的代码。干杯
    • @ruben,这就是正在发生的事情,除非你在一个单独的线程中运行子进程,否则你将无法杀死进程本身,虽然搞乱线程可能真的很痛苦,我建议你正如我在示例中指出的那样更改代码,通过 spawn_async 函数运行该过程并使用 IOChannel 读取管道(stdout)。这样您就可以像我的示例中那样终止该进程。
    • buff 不要告诉我,我不知道如何调整你的代码,因为不是我的,我真的是个菜鸟,我会迷路的。你能帮我做一个简单的改编吗?如果你想,当然。否则,我可以放弃在取消按钮中运行脚本并直接将其杀死,但如果我可以在 python 上执行此操作会更好!提前致谢!
    • @ruben 确定我愿意提供帮助,但您将不得不减少代码并制作合理数量的代码示例,这实在是太多的代码(包括您本地机器)进行编辑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多