Python/Tkinter:循环迭代未完成

0 投票
3 回答
712 浏览
提问于 2025-04-17 07:13

在下面的例子中,我想把temp2脚本的所有输出(这个脚本是通过子进程运行的)实时打印到一个文本框里。

我遇到的问题是,在temp2中,当i <= 468时,脚本运行得很好,看起来是实时的。

但是,如果我把i = 469或更高,执行就会在很多次迭代后停下来,没法完成。

举个例子,当i = 469时,日志文件里有i = 469i = 3的记录。之后整个过程就停止了。

请注意:i = 469可能在你的机器上不一样。如果i = 469在你那儿运行正常,可以试试更高的值。

Temp1.py是主脚本。

#temp1.py
from Tkinter import *
import Tkinter as tk
import os
import ttk 
os.system('echo OS ready')
os.system('chmod 755 *')
import subprocess
import sys

#Creating new Window to display output 
t = Tk()
t.title('output Run Display')
t.geometry('800x1000-5+40')
t.state('normal')
little = Label(t, text="NRUNTEST OUTPUT LOG").grid(column = 0, row = 0)
log = Text(t, state='disabled', width=115, height=150, wrap='none')
log.grid(row = 1, column = 0)

test=subprocess.Popen('temp2',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)


#stdout
while True:
    line_out = test.stdout.readline()
    line_er  = test.stderr.readline()
    if line_out == "" and line_er == "":
        break
    else:
        log['state'] = 'normal'
        log.insert('end', line_out)
        log.insert('end', line_er)
        log['state'] = 'disabled'
        print line_out
        print line_er
        t.update()
t.mainloop()

下面是我通过子进程运行的脚本。

#temp2 #csh script

set i = 469
while ($i > 0)
  echo i is $i | tee -a log
  set i = `expr "$i" - 1`
end 

3 个回答

0

正如jsbueno所说,你的问题是因为调用了一个会阻塞的函数readline。你可以考虑使用文件事件源,这样在数据可用时会收到通知(可以通过createfilehandler这个tkinter的方法来实现)。想了解更多细节,可以查看这个之前的回答

1

你的问题在于调用 test.stdout.readline 时会阻塞,也就是说程序会在这里停下来,直到有新的数据行可用时才会继续执行。

当然,test.stderr.readline 也是一样的情况。

我觉得处理你想要的效果最简单的方法是让你的子进程把数据写入文件中,然后在主进程中打开这个文件,并尝试在一个定期调用的 tkinter 回调函数里读取它,使用 .after 方法。

(通过文件,你可以使用 seektell 方法来检查是否已经到达文件末尾)

注意你提供的示例代码中,Tkinter 的 mainloop 只有在子进程已经结束后才会被调用。

更好的解决方案是完全在 Python 中读取你想要的日志,而不依赖于 shell 脚本。

Tkinter 的 after 方法类似于 JavaScript 的 setTimeout - 它是一个在某个控件(比如你的 "t" 对象)上的方法,你需要传入等待的毫秒数和要调用的函数名 -

就像这样

def verify_output():
    # read subprocess output file and update the window if needed
    ...
    # call self after 1 second
    t.after(1000, verify_output)

# callreader callback for the first time:

t.after(10, verify_output)
Tkinter.mainloop()
0

在编程中,有时候我们需要处理一些数据,这些数据可能来自不同的地方,比如用户输入、文件或者网络请求。为了让程序能够理解这些数据,我们通常需要将它们转换成一种特定的格式。这就像把不同种类的水果放进一个篮子里,确保每种水果都有自己的位置。

在这个过程中,我们可能会用到一些工具或者库,这些工具可以帮助我们更方便地处理和转换数据。就像在厨房里,我们会用刀、锅等工具来帮助我们做饭一样。

此外,处理数据时,我们还需要注意数据的安全性和有效性。就像在超市购物时,我们会检查食物的保质期和包装一样,确保我们处理的数据是可靠的。

总之,数据处理是编程中非常重要的一部分,掌握好这项技能可以让我们的程序更加智能和高效。

import os
import ConfigParser
from Tkinter import *
import Tkinter as tk
import os
import ttk 
os.system('echo OS ready')
os.system('chmod 755 *')
import subprocess
from subprocess import call
import sys

os.system('rm stdout')

#Creating new Window to display output 
t = Tk()
t.title('output Run Display')
t.geometry('100x100')
t.state('normal')
little = Label(t, text="NRUNTEST OUTPUT LOG").grid(column = 0, row = 0)
log = Text(t, state='disabled', width=50, height=50, wrap='none')
log.grid(row = 1, column = 0,sticky=(N,W,E,S))
s = ttk.Scrollbar(t,orient=VERTICAL,command=log.yview)
s.grid(column=1,row=1,sticky=(N,S))
log.configure(yscrollcommand=s.set)
ttk.Sizegrip().grid(column=1, row=1, sticky=(S,E))

with open("stdout","wb") as out:
    with open("stderr","wb") as err:
        test=subprocess.Popen('tem',shell=True,stdout=out,stderr=err)
fout = open('stdout','r')
ferr = open('stderr','r') 
def verify():

    data_out = fout.readlines()
    data_out = ''.join(data_out)

    log['state'] = 'normal'
    log.insert('end',data_out)
    log['state'] = 'disabled'
    #print data_out

    t.after(1000,verify)
    fout.close()

verify()

t.mainloop()

撰写回答