Wie kann ich in Tkinter eine kleine IDLE-ähnliche Python-Shell erstellen?

9

Ich versuche, etwas zu erstellen, das von einer Python-Shell-GUI gesteuert wird.

Das einzige ist, ich weiß nicht, wie ich diese ganze Eingabe / Ausgabe-Sache machen soll. Ich möchte nur in der Lage sein, eine Eingabe einzugeben, den Python-Befehl auszuführen und die Ausgabe des Python-Befehls anzugeben. Ich weiß, dass IDLE in Tkinter erstellt wird, also verwendet es die Widgets?

Es ist buchstäblich nur eine Sache "Eingabe eingeben, Ausgabe anzeigen".

Ich habe versucht, es zu durchsuchen, aber es scheint, dass die meisten Ergebnisse mit der Befehlszeile zu tun haben, was nicht das ist, wonach ich suche. Die einzige andere Frage, die genau meiner entsprach, war auch nicht meine. Ich habe auch versucht, den Quellcode für IDLE zu finden, konnte aber nicht finden, wonach ich gesucht habe.

Ich habe einige Antworten gefunden, die für Linux funktionieren, aber ich bin unter Windows 10 ...

Ich brauche die "Shell", um in Tkinter zu sein, weil auf einer Seite des Bildschirms etwas anderes ist, das mit den Befehlsausgängen verbunden ist.

Kennt jemand die Widgets, mit denen eine sehr einfache Python-Shell erstellt wurde?

Eleeza der Charakter-Assistent
quelle
stackoverflow.com/questions/38977525/… könnte von Interesse sein.
Jasonharper
Diese Frage sieht aus wie eine Fortsetzung einer anderen, die gelöscht wurde. Ohne den Kontext der gelöschten Frage ergab sie für mich keinen Sinn. Nun, zumindest weiß ich jetzt, dass es möglich ist - und dass ich nicht der bin nur einer versucht etwas zu tun ... wieder
Eleeza der Charakter-Zauberer
1
Idle wird in Python mit tkinter geschrieben ... lesen Sie den Quellcode .
Reblochon Masque

Antworten:

13

Einfache Python-Shell / Terminal / Eingabeaufforderung


  • ********************* Es ist buchstäblich nur ein " type input, show output" Ding. ************************

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

Das oben angegebene Python-Skript hat die folgende Hierarchie:

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

Arbeitsablauf:

Der grundlegende Workflow für den obigen Code ist wie folgt angegeben:

  • def welcome_note():... Enthält die Beschriftung, die außerhalb des Textkörpers angezeigt wird.

  • def get_text(self):...Führt zwei Operationen aus; ** Text aus dem Textkörper abrufen ** & ** Ausgabe im Eingabefeld festlegen **.

  • def store_commands(self, command):... Verwenden Sie diese Option, um eine Variable in einer Datei zu speichern.

  • def get_stored_commands(self):... Variable in Datei speichern lassen.

  • def check_if_file_empty():... Größe der Datei prüfen.

  • def run_commands(self, command):...Diese Methode fungiert als Python-Compiler , der Befehle entgegennimmt, verarbeitet und für den angegebenen Befehl eine Ausgabe liefert. Um Befehle auszuführen, würde ich empfehlen, sie zu verwenden, subprocess-moduleda sie leistungsfähigere Funktionen zum Laichen neuer Prozesse und zum Abrufen ihrer Ergebnisse bieten. Das Ausführen von Fensterbefehlen mit Python umfasst verschiedene integrierte Bibliotheken wie:

    1. OS ( im Detail ), 2. Unterprozess ( im Detail ) usw.

    Um herauszufinden, was besser zu verwenden ist, besuchen Sie die Referenz: Unterprozessmodul ist dem OS-Modul vorzuziehen .

  • def start_terminal(self):...Diese Methode beinhaltet lediglich die Funktionalität zum Initiieren des tkinterSitzungsfensters und zum Anzeigen des Grundlayouts für das Eingabe- und Ausgabefenster.

    Sie können diesen Code entsprechend Ihren Anforderungen weiter modifizieren und optimieren.


Workaroud:

Diese einfache tkinter GUI based python shelleinfache Funktion als Windows-Eingabeaufforderung ausführen. Um Python-Befehle directlyin der Eingabeaufforderung auszuführen, ohne in das Python-Terminal zu wechseln, gehen wir wie folgt vor:

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

Das Ergebnis wäre einfach wie:

Hey Eleeza!!!

In ähnlicher Weise können mehrere Zeilen wie angegeben direkt gleichzeitig ausgeführt werden.

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

Seine Ausgabe wäre wie;

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

Also, um dies zu nutzen tkinter python shell;

  • Entweder können Sie den Befehl als platzieren;

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

    import platform;value=platform.uname();
    print('Value:', value)
  • oder einfach Inline-Befehl als

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

Sie erhalten das gleiche Ergebnis.

Muhammad Usman
quelle
1
Diese Eingabe / Ausgabe ist perfekt, vielen Dank! Nur eine Sache: Wenn ich eine Variable zuweise, den Befehl ausführe und dann lösche und versuche, die Variable zu drucken, funktioniert dies nicht. Wie würde ich den Befehl dazu bringen, in der realen Python ausgeführt zu werden (wenn dies nicht der Fall ist)? das machen)?
Eleeza der Charakter-Zauberer
2
@Eleeza, zuallererst hat Ihre Frage keine solche Anforderung gestellt. Da es sehr einfach ist input, output Shell. Es wirkt wie einfach python kernel. Es funktioniert nur, was im Textkörper platziert ist . Ich habe keinen Puffer oder Speicher-Cache für den Variablenverlauf festgelegt. Lassen Sie mich zuerst in diesem LEERLAUF nach dieser Anforderung suchen!
Muhammad Usman
1
Das, was ich machen wollte, war ein Spiel, bei dem die ganze Welt in den Hauptcode programmiert ist und das Terminal verwendet wird, um ihn zu erkunden und mit ihm zu interagieren. Ich dachte, es wäre nur ein Fall, in dem der Code ausgeführt wird, bevor die Ausgabe angezeigt wird, aber ich muss das überprüfen.
Eleeza der Charakter-Zauberer
2
Im Detail erklärt: Ich mache eine Art "Welt" im Hauptspielprogramm, in der die Leute, die Orte usw. alle Objekte in Python sind. Der Spieler muss durch das GUI-Terminal durch die Welt navigieren, die Python-Befehle verwenden, es ist ein Spiel über das Erlernen von Python durch Erkunden. Der Code wird im wirklichen Leben tatsächlich geändert (aber danach zurückgesetzt).
Eleeza der Charakter-Zauberer
8
@Eleeza, hier ist das beste Github-Repository , das dir den vollständigen Track geben kann, auf dem du bist; Besuchen Sie die Referenz: github.com/codecombat/codecombat . Hier sind einige andere Referenzen, die Sie sich ebenfalls ansehen müssen. github.com/replit/play , github.com/jatinmandav/Gaming-in-Python , github.com/PacktPublishing/-Learn-Python-Programming-with-Games , github.com/… , github.com/pyland/pyland , Hier ist noch eine Referenz github.com/CharlesPikachu/Games
Muhammad Usman
4

Dies ist eine einfache Shell, die hauptsächlich exec()zum Ausführen der Python-Anweisungen und subprocess.Popen()zum Ausführen eines externen Befehls verwendet wird:

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

Geben Sie einfach die normale Python-Anweisung ein:

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

Oder geben Sie einen Shell-Befehl ein:

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

Sie können auch die UpTaste verwenden, um den letzten Befehl abzurufen.

Beachten Sie, dass die Shell 5 Sekunden lang eingefroren wird, wenn Sie einen Systembefehl ausführen, für den Benutzereingaben erforderlich sind (Zeitüberschreitung in communicate()).

Sie können die on_key()Funktion an Ihre Bedürfnisse anpassen.

Bitte beachten Sie auch, dass die Verwendung exec()keine gute Praxis ist.

acw1668
quelle
Könnten Sie bitte den Code durch erklären, bitte? Ich bin ein bisschen ein Anfänger in all dem ... und ich weiß nicht, ob ich richtig verstehe
Eleeza der Charakter-Zauberer
1
Ich habe Kommentare in meinen Code eingefügt, hoffe es hilft.
acw1668
3

Ich hatte die Python-Shell implementiert code.InteractiveConsole, um die Befehle für ein Projekt auszuführen. Unten ist eine vereinfachte Version, obwohl sie noch ziemlich lang ist, da ich Bindungen für spezielle Schlüssel (wie Return, Tab ...) geschrieben habe, die sich wie in der Python-Konsole verhalten sollen. Es ist möglich, weitere Funktionen wie die automatische Vervollständigung mit jedi und die Syntax-Hervorhebung mit pylements hinzuzufügen.

Die Hauptidee ist, dass ich die push()Methode von verwende code.InteractiveConsole, um die Befehle auszuführen. Diese Methode wird zurückgegeben, Truewenn es sich beispielsweise um einen Teilbefehl handelt def test(x):, und ich verwende dieses Feedback, um eine ...Eingabeaufforderung einzufügen . Andernfalls wird die Ausgabe angezeigt und eine neue >>>Eingabeaufforderung angezeigt. Ich erfasse die Ausgabe mit contextlib.redirect_stdout.

Es gibt auch viel Code, der Markierungen und das Vergleichen von Indizes beinhaltet, da ich den Benutzer daran hindere, Text in zuvor ausgeführte Befehle einzufügen. Die Idee ist, dass ich eine Markierung 'Eingabe' erstellt habe, die mir sagt, wo der Beginn der aktiven Eingabeaufforderung liegt und mit der self.compare('insert', '<', 'input')ich wissen kann, wann der Benutzer versucht, Text über der aktiven Eingabeaufforderung einzufügen.

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
quelle