Ich habe die Antwort, falls sich jemand noch interessiert! (Ich habe auch auf Python 3 geändert, daher der import tkinter
statt import Tkinter
)
ich den Ansatz geändert haben etwas von der ursprünglichen durch eine separate Datei mit dem InteractiveConsole
, laufen und macht dann die Hauptdatei, um diese andere Datei öffnen (die ich console.py genannt und ist im selben Verzeichnis) in einem Teilprozess, der stdout Verknüpfung, stderr und stdin dieser subprocess zum tkinter Text-Widget programmatisch. Hier
ist der Code in der für die Konsole-Datei (falls dies in der Regel ausgeführt wird, wirkt es wie eine normale Konsole):
# console.py
import code
if __name__ == '__main__':
vars = globals().copy()
vars.update(locals())
shell = code.InteractiveConsole(vars)
shell.interact()
Und hier ist der Code für den Python-Interpreter, dass die Konsole läuft innerhalb des Text-Widget:
# main.py
import tkinter as tk
import subprocess
import queue
import os
from threading import Thread
class Console(tk.Frame):
def __init__(self,parent=None):
tk.Frame.__init__(self, parent)
self.parent = parent
self.createWidgets()
# get the path to the console.py file assuming it is in the same folder
consolePath = os.path.join(os.path.dirname(__file__),"console.py")
# open the console.py file (replace the path to python with the correct one for your system)
# e.g. it might be "C:\\Python35\\python"
self.p = subprocess.Popen(["python3",consolePath],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
# make queues for keeping stdout and stderr whilst it is transferred between threads
self.outQueue = queue.Queue()
self.errQueue = queue.Queue()
# keep track of where any line that is submitted starts
self.line_start = 0
# make the enter key call the self.enter function
self.ttyText.bind("<Return>",self.enter)
# a daemon to keep track of the threads so they can stop running
self.alive = True
# start the functions that get stdout and stderr in separate threads
Thread(target=self.readFromProccessOut).start()
Thread(target=self.readFromProccessErr).start()
# start the write loop in the main thread
self.writeLoop()
def destroy(self):
"This is the function that is automatically called when the widget is destroyed."
self.alive=False
# write exit() to the console in order to stop it running
self.p.stdin.write("exit()\n".encode())
self.p.stdin.flush()
# call the destroy methods to properly destroy widgets
self.ttyText.destroy()
tk.Frame.destroy(self)
def enter(self,e):
"The <Return> key press handler"
string = self.ttyText.get(1.0, tk.END)[self.line_start:]
self.line_start+=len(string)
self.p.stdin.write(string.encode())
self.p.stdin.flush()
def readFromProccessOut(self):
"To be executed in a separate thread to make read non-blocking"
while self.alive:
data = self.p.stdout.raw.read(1024).decode()
self.outQueue.put(data)
def readFromProccessErr(self):
"To be executed in a separate thread to make read non-blocking"
while self.alive:
data = self.p.stderr.raw.read(1024).decode()
self.errQueue.put(data)
def writeLoop(self):
"Used to write data from stdout and stderr to the Text widget"
# if there is anything to write from stdout or stderr, then write it
if not self.errQueue.empty():
self.write(self.errQueue.get())
if not self.outQueue.empty():
self.write(self.outQueue.get())
# run this method again after 10ms
if self.alive:
self.after(10,self.writeLoop)
def write(self,string):
self.ttyText.insert(tk.END, string)
self.ttyText.see(tk.END)
self.line_start+=len(string)
def createWidgets(self):
self.ttyText = tk.Text(self, wrap=tk.WORD)
self.ttyText.pack(fill=tk.BOTH,expand=True)
if __name__ == '__main__':
root = tk.Tk()
root.config(background="red")
main_window = Console(root)
main_window.pack(fill=tk.BOTH,expand=True)
root.mainloop()
der Grund, dass ist in separate Threads von stdout und stderr zu lesen ist, weil das Leseverfahren blockiert, die das Programm bewirkt einzufrieren, bis die console.py subprocess mehr Leistung gibt, es sei denn, diese sind in separaten Threads. Die writeLoop-Methode und die Warteschlangen werden benötigt, um in das Text-Widget zu schreiben, da tkinter nicht Thread-sicher ist.
Dies hat sicherlich noch Probleme ausgebügelt, wie die Tatsache, dass jeder Code auf dem Text-Widget noch einmal bearbeitet wird bereits eingereicht, aber hoffentlich beantwortet sie Ihre Frage.
EDIT: Ich habe auch einige der tkinter, so dass die Konsole verhält sich mehr wie ein Standard-Widget neatened.
mögliches Duplikat von http://stackoverflow.com/questions/21603038/python-compiler-connected-to-a-button – markcial
Dieses Problem ist ähnlich, aber diese Frage ist, wie man ein interaktives Terminal in einem tkinter Rahmen macht, und stdout und stderr abzufangen, stdin zu erwähnen war ein Tippfehler, den ich beheben werde. –
IDLE hat so etwas genannt [_Python Shell window_] (http://docs.python.org/2/library/idle.html#python-shell-window), und Sie können die [Quellcode] lesen (http : //hg.python.org/cpython/file/a87f284e14ea/Lib/idlelib). – martineau