2012-05-09 2 views
1

포펫 프로세스와 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

을하지만 난 G 객체가는 popen 프로세스가 종료 되더라도 일단 CPU 사이클을 많이 사용하는 것으로 나타났습니다. 위의 스크립트를 실행하고 우분투 시스템 모니터를 시청하십시오.

"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() 

질문 I이 있습니다

  • PTY 좋은 생각인가? Windows에서도 사용할 수 있습니까?

  • pty를 사용하지 않고 stdout을 사용하는 것이 가능하고 CPU 사용량이 높은 문제가 있습니까?

  • 처음으로이 스크립트를 실행하면 txt 출력을 버퍼링하고 불완전한 출력을 나타내는 것처럼 보입니다.

os.read

답변

0

사용 버퍼링 읽기, 그것은 실제 파일 기술자 소요 도움을 주셔서 감사합니다. fd는 실제 파일 기술자가 아니며 파일 객체입니다. 보통 f라고 불리는

프로세스가 종료되었는지 확인하려면 os.kill을 사용하십시오.

+0

해결 방법에 대해 좀 더 자세히 설명해 주시겠습니까? 실제로 쉘 = True를 사용하기 때문에 self.proc.kill() 명령이 실제로 프로세스를 죽이지 않는다고 생각됩니다. 가능한? – Fabrizio

+0

무엇이든 가능합니다. –

+0

예를 들어 cmd = 'ls -R /'인 경우이 예제는 실제로 작동하지 않습니다. 그리고 그것과 함께 작동 시키려면 shell = False가 필요할 것입니다. 어떤 경우에는 stop 버튼이 작동하지 않습니다. 결론은 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() 
관련 문제