2014-10-28 20 views
0

基本上,我问的是如何将不断更新的程序显示到tkinter的文本小部件中。Python - 关于tkinter和更新程序

from tkinter import Tk, Frame, Text, BOTH 

class FrameApp(Frame): 
    def __init__(self, parent): 
     Frame.__init__(self, parent, background="white") 

     self.parent = parent 
     self.parent.title("Ethis") 
     self.pack(fill=BOTH, expand=1) 
     self.centerWindow() 
    def centerWindow(self): 
     w = 900 
     h = 450 
     sw = self.parent.winfo_screenwidth() 
     sh = self.parent.winfo_screenheight() 

     x = (sw - w)/2 
     y = (sh - h)/2 

     self.parent.geometry("%dx%d+%d+%d" % (w, h, x, y)) 

    def theText(self): 
     w = Text() 
def main(): 
    root=Tk() 
    app = FrameApp(root) 
    root.mainloop() 


if __name__ == '__main__': 
    main() 

这是我的tkinter程序。正如你所看到的,我已经将它集中并设置了一个定义为文本(自我)的文本函数。我对文本(自我)做了任何事情,因为我不知道从哪里开始。这个效果很好,如预期的那样,它的中心是标题。

# Money Generator Mark 1 
import time 

t = 'true' 

while t == 'true': 
    s = 0 
    x = 1 
    print ("You have $%s." % (s)) 
    time.sleep(.75) 
    t = 'false' 

while t == 'false':   
    s = s + (1 * x) 
    print ("You have $%s." % (s)) 
    time.sleep(.75) 
    if s >= 100 and s < 200: 
     x = 2 
    if s >= 200: 
     x = 4 

在这里我有另一个程序,它可以正常工作。我把它叫做Money Generator,就像Cookie Clicker和Candy Box,这些类型的东西。这也适用于命令框,功能和打印到那里。我想知道如何整合这两个单独的程序,以便这里列出的第二个程序将显示在tkinter的窗口中。 这是我的新代码有一个新问题。我收到一个错误,指出'generate_money'没有在theText函数中定义。这些新功能在我的frameApp类中。

def theText(self): 
    self.w = Text() 
    self.t = threading.Thread(target=generate_money, args=(self.w)) 
    self.t.daemon = True 

def generate_money(textwidget): 
    p = subprocess.Popen([sys.executable, os.path.join('window.py', 'moneygenerator.py')], 
         stdout = subprocess.PIPE) 
    for line in p.stdout: 
     do_stuff_with(textwidget, line) 
    p.close() 
+0

看起来您已经缩进了'generate_money'函数,因此它是'FrameApp'类的一部分。不要这样做。这使'generate_money'成为你的'FrameApp'对象的一个​​方法,而不是一个顶层函数。 (你可以做这个工作,但是你必须为这个方法增加一个'self'参数,并且把它称为'self.generate_money'而不是'generate_money',这里没有很好的理由去做这件事。) – abarnert 2014-10-29 00:12:01

+0

另外,如果你希望在后台线程中抛出一个'NameError',除了'do_stuff_with'函数外,你还得编写一个'do_stuff_with'函数。 – abarnert 2014-10-29 00:12:26

+0

对不起,最后一个问题。我在课堂外移动了generate_money。不过,我仍然对do_stuff_with的内容感到困惑。我会以某种方式使用text.get()从线程获取线条,然后使用text.insert()将它们插入到文本框中。 – TastyOs 2014-10-29 01:11:35

回答

0

这是不幸的是将是一个有点麻烦,不是你想。

第一部分是简单:你可以只使用subprocess模块运行后台脚本,并捕获它的输出:

p = subprocess.Popen([sys.executable, os.path.join(scriptpath, 'moneygenerator.py')], 
        stdout = subprocess.PIPE) 
for line in p.stdout: 
    do_stuff_with(line) 
p.close() 

的问题是,在一个Tkinter的回调中这样做将阻止整个程序,直到后台程序完成。所以,而不是每行更新,你只需冻结应用程序,直到操作系统杀死你/显示一个沙滩球/等。

有两种常见的解决方案可以在不阻塞的情况下执行:在线程上执行 - 这很容易,但不能从后台线程访问Tkinter窗口小部件。或者以非阻塞的方式检查子进程的管道 - 如果有跨平台的方式来做到这一点,而不会永远阻塞,这将是非常好的。 (然而,PyPI上有第三方“异步子进程”封装,这可能是解决此问题的替代方法。)

因此,您需要结合这两个这些。有一个线程可以阻塞子进程,并在可以在主线程中以非阻塞方式检查的东西上发布消息,如queue.Queue

但是,不是自己写,而是一个很好的包装,称为mtTkinter,它为你做了大部分难题。你可以编写你的后台线程,就好像访问这些小部件是合法的,并且它会拦截该访问并将其转换为队列文章。可悲的是,mtTkinter在Python 3中不起作用 - 但它看起来很容易修复;在几分钟内我slapped together a port。尽管我没有对它做过多的测试,但我认为这可能是最简单的方法。

所以:

from tkinter import Tk, Frame, Text, BOTH 
import subprocess 
import sys 
import threading 

# ... 

def generate_money(textwidget): 
    p = subprocess.Popen([sys.executable, os.path.join(scriptpath, 'moneygenerator.py')], 
         stdout = subprocess.PIPE) 
    for line in p.stdout: 
     do_stuff_with(textwidget, line) 
    p.close() 

# ... 

class FrameApp(Frame): 
    # ... 
    def theText(self): 
     self.w = Text() 
     self.t = threading.Thread(target=generate_money, args=(self.w,)) 

你可能想增加一些方式要么告诉self.t提前关闭,或者等待它完成,在退出时间。 (或者,或者因为你知道它不能在它突然杀死后留下任何“危险垃圾”,你可能只能设置self.t.daemon = True)。但这足以显示基本知识。

+0

非常感谢您的回复。但我遇到了一个问题。我在这里发布新代码的问题,所以我会更新线程。 – TastyOs 2014-10-28 23:58:36