2012-05-09 11 views
1

私は、オブジェクトを使用してPopenプロセスとGTK GUI間の通信を許可しようとしています。この触発されgobjectとsubprocess.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

を私はGObjectのは、popenのプロセスが終了しても、いったん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出力をバッファして不完全な出力を出すようです。

os.readのヘルプ

答えて

0

使用バッファなしお読みいただきありがとうございました、それは実際のファイルディスクリプタをとります。 fdは実際のファイル記述子ではなく、ファイルオブジェクトです。通常fと呼ばれる。

プロセスが停止していることを確認するには、os.killを使用します。

+0

もう少し詳しく解答してください。私は実際にself.proc.kill()というコマンドがshell = Trueを使うので実際にはプロセスをkillしないと思われます。可能? – Fabrizio

+0

何かが可能です。 –

+0

この例は、cmd = 'ls -R /'のように実際には動作しないようです。そして、それを使って動作させるには、shell = Falseが必要な場合があります。その場合、停止ボタンは機能しません。要するに、pygtkプロセスのGUIのような素晴らしい例ではない。 – Fabrizio

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() 
関連する問題