0

我需要 Tkinter GUI 能够启动长时间运行的 Linux 脚本,但同时我希望能够启用停止按钮,以便我可以停止进程。Tkinter 和 popen 都不是线程安全的。我想简单地将 popen 函数放在一个线程中,或者可能只是在一个线程中启用一个按钮。我目前正在使用使用 Python 2.4.3 的 Red Hat Linux 5.9,但我可以使用更高版本的在线版本。在程序中,请注意我将开始按钮重新配置为停止按钮,但这不起作用,因为开始按钮功能处于活动状态,但它表明了我的意图。请注意,停止功能只是对孩子执行 os.kill() 。

#!/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()    
4

1 回答 1

1

您可以使用 Popen 启动您的流程,使用非阻塞管道与流程通信 - 这样,您可以异步接收其输出。我已经使用了 Popen 的增强版本,代码来自 ActiveState Python 食谱食谱。我在网上找不到食谱了,但因为我还有代码,所以我把它贴在这里:

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)
于 2013-10-07T21:02:35.347 回答