Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read Only Text widget in python3-tkinter; cross platform

How to suppress end user ability to edit/add/delete text in a Text widget? (Python v3.2.. and tkinter)

The point is to suppress only the ability to change/add/delete text but not to castrate other features. Perhaps a NoEdit Text widged would be a better name.

I've tried .text['state'] = 'disabled' and it works almost OK in Windows (it still allows user to select/copy text highlights the selection, page up/down and up/down buttons work. The only thing broken seems to be the cursor made invisible.)

But on MacIntosh everything is broken. No highlights, no select/copy,... UGH

Since Tkinter has practically no documentation in Python, I've searched and found some TCL advise, to derive a new class and suppress the insert and delete functions.

So, I've tried as so:

class roText(tk.Text):
    def insert(self,*args,**kwargs):
        print(" Hey  - Im inside roText.insert")
        pass
    def delete(self,*args,**twargs):
        pass    
    def pInsert(self,*args,**twargs):
        super().insert(*args,**twargs)

Unfortunately it didn't work right. Apparently tkinter does not use those insert and delete functions when end user enters/deletes code. Perhaps those TCL insert/delete are something else, and I lost something in translation from TCL and Swahili. What functions does tkinter.Text use for end user editing text? Hopefully they are not internal...

So, is there a way to modify the Text widget to suppress only end user editing? Is there a way to do it without diving inside and overriding internal Tkinter code, so the stuff doesn't get broken by next releases of Tkinter?

Looking at the Idle shell window, I see that they've managed to suppress edits (except for the last line). So there is a way. But what is it and how costly?

like image 368
Momus Avatar asked Oct 16 '11 05:10

Momus


People also ask

Is there a way to make the tkinter text widget read only?

In Tkinter, sometimes, we may want to make a text widget disabled. To achieve this, we can set the text configuration as DISABLED. This will freeze the text widget and will make it read-only.

How do you disable a TextBox in Python?

How do I disable TextBox editing? Just SET the TextBox Property Enabled = True / False for Enable / Disable Textbox Control.


2 Answers

Sorry for bumping an old question, but I was searching for an answer to this question also and finally found a solution. The solution I found involves overriding the key bindings when the text widget has focus and is pretty simple. Found here.

To override the bindings of a widget there is a bind function where you pass a string of what is to be overridden and the new function you want it to call.

    self.txtBox.bind("<Key>", self.empty)

Somewhere else in the class you'll need to define the function to handle the event.

    def empty(self, event):
        return "break"

By returning the string "break" the event handler knows to stop after your function, instead of continuing with the default action.

I hope this answers your question. Cheers.

like image 105
colton7909 Avatar answered Oct 30 '22 19:10

colton7909


The reason the disabled state doesn't seem to work on the Mac is because it turns off the binding that gives focus to the widget. Without focus, the highlighting on a Mac doesn't show up. If you set the state to disabled but then assign a binding to <ButtonPress-1> to explicitly set focus to the disabled text widget, you can then select and copy text and the highlighting will show.

As for the cursor disappearing... arguably, that's what's supposed to happen. The cursor tells the user "this is where text will get inserted". Since no text will get inserted, having that visual clue would be confusing to the user. What you could do instead, if it was really important, is to insert a small image wherever they click to simulate the cursor.

To answer your question about whether the widget actually uses the insert and delete methods: the methods on the actual underlying widget are what the default bindings use, so overriding them in a subclass has no effect. You would need to redo all the default bindings for that to work. It's doable, but a lot of work.

Unfortunately, this is one area where programming in Tcl really shines, because you can simply disable the insert and delete commands of the widget. Of course, you can do that directly in Tkinter also since ultimately it runs tcl code to do everything, but that would involve writing some tcl code which is not a very good solution from the perspective of a Python coder.

I think the best solution is to use the disabled state, then add in just enough bindings to do what you want.

Here's a simple example that works by explicitly setting focus on a mouse button click. With this code I'm able to click and swipe to select a region, or double- or triple-click to select words and lines:

import Tkinter as tk

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.text = tk.Text(width=40, height=20)
        self.text.bind("<1>", self.set_focus)
        self.text.insert("end", "\n".join(dir(tk.Tk)))
        self.text.configure(state="disabled")
        self.text.pack(fill="both", expand=True)

    def set_focus(self, event):
        '''Explicitly set focus, so user can select and copy text'''
        self.text.focus_set()

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()
like image 34
Bryan Oakley Avatar answered Oct 30 '22 21:10

Bryan Oakley