Python 如何使tkinter文本框成为标准输入接收器?
我制作了两个tkinter文本框,其中一个以python脚本作为输入,另一个显示脚本的执行结果,但是当我使用Python 如何使tkinter文本框成为标准输入接收器?,python,tkinter,console,textbox,stdin,Python,Tkinter,Console,Textbox,Stdin,我制作了两个tkinter文本框,其中一个以python脚本作为输入,另一个显示脚本的执行结果,但是当我使用input()命令时,我遇到了一个错误。下面给出了stdout重定向器的类,以及读取脚本后执行的execute函数,该函数运行良好。我没有包括Text,tkinter等,因为我使用了所有与代码一起工作的通用方法,如Text.get(),Text.mark\u set(),Text.replace()等,这里也没有包括一些函数。除了脚本和输出框之外,我还尝试将整个控制台嵌入带有Interac
input()
命令时,我遇到了一个错误。下面给出了stdout重定向器的类,以及读取脚本后执行的execute函数,该函数运行良好。我没有包括Text
,tkinter
等,因为我使用了所有与代码一起工作的通用方法,如Text.get()
,Text.mark\u set()
,Text.replace()
等,这里也没有包括一些函数。除了脚本和输出框之外,我还尝试将整个控制台嵌入带有InteractiveConsole
的文本框中,但在接收输入或stdin的情况下问题是相同的,但在stdout
和stderr
两种情况下都可以正常工作
从代码导入InteractiveConsole、InteractiveInterpreter
类StdoutRedirector(对象):
def_uuuinit_uuu(self,text_小部件):
self.text\u space=text\u小部件
def写入(自身,字符串):
self.text\u space.insert('end',string)
self.text\u空格。请参见('end')
##类StdinRedirector(对象):
##def_uuuinit_uuu(self,text_小部件):
##self.text\u space=text\u小部件
##
##def读线(自身)->str:
##t=self.text_space.get(INSERT,f“{int(text.index(INSERT).split('.')[0])}.{int(text.index(INSERT).split('.')[1]))
##返回t
def执行(事件=无):
保存()
code=text.get('1.0',END+'-1c')
stdin=sys.stdin
stdout=sys.stdout
stderr=sys.stderr
输出.删除('1.0',结束)
##定义a():
##sys.stdin=StdinRedirector(输出)
##output.bind(“”,lambda:a)
sys.stdout=StdoutRedirector(输出)
sys.stderr=StdoutRedirector(输出)
interp=InteractiveInterpreter()
内部运行代码(代码)
sys.stdout=stdout
sys.stderr=stderr
##sys.stdin=stdin
之后,我尝试重定向stdin,这显然不起作用,相反,应用程序挂起,窗口停止响应,即使在一次又一次尝试之后。
请帮我做这个。。。我不知道这是否是不可能的,但PyCharm和其他人的内部有I/O流,因此控制台或执行窗口可以完全嵌入到文本框中。好的,在研究了web、文档和队列、idlelib和子流程模块的代码之后,我找到了使tkinter Textbox作为stdin、stdout和stderr接收器与python控制台交互的最简单方法。代码如下:
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]?', tk.END, 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()
将tkinter作为tk导入
导入子流程
导入队列
导入操作系统
从线程导入线程
类控制台(tk.Frame):
def uuu init uuuu(self,parent=None,**kwargs):
tk.Frame.\uuuuu init\uuuuuuu(自、父、**kwargs)
self.parent=parent
#创建小部件
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\u NO\u窗口)
#在线程之间传输stdout和stderr时,创建用于保存stdout和stderr的队列
self.outQueue=queue.queue()
self.errQueue=queue.queue()
#跟踪提交的任何行的起始位置
self.line_start=0
#一个守护进程来跟踪线程,以便它们可以停止运行
self.alive=True
#在单独的线程中启动获取stdout和stderr的函数
线程(target=self.readFromProcessOut).start()
线程(target=self.readfromProcesserr).start()
#在主线程中启动写循环
self.writeloop()
#事件的键绑定
self.ttytext.bind(“,self.enter)
self.ttytext.bind(“”,self.on_bkspace)
self.ttytext.bind(“”,self.on_delete)
self.ttytext.bind(“”,self.on_copy)
self.ttytext.bind(“”,self.on_粘贴)
self.ttytext.bind(“”,self.on_copy)
self.ttytext.bind(“”,self.on_粘贴)
def销毁(自我):
“”“这是当小部件被销毁时自动调用的函数。”“”
self.alive=False
#将exit()写入控制台以停止其运行
self.p.stdin.write(“exit()\n”.encode())
self.p.stdin.flush()
#调用destroy方法来正确销毁小部件
self.ttytext.destroy()
框架破坏(自我)
def enter(自我,事件):
“”“按键处理程序”“”
cur_ind=str(self.ttytext.index(tk.INSERT))
如果int(cur_ind.split('.')[0])0:
self.ttytext.insert(tk.END,选中)
self.ttytext.mark_集(tk.INSERT,tk.END)
self.ttytext.see(tk.INSERT)
返回“中断”
除:
selected=self.ttytext.get(
self.ttytext.search(“:”,tk.INSERT,backwards=True),tk.INSERT)
self.ttytext.insert(tk.END,selected.strip(':'))
self.ttytext.mark_集(tk.INSERT,tk.END)
self.ttytext.see(tk.INSERT)
返回“中断”
string=self.ttytext.get(1.0,tk.END)[self.line\u start:]
self.line_start+=len(字符串)
self.p.stdin.write(string.encode())
self.p.stdin.flush()
bku空间上的def(自身、事件):
通过
def on_删除(自身、事件):
通过
def on_键(自身,事件):
“”“类型控制()处理程序”“”
cur_ind=str(self.ttytext.index(tk.INSERT))
尝试: