Làm cách nào tôi có thể tạo một Python Shell nhỏ giống như IDLE trong Tkinter?


9

Tôi đang cố gắng tạo ra một thứ được điều khiển bởi GUI Python Shell.

Điều duy nhất là, tôi không biết làm thế nào để tạo ra toàn bộ đầu vào / đầu ra. Tôi chỉ muốn có thể gõ một đầu vào, thực hiện lệnh python và đưa ra đầu ra của lệnh python. Tôi biết rằng IDLE được tạo ra trong Tkinter, vì vậy nó sử dụng các vật dụng?

Đó thực sự chỉ là một thứ "kiểu nhập, hiển thị đầu ra".

Tôi đã thử tìm kiếm nó nhưng có vẻ như hầu hết các kết quả đều được thực hiện với dòng lệnh, đây không phải là thứ tôi đang tìm kiếm. Câu hỏi duy nhất giống hệt câu hỏi của tôi cũng không phải là điều tôi nghĩ. Tôi cũng đã thử sử dụng mã nguồn cho IDLE nhưng không thể tìm thấy những gì tôi đang tìm kiếm.

Tôi đã tìm thấy một số câu trả lời hoạt động cho Linux nhưng tôi đang dùng Windows 10 ...

Tôi cần "shell" để ở trong Tkinter vì ở một bên của màn hình sẽ là một thứ khác được kết nối với đầu ra lệnh.

Có ai biết các widget được sử dụng để tạo một vỏ Python rất đơn giản không?


stackoverflow.com/questions/38977525/ có thể được quan tâm.
jasonharper

Câu hỏi đó trông giống như một câu hỏi tiếp theo đã bị xóa, nó không có ý nghĩa với tôi mà không có ngữ cảnh từ câu hỏi đã xóa ... tốt, ít nhất tôi biết bây giờ là có thể - và tôi không phải là chỉ có một người cố gắng làm điều gì đó ... một lần nữa
Eleeza the Wizard Wizard

1
Idle được viết bằng python, sử dụng tkinter ... đọc mã nguồn .
Reblochon Masque

Câu trả lời:


13

Shell Python đơn giản / Terminal / Command-Prompt


  • ****** / TÌM HIỂU Nghĩa đen chỉ là một thứ " type input, show output". ****** / TÌM HIỂU

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

Kịch bản python đã cho ở trên có hệ thống phân cấp như đã cho;

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

Quy trình làm việc:

Quy trình làm việc cơ bản cho mã trên được đưa ra như sau;

  • def welcome_note():... Bao gồm Nhãn sẽ hiển thị bên ngoài thân văn bản.

  • def get_text(self):...Thực hiện hai thao tác; ** Nhận văn bản từ thân văn bản ** & ** Đặt đầu ra trong Hộp nhập **.

  • def store_commands(self, command):... Sử dụng để lưu trữ biến thành tập tin.

  • def get_stored_commands(self):... Nhận biến được lưu trữ trong tập tin.

  • def check_if_file_empty():... Kiểm tra kích thước của tập tin.

  • def run_commands(self, command):...Phương thức này hoạt động như trình biên dịch python nhận lệnh, xử lý và mang lại kết quả đầu ra cho lệnh đã cho. Để chạy các lệnh, tôi khuyên bạn nên sử dụng subprocess-modulevì nó cung cấp các phương tiện mạnh hơn để sinh ra các quy trình mới và lấy kết quả của chúng; Chạy các lệnh window bằng python bao gồm các thư viện dựng sẵn khác nhau như;

    1. os ( chi tiết ), 2. quy trình con ( chi tiết ), v.v.

    Để kiểm tra cái nào tốt hơn để sử dụng, hãy truy cập tài liệu tham khảo: mô đun con - mô đun thích hợp hơn mô đun os .

  • def start_terminal(self):... Phương pháp này chỉ đơn giản liên quan đến chức năng để bắt đầu tkinter cửa sổ phiên và hiển thị bố cục cơ bản cho cửa sổ đầu vào và đầu ra.

    Bạn có thể sửa đổi và tối ưu hóa mã này theo yêu cầu của bạn.


Workaroud:

Đơn giản này tkinter GUI based python shellthực hiện chức năng đơn giản như cửa sổ lệnh-nhắc. Để chạy các lệnh python directlytrong dấu nhắc lệnh mà không di chuyển vào terminal python, chúng ta thực hiện đơn giản như;

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

Kết quả của nó sẽ đơn giản như;

Hey Eleeza!!!

Tương tự, để chạy nhiều hơn một dòng trực tiếp tại một thời điểm như đã cho;

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

Sản lượng của nó sẽ là;

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

Vì vậy, để sử dụng này tkinter python shell;

  • Hoặc bạn có thể đặt lệnh như;

    import platform
    value=platform.uname()
    print('Value:', value)
  • hoặc thích cách này;

    import platform;value=platform.uname();
    print('Value:', value)
  • hoặc đơn giản là lệnh nội tuyến như

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

Bạn sẽ nhận được kết quả tương tự.


1
Đầu vào / đầu ra này là hoàn hảo, cảm ơn bạn rất nhiều! Mặc dù chỉ có một điều, nếu tôi gán một biến, chạy lệnh và sau đó xóa và thử in biến, nó sẽ không làm điều đó, vậy làm thế nào tôi có thể chạy lệnh trong con trăn ngoài đời thực (nếu không làm vậy)
Eleeza the Wizard Wizard

2
@Eleeza, trước hết câu hỏi của bạn không liên quan đến loại yêu cầu như vậy. Vì nó rất cơ bản input, output vỏ. Nó hoạt động như cơ bản python kernel. Nó chỉ hoạt động trên đó được đặt trong cơ thể văn bản . Tôi chưa đặt bất kỳ bộ đệm hoặc bộ nhớ cache để giữ lịch sử biến. Hãy để tôi kiểm tra trước cho yêu cầu này trong IDLE này!
Muhammad Usman

1
Thứ tôi sẽ làm là một trò chơi trong đó cả thế giới được lập trình thành mã chính, và thiết bị đầu cuối được sử dụng để khám phá nó và tương tác với nó. Tôi nghĩ rằng đó chỉ là một trường hợp thực thi mã trước khi hiển thị đầu ra nhưng tôi sẽ phải kiểm tra điều đó.
Eleeza the Wizard Wizard

2
Giải thích chi tiết: Tôi đang tạo ra một "thế giới" trong chương trình trò chơi chính, nơi con người, địa điểm, v.v., tất cả đều là đối tượng trong Python. Người chơi phải điều hướng khắp thế giới thông qua thiết bị đầu cuối GUI, sử dụng các lệnh python, đây là một trò chơi về việc học Python thông qua khám phá. Mã này thực sự được sửa đổi trong cuộc sống thực (nhưng nó sẽ thiết lập lại sau đó).
Eleeza the Wizard Wizard

8
@Eleeza, Đây là kho lưu trữ github tốt nhất có thể cung cấp cho bạn bản nhạc hoàn chỉnh mà bạn đang có; Truy cập tài liệu tham khảo: github.com/codecombat/codecombat . Dưới đây là một số tài liệu tham khảo khác bạn phải xem xét cả những điều này; github.com/replit/play , github.com/jatinmandav/Gaming-in-Python , github.com/PacktPublishing/-Learn-Python-Programming-with-Games , github.com/... , github.com/pyland/pyland , Đây cũng là một tài liệu tham khảo khác github.com/CharlesPikachu/Games
Muhammad Usman

4

Đây là một shell đơn giản chủ yếu sử dụng exec()để thực thi các câu lệnh python và subprocess.Popen()để thực thi lệnh bên ngoài:

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

Chỉ cần nhập câu lệnh python bình thường:

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

Hoặc nhập lệnh shell:

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

Bạn cũng có thể dùng Up phím để gọi lại lệnh cuối cùng.

Xin lưu ý rằng nếu bạn thực thi lệnh hệ thống yêu cầu đầu vào của người dùng, trình bao sẽ bị đóng băng trong 5 giây (khoảng thời gian chờ được sử dụng trong communicate() ).

Bạn có thể sửa đổi on_key()chức năng cho phù hợp với nhu cầu của bạn.

Cũng xin lưu ý rằng sử dụng exec()không phải là một thực hành tốt.


Bạn có thể giải thích mã thông qua không? Tôi là một người mới bắt đầu tất cả những điều này ... và tôi không biết liệu tôi có hiểu đúng không
Eleeza the Wizard Wizard

1
Tôi đã thêm ý kiến ​​trong mã của tôi, hy vọng nó sẽ giúp.
acw1668

3

Tôi đã thực hiện python shell bằng cách sử dụng code.InteractiveConsoleđể thực hiện các lệnh cho một dự án. Dưới đây là một phiên bản đơn giản hóa, mặc dù vẫn còn khá dài vì tôi đã viết các ràng buộc cho các phím đặc biệt (như Return, Tab ...) để hoạt động như trong bảng điều khiển python. Có thể thêm nhiều tính năng hơn như tự động hoàn thành với jedi và cú pháp nâng cao cú pháp với pygments.

Ý tưởng chính là tôi sử dụng push()phương thức của lệnh code.InteractiveConsoleđể thực thi các lệnh. Phương thức này trả về Truenếu đó là lệnh một phần, ví dụ def test(x):, và tôi sử dụng phản hồi này để chèn ...lời nhắc, nếu không, đầu ra được hiển thị và mới>>> dấu nhắc được hiển thị. Tôi chụp đầu ra bằng cách sử dụng contextlib.redirect_stdout.

Ngoài ra, có rất nhiều mã liên quan đến các dấu và so sánh các chỉ mục vì tôi ngăn người dùng chèn văn bản bên trong các lệnh đã thực hiện trước đó. Ý tưởng là tôi đã tạo một dấu 'đầu vào' cho tôi biết nơi bắt đầu của dấu nhắc hoạt động và self.compare('insert', '<', 'input')tôi có thể biết khi nào người dùng đang cố gắng chèn văn bản lên trên dấu nhắc hoạt động.

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.