Tkinter,Popen和线程 [英] Tkinter, Popen, and threads

查看:92
本文介绍了Tkinter,Popen和线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a need from a Tkinter GUI to be able to start a long-running Linux script but at the same time I want to be able to have a stop-button enable so I can stop the process. Neither Tkinter nor popen are threadsafe. I thought of simply either placing the popen function in a thread or possibly just enabling a button in a thread. I am currently using Red Hat Linux 5.9 which uses Python 2.4.3, but I have later versions available online that I could use. In the program, below note that I reconfigure the start button to a stop button, but that does not work because the start button function is active, but it indicates my intent. Note that the stop function simply does an os.kill() on the child.

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

推荐答案

您可以使用Popen通过无阻塞管道进行通信来启动您的进程通过该过程-这样,您可以异步接收其输出.我已经使用Popen的增强版,代码来自ActiveState Python食谱.我再也无法在网上找到该食谱,但是由于我仍然拥有将其粘贴到此处的代码:

You can start your process using Popen, using a non-blocking pipe to communicate with the process - this way, you can receive its output asynchronously. I already used an enhanced version of Popen, code was from an ActiveState Python cookbook recipe. I could not find the recipe on the web anymore, but as I still have the code I pasted it here:

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

然后,在您的Tkinter代码中,您可以使用计时器定期检查进程的状态(是否终止)并获取输出:

Then, in your Tkinter code, you can use a timer to check periodically for the state of the process (terminated, or not) and to get output:

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)

这篇关于Tkinter,Popen和线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆