-
在 PyGTK 中,有时需要在运行脚本时添加取消按钮以停止或取消进度条。这种方法可以使脚本在运行过程中具有更强的交互性和可控性。但是,在某些情况下,取消按钮可能无法正常工作。
-
解决方案 为了解决这个问题,以下给出了一个改进后的代码示例:
import subprocess
import signal
import gtk
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):
self.p.kill()
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()
在这个改进后的代码中,我们使用 subprocess.Popen 来启动脚本,并使用 SIGTERM 信号来终止子进程。另外,在取消按钮的点击事件处理程序中,我们使用 subprocess.Popen.kill() 方法来终止子进程,从而使取消按钮更加可靠。同时, 此方法更适合基于Linux系统的运行环境.
以下是改进后的代码示例中的关键代码片段:
def kill(self, widget, data=None):
self.p.kill()
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)
上述代码片段中,kill() 方法用于终止子进程,GLib.spawn_async() 函数用于启动脚本并获取子进程的 PID。