Comment puis-je créer un petit shell Python de type IDLE dans Tkinter?

9

J'essaie de faire une chose contrôlée par une interface graphique Python Shell.

La seule chose est, je ne sais pas comment faire toute cette chose d'entrée / sortie. Je veux juste pouvoir taper une entrée, exécuter la commande python et donner la sortie de la commande python. Je sais que IDLE est fait dans Tkinter, donc il utilise les widgets?

C'est littéralement juste une chose "type d'entrée, afficher la sortie".

J'ai essayé de le rechercher, mais il semble que la plupart des résultats concernent la ligne de commande, ce qui n'est pas ce que je recherche. La seule autre question qui était exactement comme la mienne n'était pas non plus celle que j'avais en tête. J'ai également essayé de fouiller le code source d'IDLE mais je n'ai pas trouvé ce que je cherchais.

J'ai trouvé des réponses qui fonctionnent pour Linux mais je suis sur Windows 10 ...

J'ai besoin que le "shell" soit dans Tkinter car d'un côté de l'écran il y aura quelque chose d'autre qui est connecté aux sorties de commande.

Est-ce que quelqu'un connaît les widgets utilisés pour créer un shell Python très simple?

Eleeza l'assistant de personnage
la source
stackoverflow.com/questions/38977525/… peut être intéressant.
jasonharper
Cette question ressemble à un suivi d'une autre qui est supprimée, cela n'avait pas de sens pour moi sans le contexte de la question supprimée ... enfin, au moins je sais maintenant que c'est possible - et que je ne suis pas le un seul essaie de faire quelque chose ... encore
Eleeza the Character Wizard
1
Idle est écrit en python, en utilisant tkinter ... lire le code source .
Reblochon Masque

Réponses:

13

Shell Python simple / Terminal / Invite de commandes


  • ********************* C'est littéralement juste une chose " 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()

Le script python donné ci-dessus a la hiérarchie suivante comme indiqué;

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

Flux de travail:

Le flux de travail de base pour le code ci-dessus est donné comme suit;

  • def welcome_note():... Inclut l'étiquette qui s'affichera en dehors du corps du texte.

  • def get_text(self):...Effectue deux opérations; ** Obtenez le texte du corps du texte ** & ** Définissez la sortie dans la zone de saisie **.

  • def store_commands(self, command):... Utilisez pour stocker la variable dans un fichier.

  • def get_stored_commands(self):... Obtenez une variable stockée dans un fichier.

  • def check_if_file_empty():... Vérifiez la taille du fichier.

  • def run_commands(self, command):...Cette méthode agit comme un compilateur python qui prend les commandes, effectue le traitement et produit une sortie pour la commande donnée. Pour exécuter des commandes, je recommanderais d'utiliser subprocess-modulecar il fournit des fonctionnalités plus puissantes pour générer de nouveaux processus et récupérer leurs résultats; Courir des commandes de fenêtre à l' aide de python, il inclut diverses bibliothèques intégrées telles que;

    1. os ( en détail ), 2. sous-processus ( en détail ) etc.

    Pour vérifier ce qui est préférable d'utiliser, visitez la référence: le sous - processus-module est préférable à os-module .

  • def start_terminal(self):...Cette méthode implique simplement la fonctionnalité pour lancer la tkinterfenêtre de session et afficher la disposition de base pour la fenêtre d'entrée et de sortie.

    Vous pouvez encore modifier et optimiser ce code en fonction de vos besoins.


Workaroud:

Ce simple tkinter GUI based python shellexécuter des fonctionnalités simples comme invite de commande windows. Pour exécuter des commandes python directlydans l'invite de commande sans passer dans le terminal python, nous faisons simplement comme;

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

Son résultat serait simple comme;

Hey Eleeza!!!

De même, pour exécuter plusieurs lignes directement à la fois comme indiqué;

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

Sa sortie serait comme;

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

Donc, pour utiliser cela tkinter python shell;

  • Soit vous pouvez placer la commande en tant que;

    import platform
    value=platform.uname()
    print('Value:', value)
  • ou comme ça;

    import platform;value=platform.uname();
    print('Value:', value)
  • ou simplement une commande en ligne comme

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

Vous obtiendrez le même résultat.

Muhammad Usman
la source
1
Cette entrée / sortie est parfaite, merci beaucoup! Une seule chose cependant, si j'attribue une variable, exécute la commande, puis efface et essaie d'imprimer la variable, cela ne le fait pas, alors comment puis-je faire exécuter la commande dans le python réel (si ce n'est pas le cas) Faisant cela)?
Eleeza the Character Wizard
2
@Eleeza, tout d'abord votre question n'a pas impliqué ce type d'exigence. Comme c'est très basique input, output shell. Il agit comme basique python kernel. Il ne fonctionne que sur celui qui est placé dans le corps du texte . Je n'ai défini aucun tampon ou cache-mémoire pour contenir l'historique des variables. Laissez-moi d'abord vérifier cette exigence dans ce RALENTI!
Muhammad Usman
1
La chose que j'allais faire était un jeu où le monde entier est programmé dans le code principal, et le terminal est utilisé pour l'explorer et interagir avec lui. Je pensais qu'il s'agirait simplement d'exécuter le code avant d'afficher la sortie, mais je vais devoir vérifier cela.
Eleeza the Wizard Wizard
2
Expliqué en détail: je fais une sorte de «monde» dans le programme de jeu principal, où les gens, les lieux, etc., ce sont tous des objets en Python. Le joueur doit naviguer à travers le monde via le terminal GUI, en utilisant les commandes python, c'est un jeu sur l'apprentissage de Python à travers l'exploration. Le code est en fait modifié dans la vie réelle (mais il se réinitialise ensuite).
Eleeza the Wizard Wizard
8
@Eleeza, Voici le meilleur référentiel github qui peut vous donner la piste complète sur laquelle vous êtes; Visitez la référence: github.com/codecombat/codecombat . Voici quelques autres références que vous devez également consulter; github.com/replit/play , github.com/jatinmandav/Gaming-in-Python , github.com/PacktPublishing/-Learn-Python-Programming-with-Games , github.com/… , github.com/pyland/pyland , Voici également une autre référence github.com/CharlesPikachu/Games
Muhammad Usman
4

Il s'agit d'un simple shell principalement utilisé exec()pour exécuter les instructions python et subprocess.Popen()pour exécuter une commande externe:

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

Entrez simplement l'instruction python normale:

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

Ou entrez une commande shell:

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

Vous pouvez également utiliser la Uptouche pour rappeler la dernière commande.

Veuillez noter que si vous exécutez une commande système nécessitant une entrée utilisateur, le shell sera figé pendant 5 secondes (délai d'expiration utilisé dans communicate()).

Vous pouvez modifier la on_key()fonction selon vos besoins.

Nous vous rappelons également que l'utilisation exec()n'est pas une bonne pratique.

acw1668
la source
Pourriez-vous expliquer le code, s'il vous plaît? Je suis un peu débutant à tout cela ... et je ne sais pas si je comprends bien
Eleeza the Character Wizard
1
J'ai ajouté des commentaires dans mon code, j'espère que cela aide.
acw1668
3

J'avais implémenté un shell python en utilisant code.InteractiveConsolepour exécuter les commandes d'un projet. Ci-dessous est une version simplifiée, bien que toujours assez longue car j'avais écrit des liaisons pour des touches spéciales (comme Return, Tab ...) pour se comporter comme dans la console python. Il est possible d'ajouter plus de fonctionnalités telles que la saisie semi-automatique avec jedi et la mise en évidence de la syntaxe avec pygments.

L'idée principale est que j'utilise la push()méthode du code.InteractiveConsolepour exécuter les commandes. Cette méthode retourne Trues'il s'agit d'une commande partielle, par exemple def test(x):, et j'utilise cette rétroaction pour insérer une ...invite, sinon, la sortie est affichée et une nouvelle >>>invite est affichée. Je capture la sortie en utilisant contextlib.redirect_stdout.

Il y a aussi beaucoup de code impliquant des marques et comparant les index car j'empêche l'utilisateur d'insérer du texte dans des commandes précédemment exécutées. L'idée est que j'ai créé une marque «entrée» qui m'indique où se trouve le début de l'invite active et avec self.compare('insert', '<', 'input')je peux savoir quand l'utilisateur essaie d'insérer du texte au-dessus de l'invite active.

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
la source