Tkinter,Popen和线程 [英] Tkinter, Popen, and threads
问题描述
我需要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屋!