3

I have a quick question about Tkinter Text and Entry widgets. How can I make specific text non-removable? and yes, I know about .config(state="disabled") and no, it's not what I want, it just makes all text non removable.

Let's say I have a text box. In that text box there's a "Enter message" text. The user can type and delete like any other text box. But, i don't want the user to delete "Enter message".

Its like in the Windows shell where you can't delete "C:\Users\your username>" or whatever directory you were in.

Here's some code:

from tkinter import *

root = Tk()
root.geometry("500x500")
entry = Entry(root, bg="black", fg="white")
entry.pack(side="top", fill="x")
entry.insert(END, "Enter message: ")
entry.non_removable("Enter message: ") # << not a real code. just what i imagine it to be

mainloop()
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • 1
    Does this answer your question? [How can you mark a portion of a text widget as readonly?](https://stackoverflow.com/questions/11180130/how-can-you-mark-a-portion-of-a-text-widget-as-readonly) – Thingamabobs Dec 31 '20 at 13:59
  • 1
    @Atlas435: that's for the Text widget. This question is about the Entry widget. The two widgets have very different capabilities. – Bryan Oakley Dec 31 '20 at 16:39
  • @BryanOakley *about Tkinter Text and Entry widgets.* was the Question of the OP. I think with your provided code it is possible with a few adjustment to create an costum entry. – Thingamabobs Dec 31 '20 at 21:17
  • 1
    @Atlas435: Ok. Yeah, the opening paragraph mentions a `Text` widget, but the code doesn't create one. Thanks for pointing that out. – Bryan Oakley Dec 31 '20 at 22:54

2 Answers2

6

Another method would be to use validate.

def do_validate( text ):
    # if this returns False the entered key(s) aren't accepted.
    return text.startswith( "Enter message: " )

import tkinter as tk

root = tk.Tk()
root.geometry("500x500")
entry = tk.Entry(root, bg="black", fg="white")
entry.pack(side="top", fill="x")
entry.insert( tk.END, "Enter message: ")

validate_cmd = ( root.register( do_validate ), '%P' )
entry.config( validate = 'key', validatecommand = validate_cmd )

root.mainloop()

If the string created by the keystrokes doesn't start with "Enter Message: " the keystrokes are rejected and the Entry text is unchanged.

Tls Chris
  • 3,564
  • 1
  • 9
  • 24
1

The simplest way to do this is to disable backspace by binding and returning 'break' when the text matches your text and unbind it when it doesn't.

Here try this:

from tkinter import *

def check(*args):
   
    if entry.index(INSERT) <= len("Enter message: "):
        entry.bind('<BackSpace>', lambda _:'break')
        
    else:
        entry.unbind('<BackSpace>')
            
    
root = Tk()
root.geometry("500x500")

var = StringVar()
var.trace('w', check)

entry = Entry(root, textvariable=var)
entry.pack(side="top", fill="x")
entry.insert(END, "Enter message: ")

mainloop()

Update The above code would fail if the user uses mouse selection and deletes it. So you must clear the selection.

Here use this

from tkinter import *

def check(*args):
    
    if entry.index(INSERT) <= len("Enter message: "):
        entry.bind('<BackSpace>', lambda _:'break')
        entry.bind('<Delete>', lambda _:'break')
        entry.bind('<Shift-.>', lambda _:'break')
        if entry.select_present():
            entry.select_clear()
        
    else:
        entry.unbind('<BackSpace>')
        entry.unbind('<Delete>')
        entry.unbind('<Shift-.>')
    
root = Tk()
root.geometry("500x500")

var = StringVar()
var.trace('w', check)

entry = Entry(root, textvariable=var)
entry.pack(side="top", fill="x")
entry.insert(END, "Enter message: ")
entry.bind('<ButtonRelease>', check)

mainloop()

Ok, I found another fool-proof workaround:

from tkinter import *

def check(*args):
    global old_var

    if var.get()[:15] != check_string:
        var.set(old_var)

    else:
        old_var=var.get()
     
    
root = Tk()
root.geometry("500x500")

check_string = "Enter message: "

var = StringVar()
var.set(check_string)

var.trace('w', check)
old_var = check_string

entry = Entry(root, textvariable=var)
entry.pack(side="top", fill="x")

mainloop()

working: create a temporary variable. say old_var. Assign both var and old_var to the same string initially. Use the trace method to detect changes in the text. Now Compare if the newly changed text contains the string. If it doesn't contain the string, set it to the old string, and if it does set the old_var to var.get()

You may use any of the above code, whichever suits you

JacksonPro
  • 3,135
  • 2
  • 6
  • 29