问题描述
我制作了两个tkinter文本框,其中一个将您的python脚本作为输入,另一个显示了脚本执行的结果,但是当我使用input()
命令时出现错误。下面给出的是stdout重定向器的类,以及在读取脚本后执行的execute函数,效果很好。我没有包括Text
,tkinter
等,因为我使用了与Text.get()
,Text.mark_set()
,Text.replace()
等类似的所有通用方法也有些功能不在此处。除了脚本和输出框之外,我还尝试将整个控制台嵌入到带有InteractiveConsole
的文本框中,但是在接收输入或标准输入的情况下,问题是相同的,但是在两种情况下,stdout
和stderr
正常。
from code import InteractiveConsole,InteractiveInterpreter
class StdoutRedirector(object):
def __init__(self,text_widget):
self.text_space = text_widget
def write(self,string):
self.text_space.insert('end',string)
self.text_space.see('end')
##class StdinRedirector(object):
## def __init__(self,text_widget):
## self.text_space = text_widget
##
## def readline(self) -> str:
## t = self.text_space.get(INSERT,f"{int(text.index(INSERT).split('.')[0])}.{int(text.index(INSERT).split('.')[1])}")
## return t
def execute(event=None):
save()
code = text.get('1.0',END+'-1c')
stdin = sys.stdin
stdout = sys.stdout
stderr = sys.stderr
output.delete('1.0',END)
## def a():
## sys.stdin = StdinRedirector(output)
## output.bind('<Return>',lambda: a)
sys.stdout = StdoutRedirector(output)
sys.stderr = StdoutRedirector(output)
interp = InteractiveInterpreter()
interp.runcode(code)
sys.stdout = stdout
sys.stderr = stderr
## sys.stdin = stdin
此后,我尝试了重定向stdin,这显然不起作用,而是挂起了应用程序,并且即使一次又一次尝试后,窗口也停止响应。 请帮助我...我不知道它是否不可能,但是PyCharm和其他人内部都具有I / O流,因此控制台或执行窗口可以完全嵌入文本框中。
解决方法
好的,在对网络、文档以及队列、idlelib 和子进程模块的代码进行研究之后,我找到了使 tkinter 文本框作为 stdin、stdout 和 stderr 接收器与 python 控制台交互的最简单方法。代码如下:
# get context
$apimContext = New-AzApiManagementContext -ResourceGroupName "targetResourceGroup" -ServiceName "targetApimInstance"
$api = Get-AzApiManagementApi -Context $apimContext -ApiId "api-id"
# set subscriptionRequired to false
$api.SubscriptionRequired=$false
Set-AzApiManagementApi -InputObject $api
上面的代码使用了jupyter qtconsole(因为它非常好用),否则也可以使用import tkinter as tk
import subprocess
import queue
import os
from threading import Thread
class Console(tk.Frame):
def __init__(self,parent=None,**kwargs):
tk.Frame.__init__(self,parent,**kwargs)
self.parent = parent
# create widgets
self.ttytext = tk.Text(self,wrap=tk.WORD)
self.ttytext.pack(fill=tk.BOTH,expand=True)
self.ttytext.linenumbers.pack_forget()
self.p = subprocess.Popen(["jupyter","qtconsole"],stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.PIPE,creationflags=subprocess.CREATE_NO_WINDOW)
# 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
# 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()
# key bindings for events
self.ttytext.bind("<Return>",self.enter)
self.ttytext.bind('<BackSpace>',self.on_bkspace)
self.ttytext.bind('<Delete>',self.on_delete)
self.ttytext.bind('<<Copy>>',self.on_copy)
self.ttytext.bind('<<Paste>>',self.on_paste)
self.ttytext.bind('<Control-c>',self.on_copy)
self.ttytext.bind('<Control-v>',self.on_paste)
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,event):
"""The <Return> key press handler"""
cur_ind = str(self.ttytext.index(tk.INSERT))
if int(cur_ind.split('.')[0]) < int(self.ttytext.search(': ',tk.END,backwards=True).split('.')[0]):
try:
selected = self.ttytext.get('sel.first','sel.last')
if len(selected) > 0:
self.ttytext.insert(tk.END,selected)
self.ttytext.mark_set(tk.INSERT,tk.END)
self.ttytext.see(tk.INSERT)
return 'break'
except:
selected = self.ttytext.get(
self.ttytext.search(': ',tk.INSERT,backwards=True),tk.INSERT)
self.ttytext.insert(tk.END,selected.strip(': '))
self.ttytext.mark_set(tk.INSERT,tk.END)
self.ttytext.see(tk.INSERT)
return 'break'
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 on_bkspace(self,event):
pass
def on_delete(self,event):
pass
def on_key(self,event):
"""The typing control (<KeyRelease>) handler"""
cur_ind = str(self.ttytext.index(tk.INSERT))
try:
if int(cur_ind.split('.')[0]) < int(self.ttytext.search(r'In [0-9]?',backwards=True).split('.')[0]):
return 'break'
except:
return
def on_copy(self,event):
"""<Copy> event handler"""
self.ttytext.clipboard_append(self.ttytext.get('sel.first','sel.last'))
# I created this function because I was going to make a custom textbox
def on_paste(self,event):
"""<Paste> event handler"""
self.ttytext.insert(tk.INSERT,self.ttytext.clipboard_get())
# I created this function because I was going to make a custom textbox
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)
self.ttytext.inst_trigger()
if __name__ == '__main__':
root = tk.Tk()
main_window = Console(root)
main_window.pack(fill=tk.BOTH,expand=True)
main_window.ttytext.focus_force()
root.mainloop()
模块中的InteractiveShell()
来使用简单的python shell。
我还没有完全为 code
键、Enter
和 Up
箭头键制作函数。这些可以由用户根据自己的选择进行。
这也可以在 Oli 的回答 here 中找到,并且可以自定义。