Tkinter窗口在嵌入式python中没有响应

时间:2014-02-02 22:16:46

标签: python windows dll tkinter embed

我在dll(Windows 7)中嵌入了python解释器(2.7.6),它作为插件加载到具有自己的ui的本机宿主程序中。 Python脚本文件由dll执行,在该脚本文件中,stderr被重定向到文件并导入另一个脚本。在这个脚本中(下面的代码)是一个创建Tkinter窗口的类,并且具有写入Tkinter文本小部件的write()方法。 Stdout被该类重定向到自身。该类在线程中运行。

下面的代码在非嵌入式python导入时效果很好。导入到嵌入式python时,窗口出现并在任何打印到stdout时显示输出,否则窗口没有响应(没有响应)。斯特德尔是空的。

import time
import threading
from Tkinter import *
from ScrolledText import *

class OutputWindow(threading.Thread):
    '''Output window where stdout is re-directed. Inherits Thread'''
    def __init__(self):

        self.autoScroll = True

        #root
        self.root = Tk()
        self.root.title("My output window")

        Grid.rowconfigure(self.root, 0, weight = 0)
        Grid.rowconfigure(self.root, 1, weight = 1)
        Grid.columnconfigure(self.root, 0, weight = 1)

        #frame for buttons
        topframe = Frame(self.root, width = "250px", height = "10px")
        topframe.grid(row = 0, sticky = E+W+N)
        Grid.rowconfigure(topframe, 0, weight = 1)

        #frame for text widget
        botframe = Frame(self.root)
        botframe.grid(row = 1, sticky = N+S+E+W)
        Grid.rowconfigure(botframe, 0, weight = 1)
        Grid.columnconfigure(botframe, 0, weight = 1)

        #autoscroll bool button
        self.autoScrollBtn = Button(topframe, text="AutoScroll", command = self.onAutoScroll)
        self.autoScrollBtn.grid(row = 0, column = 0, sticky = W+E)
        self.autoScrollBtn["relief"] = SUNKEN

        #clear button
        self.clearBtn = Button(topframe, text = "Clear", command = self.onClear)
        self.clearBtn.grid(row = 0, column = 1, sticky = W+E)

        #text widget with scrollbar
        self.text = ScrolledText(botframe)
        self.text.grid(row = 0, sticky = W+E+N+S)
        self.text.config(state = DISABLED)

        threading.Thread.__init__(self)

    def onAutoScroll(self):
        self.autoScroll = not self.autoScroll
        if self.autoScroll:
            self.autoScrollBtn["relief"] = SUNKEN
        else:
            self.autoScrollBtn["relief"] = RAISED

    def onClear(self):
        self.text.config(state = NORMAL)
        self.text.delete(1.0, END)
        self.text.config(state = DISABLED)

    def write(self, txt):
        '''write method for redirecting stdout.'''
        #print txt

        self.text.config(state = NORMAL)
        self.text.insert(END,str(txt))
        self.text.config(state = DISABLED)
        if self.autoScroll:
            self.text.see(END)

    def run(self):
        '''Thread.run method.'''
        self.stdout_prev = sys.stdout
        sys.stdout = self
        self.root.mainloop()
        sys.stdout = self.stdout_prev
        self.windowOpen = False


out = OutputWindow()
out.start()
time.sleep(0.1) #wait for thread to start properly...
print "Start of output"
print "---------------"

我也尝试在子进程中启动它并写入它的stdin。此时间窗口保持响应,但在主机应用程序退出之前输出不会出现在输出窗口中。 stderr没什么。这是我在上面的脚本末尾添加的代码:

for line in sys.stdin:        
    sys.stdout.write(line)

这是我用来启动子进程和重定向stdout的代码:

class redirect():
    def __init__(self, proc):
        self.p = proc
    def write(self, txt):
        self.p.stdin.write(txt)
        self.p.stdin.flush()

import subprocess
proc = subprocess.Popen('C:\\python27\\python.exe stackoverflow_sub.py', stdin = subprocess.PIPE)
sys.stdout = redirect(proc)
print "test redir"

主要问题是:

最好的方法是什么?怎么做?

额外问题:

1)为什么我的tkinter输出窗口没有响应状态但在未作为子进程启动时打印输出时仍然更新?可以做些什么来使其响应?

2)在这种情况下如何利用子进程stdin?

我真的很想让它在没有子处理的情况下工作,对于这样简单的输出窗口它是可以的,但我想用更复杂的接口构建ui,并且通过stdin和stdout工作不是首选。

1 个答案:

答案 0 :(得分:1)

在此找到解决方案:

Python Threads do not run in C++ Application Embedded Interpreter

在嵌入dll中释放GIL使得Tkinter窗口响应而不进行子处理。在我的特殊情况下,我知道从某些角度来看,没有放入它的所有python代码自己的线程只会响应从dll调用的回调,所以它应该是安全的。