Bagaimana saya bisa membuat Python Shell IDLE-like kecil di Tkinter?

9

Saya mencoba membuat sesuatu yang dikendalikan oleh Python Shell GUI.

Satu-satunya hal adalah, saya tidak tahu bagaimana membuat hal input / output keseluruhan. Saya hanya ingin dapat mengetikkan input, menjalankan perintah python dan memberikan output dari perintah python. Saya tahu bahwa IDLE dibuat di Tkinter, jadi itu menggunakan widget?

Ini benar-benar hanya "ketik input, tampilkan keluaran" hal.

Saya sudah mencoba mencarinya tetapi sepertinya sebagian besar hasilnya berkaitan dengan baris perintah, yang bukan yang saya cari. Satu-satunya pertanyaan lain yang persis seperti pertanyaan saya bukanlah yang ada dalam pikiran saya juga. Saya juga mencoba menempatkan kode sumber untuk IDLE tetapi tidak dapat menemukan apa yang saya cari.

Saya telah menemukan beberapa jawaban yang berfungsi untuk Linux tetapi saya menggunakan Windows 10 ...

Saya perlu "shell" berada di Tkinter karena di satu sisi layar akan ada sesuatu yang lain yang terhubung ke output perintah.

Adakah yang tahu widget yang digunakan untuk membuat shell Python yang sangat sederhana?

Eleeza the Character Wizard
sumber
stackoverflow.com/questions/38977525/… mungkin menarik.
jasonharper
Pertanyaan itu seperti tindak lanjut dari pertanyaan lain yang dihapus, sepertinya tidak masuk akal bagi saya tanpa konteks dari pertanyaan yang dihapus ... baik, setidaknya saya tahu sekarang ini mungkin - dan bahwa saya bukan yang hanya satu yang mencoba melakukan sesuatu ... lagi
Eleeza the Character Wizard
1
Idle ditulis dengan python, menggunakan tkinter ... baca kode sumbernya .
Reblochon Masque

Jawaban:

13

Simple Python Shell / Terminal / Command-Prompt


  • ********************* Secara harfiah hanya " type input, show output" hal. ************************

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()

Skrip python yang diberikan di atas memiliki hierarki berikut seperti yang diberikan;

    |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__':...

Alur kerja:

Alur kerja dasar untuk kode di atas diberikan sebagai berikut;

  • def welcome_note():... Termasuk Label yang akan ditampilkan di luar badan teks.

  • def get_text(self):...Melakukan dua operasi; ** Dapatkan teks dari badan teks ** & ** Set Output di Kotak Masuk **.

  • def store_commands(self, command):... Gunakan untuk menyimpan variabel ke dalam file.

  • def get_stored_commands(self):... Dapatkan variabel disimpan dalam file.

  • def check_if_file_empty():... Periksa Ukuran file.

  • def run_commands(self, command):...Metode ini bertindak sebagai kompiler python yang mengambil perintah, melakukan pemrosesan dan menghasilkan output untuk perintah yang diberikan. Untuk menjalankan perintah, saya akan merekomendasikan untuk menggunakan subprocess-modulekarena menyediakan fasilitas yang lebih kuat untuk memunculkan proses baru dan mengambil hasil mereka; Untuk menjalankan perintah-jendela menggunakan python termasuk berbagai pustaka builtin seperti;

    1. os ( detail ), 2. subproses ( detail ) dll.

    Untuk checkout yang lebih baik digunakan, kunjungi referensi: subprocess- module lebih disukai daripada os-module .

  • def start_terminal(self):...Metode ini hanya melibatkan fungsi untuk memulai tkinterjendela sesi dan menunjukkan tata letak dasar untuk jendela input dan output.

    Anda dapat lebih lanjut memodifikasi dan mengoptimalkan kode ini sesuai dengan kebutuhan Anda.


Workaroud:

Sederhana ini tkinter GUI based python shellmelakukan fungsi sederhana sebagai windows-command-prompt. Untuk menjalankan perintah python directlydi command-prompt tanpa berpindah ke terminal python, kami melakukannya sesederhana;

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

Hasilnya akan sesederhana;

Hey Eleeza!!!

Demikian pula, untuk menjalankan lebih dari satu baris secara langsung pada waktu yang diberikan;

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

Hasilnya akan seperti;

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')

Jadi gunakan ini tkinter python shell;

  • Anda bisa menempatkan perintah sebagai;

    import platform
    value=platform.uname()
    print('Value:', value)
  • atau seperti ini;

    import platform;value=platform.uname();
    print('Value:', value)
  • atau cukup sebaris perintah sebagai

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

Anda akan mendapatkan hasil yang sama.

Muhammad Usman
sumber
1
Input / output ini sempurna, terima kasih banyak! Hanya satu hal, jika saya menetapkan variabel, jalankan perintah dan kemudian hapus dan coba untuk mencetak variabel, itu tidak melakukan itu, jadi bagaimana saya mendapatkan perintah untuk berjalan di python kehidupan nyata (jika tidak melakukan itu)?
Eleeza the Character Wizard
2
@ Eleeza, pertama-tama pertanyaan Anda belum melibatkan jenis persyaratan seperti itu. Karena sangat dasar input, output shell. Kerjanya seperti dasar python kernel. Ini hanya beroperasi pada yang ditempatkan di badan teks . Saya belum mengatur buffer atau cache memori untuk menyimpan riwayat variabel. Biarkan saya periksa dulu persyaratan ini di IDLE ini!
Muhammad Usman
1
Hal yang akan saya buat adalah permainan di mana seluruh dunia diprogram ke dalam kode utama, dan terminal digunakan untuk menjelajahinya dan berinteraksi dengannya. Saya pikir itu hanya akan menjadi kasus mengeksekusi kode sebelum menampilkan output tetapi saya harus memeriksanya.
Eleeza the Character Wizard
2
Dijelaskan secara terperinci: Saya membuat semacam "dunia" dalam program permainan utama, di mana orang-orang, tempat, dll, mereka semua adalah objek dengan Python. Pemain harus menavigasi melalui dunia melalui terminal GUI, menggunakan perintah python, ini adalah permainan tentang belajar Python melalui penjelajahan. Kode ini sebenarnya dimodifikasi dalam kehidupan nyata (tetapi akan diatur ulang setelahnya).
Eleeza the Character Wizard
8
@Eleeza, Ini adalah repositori github terbaik yang dapat memberi Anda lagu lengkap tempat Anda berada; Kunjungi referensi: github.com/codecombat/codecombat . Berikut ini beberapa referensi lain yang harus Anda perhatikan juga; github.com/replit/play , github.com/jatinmandav/Gaming-in-Python , github.com/PacktPublishing/-Pelajari-Python-Programming-with-Games , github.com/... , github.com/pyland/pyland , Ini adalah referensi lain juga github.com/CharlesPikachu/Games
Muhammad Usman
4

Ini adalah shell sederhana yang terutama digunakan exec()untuk mengeksekusi pernyataan python dan subprocess.Popen()untuk mengeksekusi perintah eksternal:

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()

Cukup masukkan pernyataan python normal:

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

Atau masukkan perintah shell:

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

Anda juga dapat menggunakan Upkunci untuk mengingat perintah terakhir.

Harap dicatat bahwa jika Anda menjalankan perintah sistem yang membutuhkan input pengguna, shell akan membeku selama 5 detik (periode waktu habis digunakan communicate()).

Anda dapat memodifikasi on_key()fungsi sesuai dengan kebutuhan Anda.

Harap diingatkan juga bahwa menggunakan exec()bukanlah praktik yang baik.

acw1668
sumber
Bisakah Anda menjelaskan kode melalui, tolong? Saya sedikit pemula dalam semua ini ... dan saya tidak tahu apakah saya mengerti dengan benar
Eleeza the Character Wizard
1
Saya telah menambahkan komentar dalam kode saya, semoga membantu.
acw1668
3

Saya telah mengimplementasikan shell python menggunakan code.InteractiveConsoleuntuk mengeksekusi perintah untuk proyek. Di bawah ini adalah versi yang disederhanakan, meskipun masih cukup lama karena saya telah menulis binding untuk kunci khusus (seperti Return, Tab ...) untuk berperilaku seperti di konsol python. Dimungkinkan untuk menambahkan lebih banyak fitur seperti pelengkapan otomatis dengan jedi dan sorotan sintaksis dengan pigmen.

Ide utamanya adalah saya menggunakan push()metode code.InteractiveConsoleuntuk mengeksekusi perintah. Metode ini kembali Truejika itu adalah perintah parsial, misalnya def test(x):, dan saya menggunakan umpan balik ini untuk memasukkan ...prompt, jika tidak, output ditampilkan dan >>>prompt baru ditampilkan. Saya menangkap output menggunakan contextlib.redirect_stdout.

Juga ada banyak kode yang melibatkan tanda dan membandingkan indeks karena saya mencegah pengguna memasukkan teks ke dalam perintah yang sebelumnya dieksekusi. Idenya adalah bahwa saya membuat tanda 'input' yang memberitahu saya di mana awal prompt aktif dan dengan self.compare('insert', '<', 'input')saya bisa tahu kapan pengguna mencoba memasukkan teks di atas prompt aktif.

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()
j_4321
sumber