1

Context: I am trying to make a terminal using tkinter out of textbox. One of the properties of a terminal is once you have executed a line of code (after hitting enter), you will not be able to make edits/type something on the previous lines, but you can type something on the new line (after execution). You will also not be able to edit the output given and also the starting parameter of the terminal.

Example: https://i.stack.imgur.com/Ptuok.png

I will not be able to edit or write anything into the characters and lines I circled: https://i.stack.imgur.com/MMmQq.png. But I am able to type in something on the new line after the characters "C:\>"

Question: So how am I able to achieve this using tkinter inside a textbox? In my case, I should not be able to edit/make changes to the characters, "C:\>" and also previous sentences (Includes input and outputs)

Research: I did some research and you could use "state=DISABLED" but it prevents you from modifying changes to the entire textbox

My Code for now:

from tkinter import *

def enter(event):
    def insert():
        tfield.insert("end", ">>>")
    root.after(10, insert)

root = Tk()
tfield = Text(root, bg='black', fg='white')
tfield.pack()
tfield.bind("<Return>", enter)
root.mainloop()
j_4321
  • 15,431
  • 3
  • 34
  • 61
DJ ShankyShoe
  • 39
  • 1
  • 5
  • You could use two text boxes and make one populate the disabled. Another option would be to write a callback that deletes unwanted text as it is entered. – ShayneLoyd Sep 10 '20 at 12:22

1 Answers1

2

You can use a mark to keep track of the beginning of the editable part, I called it "input" in the code below. In addition I have adapted Bryan Oakley's answer https://stackoverflow.com/a/16375233/6415268: instead of updating line numbers in the proxy, it checks whether the characters are inserted/deleted in the editable part. To do so I use: Text.compare('insert' < 'input').

Here is a full example:

import tkinter as tk

class ConsoleText(tk.Text):

    def __init__(self, master=None, **kw):
        tk.Text.__init__(self, master, **kw)
        self.insert('1.0', '>>> ') # first prompt
        # create input mark
        self.mark_set('input', 'insert')
        self.mark_gravity('input', 'left')
        # create proxy
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)
        # binding to Enter key
        self.bind("<Return>", self.enter)


    def _proxy(self, *args):
        largs = list(args)

        if args[0] == 'insert':
            if self.compare('insert', '<', 'input'):
                # move insertion cursor to the editable part
                self.mark_set('insert', 'end')  # you can change 'end' with 'input'
        elif args[0] == "delete":
            if self.compare(largs[1], '<', 'input'):
                if len(largs) == 2:
                    return # don't delete anything
                largs[1] = 'input'  # move deletion start at 'input'
        result = self.tk.call((self._orig,) + tuple(largs))
        return result

    def enter(self, event):
        command = self.get('input', 'end')
        # execute code
        print(command)
        # display result and next promp
        self.insert('end', '\nCommand result\n\n>>> ')
        # move input mark
        self.mark_set('input', 'insert')
        return "break" # don't execute class method that inserts a newline

root = tk.Tk()
tfield = ConsoleText(root, bg='black', fg='white', insertbackground='white')
tfield.pack()
root.mainloop()
j_4321
  • 15,431
  • 3
  • 34
  • 61
  • Thanks greatly for this solution. It works like a charm. However, there is this minor bug I would like to mention. When moving to the starting perimeter of the terminal on the current line/sentence and hit backspace, it attempts removes the first character of the command on the current sentence. Anyways this bug doesn't really matter to me. Thanks again!! – DJ ShankyShoe Sep 10 '20 at 13:50
  • @DJShankyShoe Thanks for pointing this out, I have corrected the code – j_4321 Sep 10 '20 at 14:01
  • @ j_4321, after the bug fix, there is another bug. Now the current line of command cannot be edited/deleted – DJ ShankyShoe Sep 10 '20 at 14:58
  • @DJShankyShoe Yes, of course, I forgot the if statement. I've fixed the code now and hope everything works properly – j_4321 Sep 10 '20 at 20:28
  • new bug. Now the backspace deletes characters from the start instead of the end – DJ ShankyShoe Sep 11 '20 at 05:58
  • @DJShankyShoe My bad, I inserted the correction at the wrong place. This time I pasted the whole code. – j_4321 Sep 11 '20 at 07:04