Question

I have embedded python interpreter (2.7.6) in dll (Windows 7) that is loaded as plugin in a native host program that has its own ui. Python script file is executed by dll, in that script file stderr is redirected to a file and another script is imported. In this script (code below) is a class that creates Tkinter window and and has write() method that writes to Tkinter text widget. Stdout is redirected by that class to itself. The class is run in thread.

Code below works great when imported by non-embedded python. When importing to embedded python, window appears and displays output whenever anything is printed to stdout, but otherwise the window is unresponsive (not responding). Stderr is empty.

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 "---------------"

I've also tried to launch this in subprocess and write to it's stdin. This time window remains responsive but output isn't appearing in output window until host application exits. Nothing in stderr. This is the code I added to end of script above:

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

And this is the code I used to launch subprocess and redirect 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"

Main question being:

What would be the best approach? And how to do it?

Extra questions:

1) Why my tkinter output window goes to not responding state but still updating when output is printed when not launching as subprocess? What can be done to make it responsive?

2) How to utilize the subprocess stdin in this case?

I'd really like to get it working without subprocessing, for such simple output window it is ok, but I'd like to build ui's with a bit more elaborate interfaces and working via stdin and stdout is not preferred.

Was it helpful?

Solution

Found solution in this:

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

Releasing the GIL in embedding dll made Tkinter window responsive without subprocessing. In my particular case I know that from certain point on all python code that is not put in it's own thread will only respond to callbacks called from dll, so it should be safe.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top