Tkinter에서 작은 IDLE과 유사한 Python Shell을 어떻게 만들 수 있습니까?


9

Python Shell GUI로 제어하는 ​​것을 만들려고합니다.

유일한 것은, 그 전체 입출력을 만드는 방법을 모른다는 것입니다. 입력을 입력하고 python 명령을 실행하고 python 명령의 출력을 제공하고 싶습니다. IDLE이 Tkinter로 만들어 졌으므로 위젯을 사용한다는 것을 알고 있습니까?

말 그대로 "유형 입력, 출력 표시"일뿐입니다.

검색을 시도했지만 대부분의 결과가 명령 줄과 관련이있는 것 같습니다. 이것은 내가 찾고있는 것이 아닙니다. 내 것과 정확히 같은 유일한 질문은 내가 생각한 것이 아닙니다. 또한 IDLE의 소스 코드를 찾으려고했지만 원하는 것을 찾을 수 없었습니다.

Linux에서 작동하는 답변을 찾았지만 Windows 10을 사용하고 있습니다 ...

화면의 한쪽에 명령 출력에 연결된 다른 것이 있기 때문에 Tkinter에 "쉘"이 필요합니다.

누구나 매우 간단한 파이썬 쉘을 만드는 데 사용되는 위젯을 알고 있습니까?


stackoverflow.com/questions/38977525/… 관심이있을 수 있습니다.
jasonharper

그 질문은 삭제 된 다른 질문의 후속처럼 보입니다. 삭제 된 질문의 맥락이 없으면 나에게 이해가되지 않습니다 ... 적어도 지금은 가능하다는 것을 알고 있습니다. 무언가를 시도하는 단 한 사람 ... 다시
캐릭터 마법사 엘리자

1
유휴는 tkinter를 사용하여 파이썬으로 작성되었습니다 ... 소스 코드를 읽으십시오 .
Reblochon Masque

답변:


13

간단한 파이썬 쉘 / 터미널 / 명령 프롬프트


  • ********************* 그건 말 그대로 그냥 " type input, show output"일. ************************

import os
from tkinter import *
from subprocess import *


class PythonShell:

    def __init__(self):
        self.master = Tk()

        self.mem_cache = open("idle.txt", "w+")
        self.body = None
        self.entry = None
        self.button = None
        self.entry_content = None

    @staticmethod
    def welcome_note():
        """
        To show welcome note on tkinter window
        :return:
        """
        Label(text="Welcome To My Python Program [Version 1.0]", font='Arial 12', background="#272626",
              foreground="white").pack()

        Label(text=">> Insert Python Commands <<", font='Arial 12', background="#272626",
              foreground="white").pack()

    def get_text(self):
        """
        This method will perform following operations;
        1- Get text from body
        2- Implies python compilation (treat text as command)
        3- Set Output in Output-Entry

        :return: get and set text in body of text box
        """
        content = self.body.get(1.0, "end-1c")
        out_put = self.run_commands(content)
        self.entry_content.set(out_put)

    def store_commands(self, command=None):

        try:
            self.mem_cache.write(command + ';')
            self.mem_cache.close()

        except Exception as e:
            print(e)

    def get_stored_commands(self):
        try:
            with open("idle.txt", "r") as self.mem_cache:
                self.mem_cache.seek(0)
                val = self.mem_cache.read()
                self.mem_cache.close()
                return val

        except Exception as e:
            print(e)

    @staticmethod
    def check_if_file_empty():
        size = os.stat("idle.txt").st_size

        if size != 0:
            return True
        else:
            return False

    def run_commands(self, command):
        """

        This method would return output of every command place in text box
        :param command: python command from text box
        :return: output of command
        """

        print("Running command: {}".format(command))
        value = None
        new_line_char = command.find('\n')
        semi_colons_char = command.find(';')
        double_quote = command.find('"')

        try:
            if new_line_char != -1:

                if semi_colons_char != -1 & double_quote == -1:

                    new_cmd = command.replace("\n", "")
                    cmd_value = '"' + new_cmd + '"'
                    self.store_commands(command)

                    value = check_output("python -c " + cmd_value, shell=True).decode()
                elif semi_colons_char == -1 & double_quote == -1:

                    new_cmd = command.replace("\n", ";")
                    cmd_value = '"' + new_cmd + '"'
                    self.store_commands(command)
                    value = check_output("python -c " + cmd_value, shell=True).decode()

                elif double_quote != -1:

                    cmd_1 = command.replace('"', "'")
                    new_cmd = cmd_1.replace('\n', ';')

                    cmd_value = '"' + new_cmd + '"'
                    self.store_commands(command)

                    value = check_output("python -c " + cmd_value, shell=True).decode()

                elif self.body.compare("end-1c", "==", "1.0"):
                    self.entry_content.set("the widget is empty")

            elif self.body.compare("end-1c", "==", "1.0"):
                value = "The widget is empty. Please Enter Something."

            else:
                variable_analyzer = command.find('=')
                file_size = PythonShell.check_if_file_empty()

                if file_size:
                    new_cmd = command.replace('"', "'")
                    cmd_value = '"' + new_cmd + '"'
                    stored_value = self.get_stored_commands()
                    cmd = stored_value + cmd_value
                    cmd.replace('"', '')

                    value = check_output("python -c " + cmd, shell=True).decode()
                elif variable_analyzer != -1:
                    new_cmd = command.replace('"', "'")
                    cmd_value = '"' + new_cmd + '"'
                    self.store_commands(cmd_value)

                    value = 'Waiting for input...'
                    pass
                else:
                    new_cmd = command.replace('"', "'")
                    cmd_value = '"' + new_cmd + '"'
                    value = check_output("python -c " + cmd_value, shell=True).decode()

        except Exception as ex:
            print('>>>', ex)
            self.entry_content.set('Invalid Command. Try again!!!')

        print('>>', value)
        # To Clear Text body After Button Click
        # self.body.delete('1.0', END)

        return value

    def start_terminal(self):
        """
        Initiate tkinter session to place and run commands
        :return:
        """
        self.master.propagate(0)
        self.master.geometry('750x350')
        self.master.title('Python IDLE')
        self.master.configure(background='#272626')

        terminal.welcome_note()

        self.body = Text(self.master, height='10', width='75', font='Consolas 12', background="#272626",
                         foreground="white",
                         insertbackground='white')
        # self.body.propagate(0)
        self.body.pack(expand=True)

        Label(text=">> Command Output <<", font='Arial 12', background="#272626",
              foreground="white").pack()

        self.entry_content = StringVar()
        self.entry = Entry(self.master, textvariable=self.entry_content, width=50, font='Consolas 16',
                           background="white",
                           foreground="black")
        self.entry.pack()
        # self.entry.propagate(0)

        self.button = Button(self.master, text="Run Command", command=self.get_text, background="white",
                             foreground="black",
                             font='Helvetica 12').pack()

        self.master.mainloop()


if __name__ == '__main__':
    terminal = PythonShell()
    terminal.start_terminal()

위의 파이썬 스크립트는 주어진 계층 구조를 가지고 있습니다.

    |import ...      
    |class PythonShell:
        |def __init__(self):...

        @staticmethod
        |def welcome_note():...
        |def get_text(self):...
        |def store_commands(self, commmand):...
        |def get_stored_commands(self):...

        @staticmethod
        |def check_if_file_empty():
        |def run_commands(self, command):...
        |def start_terminal(self):...

    |if __name__ == '__main__':...

워크 플로우 :

위 코드의 기본 워크 플로우는 다음과 같습니다.

  • def welcome_note():... 텍스트 본문 외부에 표시 될 레이블을 포함합니다.

  • def get_text(self):...두 가지 작업을 수행합니다. ** 텍스트 본문에서 텍스트 가져 오기 ** & ** 입력 상자에서 출력 설정 **.

  • def store_commands(self, command):... 변수를 파일에 저장하는 데 사용합니다.

  • def get_stored_commands(self):... 파일에 저장된 변수를 가져옵니다.

  • def check_if_file_empty():... 파일 크기를 확인하십시오.

  • def run_commands(self, command):...이 메소드 는 명령을 받아서 처리하고 지정된 명령에 대한 출력을 생성하는 Python 컴파일러 역할 을합니다. 명령을 실행하려면 subprocess-module새 프로세스를 생성하고 결과를 검색하기위한보다 강력한 기능을 제공하므로 사용하는 것이 좋습니다 . 파이썬을 사용하여 창 명령 을 실행하려면 다음 과 같은 다양한 내장 라이브러리가 있습니다.

    1. os ( 상세 ), 2. subprocess ( 상세 ) 등

    사용하기에 더 좋은 것을 확인하려면 참조를 방문하십시오 : subprocess-module은 os-module보다 좋습니다 .

  • def start_terminal(self):...이 방법은 단순히 tkinter세션 창 을 시작 하고 입력 및 출력 창에 대한 기본 레이아웃을 표시 하는 기능을 포함합니다 .

    요구 사항에 따라이 코드를 추가로 수정하고 최적화 할 수 있습니다.


워 라우드 :

이 단순 tkinter GUI based python shell은 Windows 명령 프롬프트와 같은 간단한 기능을 수행합니다. directly파이썬 터미널로 이동하지 않고 명령 프롬프트에서 파이썬 명령을 실행하려면 다음 과 같이 간단합니다.

python -c "print('Hey Eleeza!!!')"

결과는 다음과 같이 간단합니다.

Hey Eleeza!!!

마찬가지로 주어진 시간에 한 번에 두 줄 이상을 직접 실행합니다.

python -c "import platform;sys_info=platform.uname();print(sys_info)"

출력은 다음과 같습니다.

My System Info: uname_result(system='Windows', node='DESKTOP-J75UTG5', release='10', version='10.0.18362', machine='AMD64', processor='Intel64 Family 6 Model 142 Stepping 10, GenuineIntel')

따라서 이것을 사용하려면 tkinter python shell;

  • 명령을 다음과 같이 배치 할 수 있습니다.

    import platform
    value=platform.uname()
    print('Value:', value)
  • 또는 이런 식으로;

    import platform;value=platform.uname();
    print('Value:', value)
  • 또는 단순히 다음과 같이 인라인 명령

    import platform;value=platform.uname();print('Value:', value)

같은 결과를 얻을 수 있습니다.


1
이 입력 / 출력은 완벽합니다. 대단히 감사합니다! 그러나 변수를 할당하고 명령을 실행 한 다음 변수를 지우고 인쇄하려고하면 그렇게하지 않으므로 실제 파이썬에서 명령을 실행하는 방법은 무엇입니까? 그렇게합니까?)
캐릭터 마법사 엘리자

2
@Eleeza, 우선 귀하의 질문에 이러한 유형의 요구 사항이 포함되어 있지 않습니다. 매우 기본 input이므로 output 쉘. 기본처럼 작동 python kernel합니다. text-body에 배치 된 위치에서만 작동합니다 . 변수 히스토리를 보유하기 위해 버퍼 또는 메모리 캐시 를 설정하지 않았습니다 . 이 유휴 상태에서이 요구 사항을 먼저 확인하겠습니다!
무하마드 Usman

1
제가 만들려고하는 것은 전 세계가 메인 코드에 프로그래밍되어 있고 터미널은 그것을 탐색하고 상호 작용하는 데 사용되는 게임이었습니다. 출력을 표시하기 전에 코드를 실행하는 경우라고 생각했지만 확인해야합니다.
캐릭터 마법사 엘리자

2
세부적인 설명 : 저는 메인 게임 프로그램에서 일종의 "세계"를 만들고 있습니다. 사람들, 장소 등은 모두 파이썬의 객체입니다. 플레이어는 GUI 터미널을 통해 세계를 탐색하고 파이썬 명령을 사용합니다. 탐색을 통해 파이썬을 배우는 게임입니다. 실제로 코드는 실제로 수정되지만 나중에 재설정됩니다.
캐릭터 마법사 엘리자

8
@Eleeza, 여기에 당신이있는 완전한 트랙을 줄 수있는 최고의 github 저장소 가 있습니다; 방문 참조 : github.com/codecombat/codecombat . 이것들도 참고해야 할 다른 참고 문헌들이 있습니다; github.com/replit/play , github.com/jatinmandav/Gaming-in-Python , github.com/PacktPublishing/-Learn-Python-Programming-with-Games , github.com/… , github.com/pyland/pyland 여기서, 우물 같은 다른 기준이다 github.com/CharlesPikachu/Games
무하마드 우스만

4

이것은 주로 exec()파이썬 문 subprocess.Popen()을 실행하고 외부 명령 을 실행하는 데 사용되는 간단한 셸입니다 .

import tkinter as tk
import sys, io
import subprocess as subp
from contextlib import redirect_stdout

class Shell(tk.Text):
  def __init__(self, parent, **kwargs):
    tk.Text.__init__(self, parent, **kwargs)
    self.bind('<Key>', self.on_key) # setup handler to process pressed keys
    self.cmd = None        # hold the last command issued
    self.show_prompt()

  # to append given text at the end of Text box
  def insert_text(self, txt='', end='\n'):
    self.insert(tk.END, txt+end)
    self.see(tk.END) # make sure it is visible

  def show_prompt(self):
    self.insert_text('>> ', end='')
    self.mark_set(tk.INSERT, tk.END) # make sure the input cursor is at the end
    self.cursor = self.index(tk.INSERT) # save the input position

  # handler to process keyboard input
  def on_key(self, event):
    #print(event)
    if event.keysym == 'Up':
      if self.cmd:
        # show the last command
        self.delete(self.cursor, tk.END)
        self.insert(self.cursor, self.cmd)
      return "break" # disable the default handling of up key
    if event.keysym == 'Down':
      return "break" # disable the default handling of down key
    if event.keysym in ('Left', 'BackSpace'):
      current = self.index(tk.INSERT) # get the current position of the input cursor
      if self.compare(current, '==', self.cursor):
        # if input cursor is at the beginning of input (after the prompt), do nothing
        return "break"
    if event.keysym == 'Return':
      # extract the command input
      cmd = self.get(self.cursor, tk.END).strip()
      self.insert_text() # advance to next line
      if cmd.startswith('`'):
        # it is an external command
        self.system(cmd)
      else:
        # it is python statement
        self.execute(cmd)
      self.show_prompt()
      return "break" # disable the default handling of Enter key
    if event.keysym == 'Escape':
      self.master.destroy() # quit the shell

  # function to handle python statement input
  def execute(self, cmd):
    self.cmd = cmd  # save the command
    # use redirect_stdout() to capture the output of exec() to a string
    f = io.StringIO()
    with redirect_stdout(f):
      try:
        exec(self.cmd, globals())
      except Exception as e:
        print(e)
    # then append the output of exec() in the Text box
    self.insert_text(f.getvalue(), end='')

  # function to handle external command input
  def system(self, cmd):
    self.cmd = cmd  # save the command
    try:
      # extract the actual command
      cmd = cmd[cmd.index('`')+1:cmd.rindex('`')]
      proc = subp.Popen(cmd, stdout=subp.PIPE, stderr=subp.PIPE, text=True)
      stdout, stderr = proc.communicate(5) # get the command output
      # append the command output to Text box
      self.insert_text(stdout)
    except Exception as e:
      self.insert_text(str(e))

root = tk.Tk()
root.title('Simple Python Shell')

shell = Shell(root, width=100, height=50, font=('Consolas', 10))
shell.pack(fill=tk.BOTH, expand=1)
shell.focus_set()

root.mainloop()

일반적인 파이썬 문장을 입력하십시오.

>> x = 1
>> print(x)
1

또는 쉘 명령을 입력하십시오.

>> `cmd /c date /t`
2019-12-09

Up키를 사용 하여 마지막 명령을 불러올 수도 있습니다 .

사용자 입력이 필요한 시스템 명령을 실행하면 셸이 5 초 동안 정지됩니다 (에서 사용 된 시간 초과 기간 communicate()).

on_key()필요에 따라 기능을 수정할 수 있습니다 .

또한 사용 exec()하는 것이 좋은 습관이 아니라는 점을 기억하십시오 .


코드를 설명해 주시겠습니까? 나는이 모든
것에서

1
내 코드에 주석을 추가했는데 도움이되기를 바랍니다.
acw1668

3

code.InteractiveConsole프로젝트의 명령을 실행하기 위해 python shell을 구현 했습니다. 아래는 단순화 된 버전이지만 파이썬 콘솔 에서처럼 특수 키 (예 : Return, Tab ...)에 대한 바인딩을 작성했기 때문에 여전히 길었습니다. jedi를 통한 자동 완성 및 피그먼트를 사용한 구문 하이팅과 같은 더 많은 기능을 추가 할 수 있습니다.

주요 아이디어는의 push()방법을 사용 code.InteractiveConsole하여 명령을 실행하는 것입니다. 이 메소드는 True예를 들어 부분 명령 인 경우 리턴 def test(x):하며이 피드백을 사용하여 ...프롬프트 를 삽입 합니다. 그렇지 않으면 출력이 표시되고 새 >>>프롬프트가 표시됩니다. 를 사용하여 출력을 캡처합니다 contextlib.redirect_stdout.

또한 사용자가 이전에 실행 한 명령 내에 텍스트를 삽입하지 못하기 때문에 마크와 인덱스를 비교하는 많은 코드가 있습니다. 아이디어는 활성 프롬프트의 시작 위치를 알려주는 '입력'마크를 작성 self.compare('insert', '<', 'input')했으며 사용자가 활성 프롬프트 위에 텍스트를 삽입하려고 할 때 알 수 있다는 것입니다.

import tkinter as tk
import sys
import re
from code import InteractiveConsole
from contextlib import redirect_stderr, redirect_stdout
from io import StringIO


class History(list):
    def __getitem__(self, index):
        try:
            return list.__getitem__(self, index)
        except IndexError:
            return


class TextConsole(tk.Text):
    def __init__(self, master, **kw):
        kw.setdefault('width', 50)
        kw.setdefault('wrap', 'word')
        kw.setdefault('prompt1', '>>> ')
        kw.setdefault('prompt2', '... ')
        banner = kw.pop('banner', 'Python %s\n' % sys.version)
        self._prompt1 = kw.pop('prompt1')
        self._prompt2 = kw.pop('prompt2')
        tk.Text.__init__(self, master, **kw)
        # --- history
        self.history = History()
        self._hist_item = 0
        self._hist_match = ''

        # --- initialization
        self._console = InteractiveConsole() # python console to execute commands
        self.insert('end', banner, 'banner')
        self.prompt()
        self.mark_set('input', 'insert')
        self.mark_gravity('input', 'left')

        # --- bindings
        self.bind('<Control-Return>', self.on_ctrl_return)
        self.bind('<Shift-Return>', self.on_shift_return)
        self.bind('<KeyPress>', self.on_key_press)
        self.bind('<KeyRelease>', self.on_key_release)
        self.bind('<Tab>', self.on_tab)
        self.bind('<Down>', self.on_down)
        self.bind('<Up>', self.on_up)
        self.bind('<Return>', self.on_return)
        self.bind('<BackSpace>', self.on_backspace)
        self.bind('<Control-c>', self.on_ctrl_c)
        self.bind('<<Paste>>', self.on_paste)

    def on_ctrl_c(self, event):
        """Copy selected code, removing prompts first"""
        sel = self.tag_ranges('sel')
        if sel:
            txt = self.get('sel.first', 'sel.last').splitlines()
            lines = []
            for i, line in enumerate(txt):
                if line.startswith(self._prompt1):
                    lines.append(line[len(self._prompt1):])
                elif line.startswith(self._prompt2):
                    lines.append(line[len(self._prompt2):])
                else:
                    lines.append(line)
            self.clipboard_clear()
            self.clipboard_append('\n'.join(lines))
        return 'break'

    def on_paste(self, event):
        """Paste commands"""
        if self.compare('insert', '<', 'input'):
            return "break"
        sel = self.tag_ranges('sel')
        if sel:
            self.delete('sel.first', 'sel.last')
        txt = self.clipboard_get()
        self.insert("insert", txt)
        self.insert_cmd(self.get("input", "end"))
        return 'break'

    def prompt(self, result=False):
        """Insert a prompt"""
        if result:
            self.insert('end', self._prompt2, 'prompt')
        else:
            self.insert('end', self._prompt1, 'prompt')
        self.mark_set('input', 'end-1c')

    def on_key_press(self, event):
        """Prevent text insertion in command history"""
        if self.compare('insert', '<', 'input') and event.keysym not in ['Left', 'Right']:
            self._hist_item = len(self.history)
            self.mark_set('insert', 'input lineend')
            if not event.char.isalnum():
                return 'break'

    def on_key_release(self, event):
        """Reset history scrolling"""
        if self.compare('insert', '<', 'input') and event.keysym not in ['Left', 'Right']:
            self._hist_item = len(self.history)
            return 'break'

    def on_up(self, event):
        """Handle up arrow key press"""
        if self.compare('insert', '<', 'input'):
            self.mark_set('insert', 'end')
            return 'break'
        elif self.index('input linestart') == self.index('insert linestart'):
            # navigate history
            line = self.get('input', 'insert')
            self._hist_match = line
            hist_item = self._hist_item
            self._hist_item -= 1
            item = self.history[self._hist_item]
            while self._hist_item >= 0 and not item.startswith(line):
                self._hist_item -= 1
                item = self.history[self._hist_item]
            if self._hist_item >= 0:
                index = self.index('insert')
                self.insert_cmd(item)
                self.mark_set('insert', index)
            else:
                self._hist_item = hist_item
            return 'break'

    def on_down(self, event):
        """Handle down arrow key press"""
        if self.compare('insert', '<', 'input'):
            self.mark_set('insert', 'end')
            return 'break'
        elif self.compare('insert lineend', '==', 'end-1c'):
            # navigate history
            line = self._hist_match
            self._hist_item += 1
            item = self.history[self._hist_item]
            while item is not None and not item.startswith(line):
                self._hist_item += 1
                item = self.history[self._hist_item]
            if item is not None:
                self.insert_cmd(item)
                self.mark_set('insert', 'input+%ic' % len(self._hist_match))
            else:
                self._hist_item = len(self.history)
                self.delete('input', 'end')
                self.insert('insert', line)
            return 'break'

    def on_tab(self, event):
        """Handle tab key press"""
        if self.compare('insert', '<', 'input'):
            self.mark_set('insert', 'input lineend')
            return "break"
        # indent code
        sel = self.tag_ranges('sel')
        if sel:
            start = str(self.index('sel.first'))
            end = str(self.index('sel.last'))
            start_line = int(start.split('.')[0])
            end_line = int(end.split('.')[0]) + 1
            for line in range(start_line, end_line):
                self.insert('%i.0' % line, '    ')
        else:
            txt = self.get('insert-1c')
            if not txt.isalnum() and txt != '.':
                self.insert('insert', '    ')
        return "break"

    def on_shift_return(self, event):
        """Handle Shift+Return key press"""
        if self.compare('insert', '<', 'input'):
            self.mark_set('insert', 'input lineend')
            return 'break'
        else: # execute commands
            self.mark_set('insert', 'end')
            self.insert('insert', '\n')
            self.insert('insert', self._prompt2, 'prompt')
            self.eval_current(True)

    def on_return(self, event=None):
        """Handle Return key press"""
        if self.compare('insert', '<', 'input'):
            self.mark_set('insert', 'input lineend')
            return 'break'
        else:
            self.eval_current(True)
            self.see('end')
        return 'break'

    def on_ctrl_return(self, event=None):
        """Handle Ctrl+Return key press"""
        self.insert('insert', '\n' + self._prompt2, 'prompt')
        return 'break'

    def on_backspace(self, event):
        """Handle delete key press"""
        if self.compare('insert', '<=', 'input'):
            self.mark_set('insert', 'input lineend')
            return 'break'
        sel = self.tag_ranges('sel')
        if sel:
            self.delete('sel.first', 'sel.last')
        else:
            linestart = self.get('insert linestart', 'insert')
            if re.search(r'    $', linestart):
                self.delete('insert-4c', 'insert')
            else:
                self.delete('insert-1c')
        return 'break'

    def insert_cmd(self, cmd):
        """Insert lines of code, adding prompts"""
        input_index = self.index('input')
        self.delete('input', 'end')
        lines = cmd.splitlines()
        if lines:
            indent = len(re.search(r'^( )*', lines[0]).group())
            self.insert('insert', lines[0][indent:])
            for line in lines[1:]:
                line = line[indent:]
                self.insert('insert', '\n')
                self.prompt(True)
                self.insert('insert', line)
                self.mark_set('input', input_index)
        self.see('end')

    def eval_current(self, auto_indent=False):
        """Evaluate code"""
        index = self.index('input')
        lines = self.get('input', 'insert lineend').splitlines() # commands to execute
        self.mark_set('insert', 'insert lineend')
        if lines:  # there is code to execute
            # remove prompts
            lines = [lines[0].rstrip()] + [line[len(self._prompt2):].rstrip() for line in lines[1:]]
            for i, l in enumerate(lines):
                if l.endswith('?'):
                    lines[i] = 'help(%s)' % l[:-1]
            cmds = '\n'.join(lines)
            self.insert('insert', '\n')
            out = StringIO()  # command output
            err = StringIO()  # command error traceback
            with redirect_stderr(err):     # redirect error traceback to err
                with redirect_stdout(out): # redirect command output
                    # execute commands in interactive console
                    res = self._console.push(cmds)
                    # if res is True, this is a partial command, e.g. 'def test():' and we need to wait for the rest of the code
            errors = err.getvalue()
            if errors:  # there were errors during the execution
                self.insert('end', errors)  # display the traceback
                self.mark_set('input', 'end')
                self.see('end')
                self.prompt() # insert new prompt
            else:
                output = out.getvalue()  # get output
                if output:
                    self.insert('end', output, 'output')
                self.mark_set('input', 'end')
                self.see('end')
                if not res and self.compare('insert linestart', '>', 'insert'):
                    self.insert('insert', '\n')
                self.prompt(res)
                if auto_indent and lines:
                    # insert indentation similar to previous lines
                    indent = re.search(r'^( )*', lines[-1]).group()
                    line = lines[-1].strip()
                    if line and line[-1] == ':':
                        indent = indent + '    '
                    self.insert('insert', indent)
                self.see('end')
                if res:
                    self.mark_set('input', index)
                    self._console.resetbuffer()  # clear buffer since the whole command will be retrieved from the text widget
                elif lines:
                    self.history.append(lines)  # add commands to history
                    self._hist_item = len(self.history)
            out.close()
            err.close()
        else:
            self.insert('insert', '\n')
            self.prompt()


if __name__ == '__main__':
    root = tk.Tk()
    console = TextConsole(root)
    console.pack(fill='both', expand=True)
    root.mainloop()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.