Detect whether or not cursor is in Text widget - python

I am writing a text editor in Python 3 with tkinter, and I'm trying to add an undo function, but in order to log the user's edits I need to log the edit when they type a letter. However, I don't want to log the typed letter if they have clicked out of the text widget. My question is this:
Can I detect whether or not the Text widget's cursor is in the widget? Is there an attribute on the text widget, or a bind I can put on the master window to detect if the cursor is in the text widget?

If your bindings are on the text widget, and if the focus is not on the text widget, your text widget will never see the keypress. Focus management is built in to Tkinter, so you shouldn't have to do anything.
To answer your specific question, you can use the method focus_get to retrieve the widget that currently has keyboard focus. You can also bind to <FocusIn> and <FocusOut> to be notified when the widget gains or loses focus.
Also, are you aware that the text widget has a built-in undo facility? The New Mexico Tech website has a nice brief overview: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/text-undo-stack.html

Related

Hide Text Cursor in Tkinter Entry

So I was working with Tkinter entries and I wanted to know whether or not there was a way to hide the text cursor in the entry. Here is just a sample entry I created (and it looks pretty horrible right now):
The text cursor in this entry is very clearly visible and it continues to blink even if I click somewhere else on the screen. Is there a way to manually hide the cursor in Tkinter? I wasn't able to find any articles on the subject so is this even possible?
Here is the code for creating an entry in tkinter:
from tkinter import *
top = Tk()
E1 = Entry(top, bd=5)
E1.pack(side=RIGHT)
E1.focus_set()
top.mainloop()
And so this will raise the same question, how do I hide the text cursor? This code also does not output the image I have given because that was made with goopylib, a graphics framework made by me on top of Tkinter. So, for the whole code you can see https://github.com/BhavyeMathur/goopylib/blob/master/goopylib/objects/Entry.py and this is the program I used:
from goopylib.imports import *
window = GraphWin("Test Window", width=110, height=110)
Entry(Point(55, 55), text_width=10).draw(window)
while True:
update(24)
to run this code, you will need goopylib installed which you can do using:
pip install goopylib
The cursor is visible when the widget has focus, which is important for when the user is typing into the widget.
If you don't want the cursor, the documented way is to set the state to "readonly". From the canonical documentation:
If the entry is readonly, then the value may not be changed using widget commands and no insertion cursor will be displayed, even if the input focus is in the widget; the contents of the widget may still be selected.
The problem could also be simply that when you click somewhere is, that "somewhere else" wasn't designed to take keyboard focus. If you adjust your bindings so that what you click on receives focus, then focus will be removed from the entry widget and the cursor will be hidden until focus is restored.
For example, if you're creating items on a canvas you can create a binding to move the focus to the canvas when you click on it:
the_canvas.bind("<1>", lambda event: event.widget.focus_set())
When you click on the canvas, focus is moved to the canvas and away from the entry, so the entry will no longer show the cursor.
To hide the cursor on the entry box (known as insert cursor) we can use an argument to the entry box like:
Entry(top,insertontime=0,bd=5)
using E1.focus_set() will set the focus to the entry box while the app is launched at the beginning, unless you click away.
To hide the Blinking Cursor of Entry Widget,
Its Very Simple.
Entry(root,
insertontime=0,
)

How can I create a read-only text widget with TkInter and still be able to modify the widget's content programmatically?

I want to use Tk's text widget to display part of the state of my program. (A label widget will not do, because the tag feature of the text widget will save a lot of work. A canvas widget will not do, because I don't want to have to lay out a lot of text manually.)
I do not want the user to be able to directly modify the contents of the text widget. They can change the state of the program by interacting with it in other ways, but the text widget is for display only.
If I set the state of the text widget to disabled, then not only is the user unable to interact with it, but I also cannot modify its contents programmatically (specifically, I cannot insert text).
The obvious workaround is decorate any code that updates the contents of the text widget with code the enables and disables the widget. But this is kludgy: I should be able to modify the contents without offering the user an opportunity to interfere, however brief that opportunity may be.
Is there a way to do this?
The "kludgy workaround" isn't kludgy at all -- that's exactly how you do it, and how it was designed to work.
However, there is another solution. You can remove all of the built-in key and mouse bindings from the widget, and then re-implement only the ones you care about (for example, you might want the user to highlight and copy a block of text). This is simple and effective, but if you want to restore some of the bindings it starts to become very tedious to re-implement the bindings you care about (cut, copy, paste, page up, page down, moving the cursor, etc).
To remove all of the default bindings you can remove the class bind tag like this:
t.bindtags((t, root, "all"))
For more information about bind tags, see these answers:
https://stackoverflow.com/a/11542200/7432
https://stackoverflow.com/a/3513906/7432
https://stackoverflow.com/a/11459001/7432
you could constantly delete then insert the text like so:
import tkinter
root = tkinter.Tk()
text = tkinter.Text(root, width=40)
text.grid(row=0, column=0, stciky=tkinter.W)
while True:
text.delete(0.0, tkinter.END)
text.insert(tkinter.END, your_text)
where your_text is the text you want to insert

tkinter popup and text processing for autocomplete

I'm making autocomplete feature for a text editor in tkinter & python.
Currently the process of autocomplete is:
If there is a input like the one in a dictionary of autocomplete,call popup.
I do it via t_start.bind("< Key >", asprint) where asprint is my popup function.
I can escape the popup via escape button or by clicking elsewhere.
What I want is - upon user pressing any text key - re-trigger popup again, narrowing search in the autocomplete.
F->FI->FIL->FILE
sort of thing. I don't know what to use to get that input, AFTER the popup is open. How do I get 2nd and every following input character?
The popup function is:
def popup(event):
selected_text=''
try:
selected_text=t_start.get("sel.first", "sel.last")
except TclError:
for i in range(len(selected_text)):
if selected_text[i:0]==word[i:0]:
menu.add_command(label="%s" %selected_text, command=insert_word)
menu.delete(0)
else:
pass
menu.tk_popup(event.x_root, event.y_root)
The key is to keep the keyboard focus in your entry widget. When you popup your window, make sure the focus stays (or is returned to) the entry widget. Any events that affect the popup need to be attached to the entry widget rather than the popup window.
However, if you're using a menu as your popup, this will be impossible. A menu is the wrong choice for an auto-complete feature because it steals all events until the menu is dismissed. Your popup needs to be an instance of a Toplevel widget (if you want it to "float") or some other widget (listbox, text, canvas, etc) if you want it embedded inside your window.
There is a recipe on ActiveState that gives an example of doing autocomplete using an embedded window. http://code.activestate.com/recipes/578253-an-entry-with-autocompletion-for-the-tkinter-gui/

wxPython: switching text control focus on tab press

I made a frame that asks the user to put in a bunch of information in several text control fields. How can I make it so that when you hit the 'tab' key your cursor moves to the next text control?
If you put a wx.Panel as the only child of the ScrolledWindow and put the other widgets on the panel, then it should work automatically. You could also use ScrolledPanel instead.

Selecting Widgets

In Tkinter I'm trying to make it so when a command is run a widget is automatically selected, so that a one may bind events to the newly selected widget.
Basically I want it so when I press a button a text widget appears. When it appears normally one would have to click the text widget to facilitate the running of events bound to the text widget. I want that behavior to automatically happen when the user clicks the button. So that one does not have to click the button and then the text widget, but simply the button.
I'd also like it so if one started typing after the button was pressed it would automatically start filling the text widget. Again to cut out having to click on the text widget.
What bit of code does the above?
The terminology which describes what you want is "focus" -- you want to set the keyboard focus to your text widget. To do that you need to use the focus_set() and/or focus_force() methods on the text widget.

Categories

Resources