2013-10-07 95 views
0

我需要從Tkinter GUI啓動一個長期運行的Linux腳本,但同時我希望能夠啓用停止按鈕,這樣我就可以停止處理。 Tkinter和popen都不是線程安全的。我想簡單地把popen函數放在一個線程中,或者可能只是在一個線程中啓用一個按鈕。我目前使用的是使用Python 2.4.3的Red Hat Linux 5.9,但是我有可以在線使用的更新版本。在程序中,請注意,我將開始按鈕重新配置爲停止按鈕,但這不起作用,因爲啓動按鈕功能處於活動狀態,但它表示我的意圖。請注意,停止功能只是對孩子做一個os.kill()。Tkinter,Popen和線程

#!/usr/bin/python 
import subprocess 
import sys 
import Tkinter 
import tkMessageBox 
import signal 
import os 
import time 

class popentest: 

    def __init__(self): 
     self.mainWindow = Tkinter.Tk() 

    def __createUI(self, mainWindow): 
     mainWindow.protocol("WM_DELETE_WINDOW", self.OnExitWindow) 
     ## Local variables. 
     sleep=5 
     iter=5 
     self.pid=0 
     self.mainWindow.title("Test popen") 
     self.start=Tkinter.Button(mainWindow, text=u"Start", command=self.onStart) 
     self.start.grid() 
     self.kwit = Tkinter.Button(mainWindow,text=u"Quit !", 
           command=self.onQuit) 
     self.kwit.grid() 
     self.lIter=Tkinter.Label(mainWindow, text="Iterations: ") 
     self.iterE=Tkinter.Entry(mainWindow, width=2) 
     self.lSleep = Tkinter.Label(mainWindow, text="Sleep time") 
     self.sleepEntry = Tkinter.Entry(mainWindow, width=3) 
     self.lIter.grid() 
     self.iterE.grid() 
     self.lSleep.grid() 
     self.sleepEntry.grid() 
     self.iterE.insert(0, str(iter)) 
     self.sleepEntry.insert(0,str(sleep)) 

    def startPopen(self): 
     self.__createUI(self.mainWindow) 
     self.mainWindow.mainloop() 

    def execute(self, numIters, sleep): 
     self.p = subprocess.Popen(['./testpopen.sh',str(numIters), str(sleep)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 
     self.pid=self.p.pid 
     print str(self.p.pid)+" started" 
     for line in iter(self.p.stdout.readline, ''): 
      print line 
     self.p.stdout.close() 
     self.pid=0 
     self.start.configure(text=u"Start", command=self.onStart) 

    def onStart(self): 
     numIters=self.iterE.get() 
     sleep=self.sleepEntry.get() 
     if not numIters.isdigit(): 
      tkMessageBox.showerror(
      "invalid entry", 
      "Iteration (%s)must be numeric\n" % numIters) 
      return 
     elif not sleep.isdigit(): 
      tkMessageBox.showerror(
      "invalid entry", 
      "Sleep(%s) is not numeric\n" % sleep) 
      return 
     numIters=int(numIters) 
     sleep=int(sleep) 
     if numIters <= 0 or sleep <=0 : 
      tkMessageBox.showerror(
      "invalid entry", 
      "Either iteration (%d) or sleep(%d) is <= 0\n" % (numIters, sleep)) 
     else: 
      print "configuring start to stop" 
      self.start.configure(text=u"Stop", command=self.onStop) 
      time.sleep(1) 
      self.execute(numIters, sleep) 

    def onStop(self): 
     print "configuring stop to start" 

     os.kill(p.pid, signal.SIGTERM) 
     self.start.configure(text=u"Start", command=self.onStart) 

    def OnExitWindow(self): 
     if self.pid != 0 : 
      os.kill(self.pid, signal.SIGKILL) 
     self.mainWindow.destroy() 

    def onQuit(self): 
     if self.pid != 0 : 
      os.kill(self.pid, signal.SIGKILL) 
     self.mainWindow.destroy() 

if __name__ == "__main__": 
    remote = popentest() 
    remote.startPopen()  
+0

避免阻塞GUI;你可以[在不同的線程中讀取子進程的輸出](https://gist.github.com/zed/42324397516310c86288) – jfs

回答

1

可以使用POPEN啓動過程中,採用非阻塞管道與進程通信 - 這樣一來,就可以異步接收其輸出。我已經 使用Popen的增強版本,代碼來自ActiveState Python Cookbook配方。 我無法找到在網絡上的配方了,但我仍然有我的代碼粘貼在這裏它:

https://gist.github.com/mguijarr/6874724

然後,在你Tkinter的代碼,你可以用一個定時器來定期檢查的 進程的狀態(終止或不是)並獲得輸出:

self.p = EnhancedPopen.Popen(['./testpopen.sh',str(numIters), str(sleep)], 
          stdin=subprocess.PIPE, 
          stdout=subprocess.PIPE, 
          stderr=subprocess.PIPE, 
          shell=True,universal_newlines=True) 
self.mainWindow.after(100, self.check_process) 

def check_process(self): 
    # get stdout output 
    output = EnhancedPopen.recv_some(self.p, e=0, stderr=0) 
    ... 
    if self.p.poll() is not None: 
    # process terminated 
    ... 
    return 
    # set timer again (until process exits) 
    self.mainWindow.after(100, self.check_process_output) 
+0

謝謝。我會看看我是否可以嘗試。我受到企業法規限制,不安裝未經批准的軟件包,但這應該沒問題。問題是我需要一個活動按鈕,以便用戶可以終止進程。這可能就足夠了。我也在下面發佈另一個解決方案。 – Jerry

+0

我將EnhancedPopen和上面的代碼合併到我的測試程序中,並且完美地工作。我應該能夠將這一點融入到2個生產計劃中,而不會有任何問題。再次感謝您發佈此信息。 – Jerry

+0

太棒了:)我爲你感到高興。 – mguijarr