【问题标题】:gobject and subprocess.Popen to communicate in a GTK GUIgobject 和 subprocess.Popen 在 GTK GUI 中进行通信
【发布时间】:2012-05-17 20:24:47
【问题描述】:

我正在尝试使用 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 系统监视器。

在使用“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 输出并给出不完整的输出。

感谢您的帮助

【问题讨论】:

    标签: python pygtk multiprocessing popen gobject


    【解决方案1】:

    这是给那些在 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()
    

    【讨论】:

      【解决方案2】:

      os.read 使用无缓冲读取,它需要一个实际的文件描述符。你的 fd 不是一个真正的文件描述符,它是一个文件对象;通常称为 f。

      如果您想确定进程已死,请使用 os.kill。

      【讨论】:

      • 您能否详细说明解决方案应该是什么?我实际上怀疑命令 self.proc.kill() 实际上并没有杀死进程,因为我使用了 shell=True。可能吗?
      • 例如,如果 cmd='ls -R /' ,此示例似乎并不能正常工作。为了使它与它一起工作,您可能需要 shell=False 在这种情况下停止按钮将不起作用。底线,不是 pygtk 进程 gui 通信的一个很好的例子。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-22
      • 2017-12-12
      • 1970-01-01
      • 2017-10-28
      • 1970-01-01
      相关资源
      最近更新 更多