I have been trying to add a cancel button to a progressbar en pygtk but now way to get the funcion. What its does that function is execute and script and every line in the output count it and is added to the progressBar. My problem is how to cancel that subprocess, is there a way?
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)
The following example should do everything you are asking for (hopefully), it prints the output of a long running command in two textviews, in the top one the stdout and in the bottom one the stderr.
You can kill the process with the stop button and the progress will stop pulsing.
In the meantime the UI should not freeze without using threads, it should work on windows too (not too sure but I'm quite confident it will) but you will have to change 'du' in something else of course.
It uses GTK3, so pygobject with introspection (and you should use it too...) but if you really want to use PyGTK it should be pretty straightforward to port.
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()