python添加解释器_如何使用tkinter在python中嵌入python解释器框架?

我想在我的纯python tkinter应用程序中添加一个控制终端小部件,类似于Blender中提供的python解释器.它应该在相同的上下文(进程)中运行,以便用户可以添加功能并控制当前从控件小部件运行的应用程序.理想情况下,我希望它还“劫持”当前应用程序的stdout和stderr,以便在运行的应用程序中报告任何问题或调试信息.

这是我到目前为止所提出的.唯一的问题是它没有响应命令,并且当用户关闭窗口时线程不会停止.

import Tkinter as tk

import sys

import code

from threading import *

class Console(tk.Frame):

def __init__(self,parent=None):

tk.Frame.__init__(self, parent)

self.parent = parent

sys.stdout = self

sys.stderr = self

self.createWidgets()

self.consoleThread = ConsoleThread()

self.after(100,self.consoleThread.start)

def write(self,string):

self.ttyText.insert('end', string)

self.ttyText.see('end')

def createWidgets(self):

self.ttyText = tk.Text(self.parent, wrap='word')

self.ttyText.grid(row=0,column=0,sticky=tk.N+tk.S+tk.E+tk.W)

class ConsoleThread(Thread):

def __init__(self):

Thread.__init__(self)

def run(self):

vars = globals().copy()

vars.update(locals())

shell = code.InteractiveConsole(vars)

shell.interact()

if __name__ == '__main__':

root = tk.Tk()

root.config(background="red")

main_window = Console(root)

main_window.mainloop()

try:

if root.winfo_exists():

root.destroy()

except:

pass

解决方法:

我有答案,万一有人还在乎! (我也改为python 3,因此导入tkinter而不是导入Tkinter)

我通过使用单独的文件来运行InteractiveConsole,然后使主文件在子进程中打开另一个文件(我称之为console.py并且在同一目录中),将原来的方法略微改变了原来的方法,这个子进程的stdout,stderr和stdin以编程方式提供给tkinter Text小部件.

以下是控制台文件中的代码(如果这是正常运行,它就像一个普通的控制台):

# console.py

import code

if __name__ == '__main__':

vars = globals().copy()

vars.update(locals())

shell = code.InteractiveConsole(vars)

shell.interact()

这是python解释器的代码,它在Text小部件中运行控制台:

# 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("",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 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()

从stdout和stderr读取的原因是在单独的线程中,因为read方法是阻塞的,这会导致程序冻结,直到console.py子进程提供更多输出,除非这些是在单独的线程中.由于tkinter不是线程安全的,因此需要writeLoop方法和队列来写入Text小部件.

这肯定还有待解决的问题,例如,即使已经提交过,Text小部件上的任何代码都可以编辑,但希望它可以回答您的问题.

编辑:我还加入了一些tkinter,使得控制台的行为更像标准小部件.

标签:python,tkinter,interpreter,embed

来源: https://codeday.me/bug/20191001/1839095.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值