টিকিনটারে আমি কীভাবে একটি ছোট আইডিএল-জাতীয় পাইথন শেল তৈরি করতে পারি?


9

আমি পাইথন শেল জিইআই দ্বারা নিয়ন্ত্রিত কোনও জিনিস করার চেষ্টা করছি।

একমাত্র জিনিস, আমি কীভাবে পুরো ইনপুট / আউটপুট জিনিসটি বানাতে পারি তা জানি না। আমি কেবল একটি ইনপুট টাইপ করতে, পাইথন কমান্ডটি কার্যকর করতে এবং পাইথন কমান্ডের আউটপুট দিতে সক্ষম হতে চাই। আমি জানি যে আইডিএলটি টিনেটারে তৈরি, তাই এটি উইজেটগুলি ব্যবহার করে?

এটি আক্ষরিক অর্থে কেবল একটি "টাইপ ইনপুট, প্রদর্শন আউটপুট" জিনিস।

আমি এটি সন্ধান করার চেষ্টা করেছি তবে মনে হচ্ছে বেশিরভাগ ফলাফল কমান্ড লাইনের সাথে করা, যা আমি খুঁজছি তা নয়। অন্য যে প্রশ্নটি হুবহু আমার মতো ছিল তাও আমার মনে ছিল না। আমি IDLE এর জন্য উত্স কোডটিও সন্ধান করার চেষ্টা করেছি তবে আমি যা খুঁজছিলাম তা খুঁজে পেলাম না।

আমি কিছু উত্তর পেয়েছি যা লিনাক্সের জন্য কাজ করে তবে আমি উইন্ডোজ 10 এ ...

টিন্টিটারে থাকতে আমার "শেল" দরকার কারণ স্ক্রিনের একপাশে অন্য কিছু হবে যা কমান্ড আউটপুটগুলির সাথে সংযুক্ত।

খুব সহজেই পাইথন শেল তৈরিতে ব্যবহৃত উইজেটগুলি কি কেউ জানেন?


stackoverflow.com/questions/38977525/… আগ্রহের বিষয় হতে পারে।
জেসনহার্পার

এই প্রশ্নটি অন্যটির ফলোআপের মতো দেখাচ্ছে যা মুছে ফেলা হয়েছে, এটি মুছে ফেলা প্রশ্ন থেকে প্রসঙ্গ ছাড়া আমার কাছে বোধগম্য হয়নি ... ভাল, কমপক্ষে আমি জানি এখন এটি সম্ভব - এবং আমি তা নই কেবল একজনই কিছু করার চেষ্টা করছেন ... আবার
এলিজা চরিত্র উইজার্ড

1
অলস টিকিনটার ব্যবহার করে অজগরটিতে লেখা হয় ... উত্স কোডটি পড়ুন
রেব্লাচন মাস্ক

উত্তর:


13

সাধারণ পাইথন শেল / টার্মিনাল / কমান্ড-প্রম্পট


  • ********************* এটি আক্ষরিকভাবে কেবল "" type input, show output"জিনিস। ************************

import os
from tkinter import *
from subprocess import *


class PythonShell:

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

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

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

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

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

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

    def store_commands(self, command=None):

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

        except Exception as e:
            print(e)

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

        except Exception as e:
            print(e)

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

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

    def run_commands(self, command):
        """

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

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

        try:
            if new_line_char != -1:

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

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

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

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

                elif double_quote != -1:

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

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

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

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

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

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

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

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

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

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

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

        return value

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

        terminal.welcome_note()

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

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

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

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

        self.master.mainloop()


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

উপরের প্রদত্ত পাইথন লিপিটি প্রদত্ত নিম্নোক্ত শ্রেণিবিন্যাসের নীচে রয়েছে;

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

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

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

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

কর্মধারা:

উপরের কোডের জন্য প্রাথমিক কর্মপ্রবাহটি নীচে দেওয়া হয়েছে;

  • def welcome_note():... পাঠ্য শৈলীর বাইরে প্রদর্শিত হবে এমন লেবেল অন্তর্ভুক্ত।

  • def get_text(self):...দুটি অপারেশন করে; ** পাঠ্য সংস্থা থেকে পাঠ্য পান ** এবং ** প্রবেশ বাক্সে আউটপুট সেট করুন **।

  • def store_commands(self, command):... ফাইলের মধ্যে ভেরিয়েবল সঞ্চয় করতে ব্যবহার করুন।

  • def get_stored_commands(self):... ফাইলের মধ্যে ভেরিয়েবল সংরক্ষণ করুন।

  • def check_if_file_empty():... ফাইলের আকার পরীক্ষা করুন।

  • def run_commands(self, command):...এই পদ্ধতিটি পাইথন সংকলক হিসাবে কাজ করে যা প্রদত্ত কমান্ডের জন্য কমান্ড নেয়, প্রসেসিং করে এবং আউটপুট দেয়। কমান্ডগুলি চালানোর জন্য, আমি ব্যবহারের পরামর্শ দেব subprocess-moduleকারণ এটি নতুন প্রক্রিয়া তৈরির এবং তাদের ফলাফলগুলি পুনরুদ্ধারের জন্য আরও শক্তিশালী সুবিধা সরবরাহ করে; পাইথন ব্যবহার করে উইন্ডো-কমান্ড চালানোর জন্য বিভিন্ন বিল্টিন লাইব্রেরি অন্তর্ভুক্ত রয়েছে;

    1. ওএস ( বিস্তারিত ), 2. সাবপ্রসেস ( বিস্তারিত ) ইত্যাদি

    কোনটি ব্যবহার করা ভাল তা চেকআউট করার জন্য, রেফারেন্সটি দেখুন: উপ-প্রসেস-মডিউলটি ওএস-মডিউলটির চেয়ে পছন্দনীয়

  • def start_terminal(self):...এই পদ্ধতিতে tkinterসেশন উইন্ডো আরম্ভ এবং ইনপুট এবং আউটপুট উইন্ডোর জন্য প্রাথমিক বিন্যাস প্রদর্শন করার জন্য কার্যকারিতা জড়িত ।

    আপনার প্রয়োজন অনুসারে আপনি এই কোডটি আরও সংশোধন ও অনুকূলিত করতে পারেন।


Workaroud:

এই সাধারণ tkinter GUI based python shellউইন্ডো-কমান্ড-প্রম্পট হিসাবে সাধারণ কার্যকারিতা সম্পাদন করে। directlyপাইথন টার্মিনালে না গিয়ে কমান্ড-প্রম্পটে পাইথন কমান্ডগুলি চালাতে , আমরা সহজ হিসাবে করি;

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

এর ফলাফল হিসাবে সহজ হবে;

Hey Eleeza!!!

একইভাবে, প্রদত্ত হিসাবে সরাসরি এক সময়ে একাধিক লাইন চালানো;

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

এর আউটপুট যেমন হবে;

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

সুতরাং এটি ব্যবহার করতে tkinter python shell;

  • হয় আপনি হিসাবে কমান্ড স্থাপন করতে পারেন;

    import platform
    value=platform.uname()
    print('Value:', value)
  • বা এইভাবে পছন্দ;

    import platform;value=platform.uname();
    print('Value:', value)
  • বা কেবল ইনলাইন কমান্ড হিসাবে

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

আপনি একই ফলাফল পাবেন।


1
এই ইনপুট / আউটপুট নিখুঁত, আপনাকে অনেক ধন্যবাদ! একটি মাত্র যদিও আমি যদি একটি ভেরিয়েবল বরাদ্দ করি, কমান্ডটি চালান এবং তারপরে সাফ করুন এবং ভেরিয়েবলটি মুদ্রণের চেষ্টা করুন, এটি তা করে না, সুতরাং আমি কীভাবে বাস্তব জীবনের অজগরটি চালানোর আদেশ পাব (যদি তা না হয়) এটা করে)?
এলিজা চরিত্র উইজার্ড

2
@ এলিজা, আপনার প্রশ্নের প্রথমত এই জাতীয় প্রয়োজনের সাথে জড়িত হয়নি। এটি খুব বেসিক হিসাবে input, output শেল। এটি বেসিকের মতো কাজ করে python kernelএটি কেবল পাঠ্য-শৃঙ্খলে রেখে যার উপর কাজ করে । পরিবর্তনশীল ইতিহাস ধরে রাখতে আমি কোনও বাফার বা মেমরি-ক্যাশে সেট করি নি । এই আইডলিতে আমাকে এই প্রয়োজনীয়তার জন্য প্রথমে পরীক্ষা করতে দিন!
মুহাম্মদ উসমান

1
আমি যে জিনিসটি তৈরি করতে যাচ্ছিলাম তা হ'ল একটি গেম যেখানে পুরো বিশ্বটিকে মূল কোড হিসাবে প্রোগ্রাম করা হয়েছিল এবং টার্মিনালটি এটি অন্বেষণ করতে এবং এর সাথে যোগাযোগ করার জন্য ব্যবহৃত হয়। আমি ভেবেছিলাম আউটপুট দেখানোর আগে কোডটি কার্যকর করার ক্ষেত্রে এটি হবে তবে আমাকে এটি যাচাই করতে হবে।
এলিজা চরিত্র উইজার্ড

2
বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে: আমি মূল গেম প্রোগ্রামে এক ধরণের "ওয়ার্ল্ড" তৈরি করছি, যেখানে লোকেরা, জায়গাগুলি ইত্যাদি, তারা পাইথনের সমস্ত বস্তু। খেলোয়াড়কে জিইউআই টার্মিনালের মাধ্যমে বিশ্বজুড়ে চলাচল করতে হবে, অজগর কমান্ড ব্যবহার করে এটি অন্বেষণের মাধ্যমে পাইথন শেখার একটি খেলা। কোডটি আসলে বাস্তব জীবনে পরিবর্তিত হয় (তবে এটি পরে পুনরায় সেট হয়)।
এলিজা ক্যারেক্টার উইজার্ড

8
@ এলিজা, এখানে সেরা গিথুব সংগ্রহস্থল যা আপনাকে যেখানে পুরো ট্র্যাক দিতে পারে তা দিতে পারে; রেফারেন্স দেখুন: github.com/codecombat/codecombat । এখানে আরও কয়েকটি উল্লেখ আপনাকে অবশ্যই এগুলি দেখতে হবে; github.com/replit/play , github.com/jatinmandav/Gaming-in-Python , github.com/PacktPublishing/-Learn-Python-Programming-with-Games , github.com/... , github.com/pyland/pyland , এখানে আরও একটি উল্লেখ রয়েছে github.com/CharlesPikachu/ গেমস
মুহাম্মদ উসমান

4

এটি একটি সাধারণ শেল যা মূলত exec()পাইথন স্টেটমেন্টগুলি কার্যকর করতে এবং subprocess.Popen()বাহ্যিক কমান্ড কার্যকর করতে ব্যবহার করে :

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

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

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

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

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

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

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

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

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

root.mainloop()

কেবলমাত্র পাইথন স্টেটমেন্টটি ইনপুট করুন:

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

অথবা একটি শেল কমান্ড ইনপুট করুন:

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

আপনি Upশেষ কমান্ডটি পুনরুদ্ধার করতে কী ব্যবহার করতে পারেন ।

দয়া করে নোট করুন যে আপনি যদি ইউজার ইনপুট প্রয়োজন এমন একটি সিস্টেম কমান্ড কার্যকর করেন তবে শেলটি 5 সেকেন্ডের জন্য স্থায়ী হয়ে যাবে (সময়সীমা নির্ধারিত সময়কালে ব্যবহৃত হবে communicate())।

on_key()আপনার প্রয়োজন অনুসারে আপনি ফাংশনটি সংশোধন করতে পারেন।

দয়া করে মনে করিয়ে দিন যে ব্যবহার exec()করা ভাল অভ্যাস নয়।


আপনি সম্ভবত কোড মাধ্যমে ব্যাখ্যা করতে পারেন, দয়া করে? আমি এই সমস্ত
কিছুর

1
আমি আমার কোডে মন্তব্য যুক্ত করেছি, আশা করি এটি সাহায্য করবে।
acw1668

3

আমি পাইথন শেল ব্যবহার করে প্রয়োগ করেছি code.InteractiveConsole একটি প্রকল্পের কমান্ডগুলি কার্যকর করতে । নীচে একটি সরলীকৃত সংস্করণ দেওয়া আছে, যদিও এখনও বেশ দীর্ঘ কারণ আমি পাইথন কনসোলটির মতো আচরণের জন্য বিশেষ কীগুলির (যেমন রিটার্ন, ট্যাব ...) জন্য বাইন্ডিং লিখেছিলাম। জেডি সহ অটোকম্প্লেশন এবং পাইগমেন্টগুলি সহ সিনট্যাক্স হাইটাইটিংয়ের মতো আরও বৈশিষ্ট্য যুক্ত করা সম্ভব।

মূল ধারণাটি হ'ল আমি কমান্ডগুলি সম্পাদন করার জন্য push()পদ্ধতিটি ব্যবহার করি code.InteractiveConsole। এই পদ্ধতিটি Trueযদি এটি আংশিক কমান্ড হয়, যেমন def test(x):, ফিরে আসে এবং আমি এই প্রতিক্রিয়াটি একটি ...প্রম্পট সন্নিবেশ করতে ব্যবহার করি , অন্যথায়, আউটপুট প্রদর্শিত হয় এবং একটি নতুন >>>প্রম্পট প্রদর্শিত হয়। আমি আউটপুট ব্যবহার করে ক্যাপচারcontextlib.redirect_stdout

এছাড়াও চিহ্নগুলি এবং ইনডেক্সের তুলনা করে প্রচুর কোড রয়েছে কারণ আমি ব্যবহারকারীকে পূর্বে সম্পাদিত কমান্ডগুলির মধ্যে পাঠ্য সন্নিবেশ করা থেকে বিরত করি। ধারণাটি হ'ল আমি একটি চিহ্ন 'ইনপুট' তৈরি করেছি যা আমাকে সক্রিয় প্রম্প্টের শুরু কোথায় তা এবং self.compare('insert', '<', 'input')ব্যবহারকারী কখন সক্রিয় প্রম্পটের উপরে পাঠ্য সন্নিবেশ করানোর চেষ্টা করছে তা সহ আমি জানতে পারি।

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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


if __name__ == '__main__':
    root = tk.Tk()
    console = TextConsole(root)
    console.pack(fill='both', expand=True)
    root.mainloop()
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.