Hayden Ravenscroft Hayden Ravenscroft - 3 months ago 31
Python Question

How to use idlelib.PyShell to embed an interpreter in a tkinter program?

I need to embed an interative python interpreter into my tkinter program. Could anyone help me out as to how to integrate it?

I have already looked at the

main()
function, but it's way to complex for my needs, but I can't seem to reduce it without breaking it.

Answer

Some details of what you must do may depend on what you want to do with IDLE's Shell once you have it running. I would like to know more about that. But let us start simple and make the minimum changes to pyshell.main needed to make it run with other code.

Note that in 3.6, which I use below, PyShell.py is renamed pyshell.py. Also note that everything here amounts to using IDLE's private internals and is 'use at your own risk'.

I presume you want to run Shell in the same process (and thread) as your tkinter code. Change the signature to

def main(tkroot=None):

Change root creation (find # setup root) to

if not tkroot:
    root = Tk(className="Idle")
    root.withdraw()
else:
    root = tkroot

In current 3.6, there are a couple more lines to be indented under if not tkroot:

    if use_subprocess and not testing:
        NoDefaultRoot()

Guard mainloop and destroy (at the end) with

if not tkroot:
    while flist.inversedict:  # keep IDLE running while files are open.
        root.mainloop()
    root.destroy()
# else leave mainloop and destroy to caller of main

The above adds 'dependency injection' of a root window to the function. I might add it in 3.6 to make testing (an example of 'other code') easier.

The follow tkinter program now runs, displaying the both the root window and an IDLE shell.

from tkinter import *
from idlelib import pyshell

root = Tk()
Label(root, text='Root id is '+str(id(root))).pack()
root.update()
def later():
    pyshell.main(tkroot=root)
    Label(root, text='Use_subprocess = '+str(pyshell.use_subprocess)).pack()

root.after(0, later)
root.mainloop()

You should be able to call pyshell.main whenever you want.