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/
Related
So I have used .bind() on all my buttons and basically I would like to see which button is currently 'selected' when I switch between them with tabulator key. I did spend already quite a while on search for solution but didn't found anything useful. I don't know how to grab that moment when after pressing Tab key focus is on a button.
When there is more widgets I can .bind() Tab key to a widget proceeding button to simply just change foreground of a button but this won't work in case when I have only buttons in frame because first button will be omitted and that's not a clean and right solution to my issue.
If I bind Tab key to that button and change foreground then it's changed but after pressing Tab when button was already selected.
I don't know is there any clean solution for that problem or I will have to for frames that have only buttons create some starting_dummy widget that would initiate change for a first button.
Haven't found a way to detect is Tab key focused at this moment on a particular button (like it is with hover and cursor) but I know when it's going to be so I used that and solved case with only buttons in frame. It's based on what I already wrote in the question - I'm binding Tab key with a function to a widget preceding button to change button colour while button is bind to a function that's changing colour back to normal like this:
self.entry.bind("<Tab>", self.focus_in)
self.button.bind('<Tab>', self.focus_out)
def focus_in(self, event):
self.button.configure(fg_color='white')
def focus_out(self, event):
self.button.configure(fg_color='black')
For the case when only widgets in the frame/root are buttons, like in a menu that I got first I focus_set() on the last button and make a functions that circulate colours on and off at on Tab like this:
self.button2.focus_set()
self.button1.bind('<Tab>', self.focus_in)
self.button2.bind('<Tab>', self.focus_out)
def focus_out(self, event):
self.button1.configure(fg_color='white')
self.button2.configure(fg_color='black')
def focus_in(self, event):
self.button1.configure(fg_color='black')
self.button2.configure(fg_color='white')
Whit a custom made buttons it's just matter of replacing fg_color with a image.
If you use a button as an image holder for your logo or so, then just set state='disabled' and it will be omitted.
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,
)
I have a Python 3.7 tkinter GUI, and within the GUI I have implemented up-down arrow key controls for the main part of the application. Next to it I have a list box that also controls the application but in a different way, and by default AFTER a listbox selection has been made the listbox selection will scroll with up and down arrows. So, after I've used the list box in the app, the arrow key triggers an up arrow event in both the main part of the application and in the list box. This triggers my application to respond in the change in list box selection by loading new data into the main app. This is obviously unacceptable.
How can I disable the arrow key controls feature of tkinter's
ListBox?
I've tried configuring the listbox to not take focus, but this doesn't seem to disable the feature.
Edit:
I solved this by binding the list box's FocusIn event to a function that immediately focus's something else. This is far from ideal, as the code now focus's in then changes focus for no reason. If there is a way to disable focus on a widget completely or disable the list box key bindings that would be a preferred solution.
from tkinter import *
class App:
def __init__(self):
self.root = Tk()
self.dummy_widget = Label()
self.lb = ListBox(master=self.root)
self.lb.bind("<FocusIn>", lambda event: self.dummy_widget.focus())
# Additional setup and packing widgets...
if __name__ == '__main__':
mainloop()
This seems very "hacky", although it does the job perfectly.
How can I disable the arrow key controls feature of tkinter's ListBox?
Create your own bindings for the events you want to override, in the widget in which you want them overridden. Do anything you want in that function (including nothing), and then return the string break, which is the documented way to prevent events from being processed any further.
For a more extensive description of how bindings work, see this answer to the question Basic query regarding bindtags in tkinter. It describes how a character is inserted into an entry widget, but the mechanism is identical for all events and widgets in tkinter.
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
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.