![](/img/trans.png)
[英]Is it possible to communicate with a sub subprocess with subprocess.Popen?
[英]gobject and subprocess.Popen to communicate in a GTK GUI
我正在嘗試使用gobject允許Popen進程和GTK GUI之間的通信。
受此啟發: https : //pygabriel.wordpress.com/2009/07/27/redirecting-the-stdout-on-a-gtk-textview/#comment-156
我實現了類似的東西:
http://hartree.altervista.org/files/command-textview.py
但是我注意到,即使Popen進程終止,gobject也會占用大量CPU周期。 只需運行上面的腳本,然后觀看Ubuntu System Monitor。
在與“ pty”一起工作之后,我想到了:
import gtk,pygtk
import subprocess
import gobject
import pty, os, time
class CommandTextView(gtk.TextView):
def __init__(self):
super(CommandTextView,self).__init__()
self.master, self.slave = pty.openpty()
gobject.io_add_watch(os.fdopen(self.master), gobject.IO_IN, self.write_to_buffer)
self.proc = None
def run(self, w, cmd):
if self.proc == None or self.proc.poll() != None: # poll()=None means still running
self.proc = subprocess.Popen(cmd.split(), shell=True, stdout=self.slave, stderr=self.slave)
def stop(self,w):
if type(self.proc) is subprocess.Popen:
self.proc.kill()
while self.proc.poll() == None:
time.sleep(0.1)
self.proc = None
def write_to_buffer(self, fd, condition):
if condition == gobject.IO_IN:
char = fd.readline()
print 'adding:',char
buf = self.get_buffer()
buf.insert_at_cursor(char)
return True
else:
return False
def test():
win=gtk.Window()
vbox = gtk.VBox(False, 0)
win.set_size_request(300,300)
win.connect('delete-event',lambda w,e : gtk.main_quit())
ctv=CommandTextView()
bt1 = gtk.Button('Run')
bt2 = gtk.Button('Stop')
vbox.pack_start(ctv)
vbox.pack_end(bt2,False,False)
vbox.pack_end(bt1,False,False)
win.add(vbox)
bt1.connect("clicked", ctv.run, 'ls -la')
bt2.connect("clicked", ctv.stop)
win.show_all()
gtk.main()
if __name__=='__main__': test()
我的問題是:
pty是個好主意嗎? 它也可以用於Windows嗎?
是否有可能避免使用pty而僅使用stdout而不會出現CPU使用率高的問題?
如果您是第一次運行此腳本,它似乎會緩沖txt輸出並給出不完整的輸出。
感謝您的幫助
這是針對那些在2016年之后偶然發現此帖子並試圖將其重寫為Gtk3的人的。
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GObject
import os
import fcntl
import subprocess
def unblock_fd(stream):
fd = stream.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
class StreamTextBuffer(Gtk.TextBuffer):
'''TextBuffer read command output syncronously'''
def __init__(self):
Gtk.TextBuffer.__init__(self)
self.IO_WATCH_ID = tuple()
def bind_subprocess(self, proc):
unblock_fd(proc.stdout)
watch_id_stdout = GObject.io_add_watch(
channel = proc.stdout,
priority_ = GObject.IO_IN,
condition = self.buffer_update,
# func = lambda *a: print("func") # when the condition is satisfied
# user_data = # user data to pass to func
)
unblock_fd(proc.stderr)
watch_id_stderr = GObject.io_add_watch(
channel = proc.stderr,
priority_ = GObject.IO_IN,
condition = self.buffer_update,
# func = lambda *a: print("func") # when the condition is satisfied
# user_data = # user data to pass to func
)
self.IO_WATCH_ID = (watch_id_stdout, watch_id_stderr)
return self.IO_WATCH_ID
def buffer_update(self, stream, condition):
self.insert_at_cursor(stream.read())
return True # otherwise isn't recalled
def sample():
root = Gtk.Window()
root.set_default_size(400, 260)
root.connect("destroy", Gtk.main_quit)
root.connect( # quit when Esc is pressed
'key_release_event',
lambda w, e: Gtk.main_quit() if e.keyval == 65307 else None
)
layout = Gtk.Box(orientation=1)
scroll = Gtk.ScrolledWindow()
layout.pack_start(scroll, expand=1, fill=1, padding=0)
buff = StreamTextBuffer()
textview = Gtk.TextView.new_with_buffer(buff)
scroll.add(textview)
button_start = Gtk.Button("Execute Command")
layout.add(button_start)
def on_click(widget):
if len(buff.IO_WATCH_ID):
for id_ in buff.IO_WATCH_ID:
# remove subprocess io_watch if not removed will
# creates lots of cpu cycles, when process dies
GObject.source_remove(id_)
buff.IO_WATCH_ID = tuple()
on_click.proc.terminate() # send SIGTERM
widget.set_label("Execute Command")
return
on_click.proc = subprocess.Popen(
[ 'ping', '-c', '3', 'localhost' ],
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines=True,
)
buff.bind_subprocess(on_click.proc)
widget.set_label("STOP!")
button_start.connect("clicked", on_click)
root.add(layout)
root.show_all()
if __name__ == "__main__":
sample()
Gtk.main()
與os.read
一起使用無緩沖讀取,它需要一個實際的文件描述符。 您的fd不是真正的文件描述符,它是文件對象; 通常稱為f。
如果要確保進程已死,請使用os.kill。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.