CTRL + a select all in entry widget tkinter python - python

How I can select all text like block using click+drug left mouse in Entry widget tkinter python.
e1 = tk.Entry(bop, width = 50, font = "Helvetica 13")
e1.grid(row=1,column=1, padx=15, pady=15)
e1.bind_class("Entry","<Control-a>", select_all(e1))
here is the function of select_all():
def select_all(e):
a = e.select_range(0,tk.END)

There was so many similar examples on SO
import tkinter as tk
def callback(event):
print('e.get():', e.get())
# or more universal
print('event.widget.get():', event.widget.get())
# select text after 50ms
root.after(50, select_all, event.widget)
def select_all(widget):
# select text
widget.select_range(0, 'end')
# move cursor to the end
widget.icursor('end')
root = tk.Tk()
e = tk.Entry(root)
e.pack()
e.bind('<Control-a>', callback)
root.mainloop()
bind expects filename without () and arguments (callback). But also bind executes this function always with one argument event which gives access to entry which executed this function event.widget so you can use it with many different entries. And finally Entry has .get() to get all text.
EDIT:
Because after releasing keys <Control-a> selection is removed so I use after() to execute selection after 50ms. It selects all text (but it moves cursor to the beginning) and moves cursor to the end. (see code above)
EDIT:
Before I couldn't find correct combination with Release but it has to be <Control-KeyRelease-a> and now it doesn't need after()
import tkinter as tk
def callback(event):
print('e.get():', e.get())
# or more universal
print('event.widget.get():', event.widget.get())
# select text
event.widget.select_range(0, 'end')
# move cursor to the end
event.widget.icursor('end')
root = tk.Tk()
e = tk.Entry(root)
e.pack()
e.bind('<Control-KeyRelease-a>', callback)
root.mainloop()

furas' answer is great but still does not work as a perfect analogue of windows Ctrl+A behavior. The event only fires after releasing the 'a' key, but the event should fire on the 'a' key press.
Taking from Python tkinter: stopping event propagation in text widgets tags , stopping the event propagation is what we need. Returning 'break' stops whatever following event is breaking the ctrl+a behavior, and also allows us to shorten our bind to '<Control-A>'
def callback(event):
# select text
event.widget.select_range(0, 'end')
# move cursor to the end
event.widget.icursor('end')
#stop propagation
return 'break'
root = tk.Tk()
e = tk.Entry(root)
e.pack()
e.bind('<Control-a>', callback)
root.mainloop()

Related

How to use a Python generator function in tkinter?

Generator functions promise to make some code easier to write. But I don't always understand how to use them.
Let's say I have a Fibonacci generator function fib(), and I want a tkinter application that displays the first result. When I click on a button "Next" it displays the second number, and so on. How can I structure the app to do that?
I probably need to run the generator in a thread. But how do I connect it back to the GUI?
You don't need a thread for this particular problem. You just need to instantiate the generator, then use next to get a new value each time the button is pressed.
Here's a simple example:
import tkinter as tk
def fibonacci_sequence():
'''Generator of numbers in a fibonacci sequence'''
a,b = 1,1
while True:
yield a
a,b = b, a+b
def do_next():
'''Updates the label with the next value from the generator'''
label.configure(text=next(generator))
root = tk.Tk()
label = tk.Label(root, text="")
button = tk.Button(root, text="Next", command=do_next)
label.pack(side="top")
button.pack(side="bottom")
# Initialize the generator, then us it to initialize
# the label
generator = fibonacci_sequence()
do_next()
root.mainloop()
Not expert in tkinter but one way is to use function StringVar to be able to update its value and using text box for output.
Try this out, hope it gives you a clue.
import tkinter as tk
fibo_list = [1,1]
def fibo():
fibo_list.append(fibo_list[-1] + fibo_list[-2])
variable.set(fibo_list[-1])
show_fibo_value()
def show_fibo_value():
text_box.insert(index='end',chars=variable.get()+'\n')
root = tk.Tk()
variable = tk.StringVar()
text_box = tk.Text()
text_box.pack()
button = tk.Button(root, text="Next", command=fibo)
button.pack()
root.mainloop()

Is there a function to switch "Tab" and "Enter" button in tkinter?

I'm creating a Gui with Tkinter with a lot of Entry widgets. However, I don't like the fact of clicking on Tab button to go from one entry to another. I would like it to be the Enter key that does this. Is there a function to make it happen ?? here is the list of all entries:
```python
entries = [self.entMath1, self.entMath2, self.entFran1,
self.entFran2, self.entSvt1, self.entSvt2, self.entEps1,
self.entEps2, self.entHg1, self.entHg2, self.entPc1,
self.entPc2, self.entAng1, self.entAng2, self.entPhi1,
self.entPhi2, self.entM1, self.entM2, self.entM3,
self.entM4]
for i in range(len(entries_1) - 1):
entries_1[i].bind("<Return>", lambda e: entries_1[i].focus_set())
```
all these entries are placed with place() method.
You can bind the <Return> key event on the Entry widget. Then in the bind callback, get the next widget in the focus order by .tk_focusNext() and move focus to this widget:
import tkinter as tk
root = tk.Tk()
for i in range(10):
e = tk.Entry(root)
e.grid(row=0, column=i)
e.bind('<Return>', lambda e: e.widget.tk_focusNext().focus_set())
root.mainloop()
Have a read of this answer binding enter to a widget for the difference between <Return> and <Enter>
From that, you can build something like
import tkinter as tk
top = tk.Tk()
frm = tk.Frame(top)
frm.pack()
# List of entries
seq = [None]
next = []
entmax = 5
for ii in range(entmax):
ent = tk.Entry(frm)
ent.pack(side=tk.LEFT, padx=5, pady=5)
seq.append(ent)
next.append(ent)
# Now set the entries to move to the next field
for ix in range(1, entmax, 1):
seq[ix].bind('<Return>', lambda e: next[ix].focus_set())
top.mainloop()

How to trigger tkinter's "<Enter>" event with mouse down?

In tkinter with Python 3.7, the default behavior for an event binding is that an "<Enter>" event will not be triggered after the mouse has been clicked down before it has been released. I was intending to implement a scrollable table such that it detects "<Button-1>" (Mouse left-click down) and "<ButtonRelease-1>" (Mouse left-click up) events as well as having each table-row's widgets "<Enter>" event bound to detect when the mouse pointer enters a different table row. In this way I could scroll my table by clicking an row and dragging through a table. My assumption was that "<Enter>" events would be triggered even while the mouse button is held down, which was incorrect. So, my entire scrolling implementation hit a brick wall. I need these events to be triggered while the mouse is down or it just won't work. I'm doing something like:
from tkinter import *
class App:
def __init__(self):
self.root = Tk()
# The name kwarg is used to infer the index of the row in the event handlers.
self.labels = [Label(text=f"Label #{i}", name=f"row-{i}") for i in range(5)]
for row, label in enumerate(self.labels):
label.bind("<Button-1>", self.mouse_down)
label.bind("<ButtonRelease-1>", self.mouse_up)
label.bind("<Enter>", self.mouse_enter)
label.grid(row=row, column=0)
mainloop()
def mouse_up(self, event):
idx = self.index_from_event(event)
# Do some with the row with the passed index
def mouse_down(self, event):
idx = self.index_from_event(event)
# Do some with the row with the passed index
def mouse_enter(self, event):
# I would like for this to be triggered even when the mouse is pressed down.
# However, by default tkinter doesn't allow this.
pass
def index_from_event(self, event):
# Get the index of the row from the labels name string.
return str(event.widget).split('-')[-1]
Any way to enable mouse enter events while the mouse button 1 is held down in tkinter? All the docs on effbot (http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm) say about the enter event is:
<Enter>
The mouse pointer entered the widget (this event doesn’t mean that the user pressed the Enter key!).
No, there is no direct way to bind to the enter and leave events when the button is down, except for the widget which first gets the click. It is fairly easy to add that ability, however,
You can bind <B1-Motion> to all widgets which will call a function whenever the mouse moves. You can then use the winfo_containing method to figure out which widget is under the cursor. With that information you could generate a virtual event which you can bind to (or you can skip the virtual events and add your code in the handler for the motion event).
Here's a bit of a contrived example. When you click and move the mouse, show_info will be called. It keeps track of the current widget and compares it to the widget under the cursor. If it's different, it generates a <<B1-Leave>> to the previous widget and a <<B1-Enter>> on the new widget. Those bindings will display "Hello, cursor!" when the cursor is over the label.
import tkinter as tk
current_widget = None
def show_info(event):
global current_widget
widget = event.widget.winfo_containing(event.x_root, event.y_root)
if current_widget != widget:
if current_widget:
current_widget.event_generate("<<B1-Leave>>")
current_widget = widget
current_widget.event_generate("<<B1-Enter>>")
def on_enter(event):
event.widget.configure(text="Hello, cursor!")
def on_leave(event):
event.widget.configure(text="")
root = tk.Tk()
label = tk.Label(root, bd=1, relief="raised")
l1 = tk.Label(root, text="", width=20, bd=1, relief="raised")
l2 = tk.Label(root, text="", width=20, bd=1, relief="raised")
label.pack(side="top", fill="x")
l1.pack(fill="both", expand=True, padx=20, pady=20)
l2.pack(fill="both", expand=True, padx=20, pady=20)
root.bind_all("<B1-Motion>", show_info)
l1.bind("<<B1-Enter>>", on_enter)
l1.bind("<<B1-Leave>>", on_leave)
l2.bind("<<B1-Enter>>", on_enter)
l2.bind("<<B1-Leave>>", on_leave)
tk.mainloop()

When I delete Text box in tkinter, a blank line is always left

from tkinter import *
def hi(event):
print(txt.get('1.0',END))
txt.delete('1.0',END)
root = Tk()
lbl = Label(root, text="client")
lbl.pack()
txt = Text(root, height=10, width=50)
txt.pack()
txt.bind('<Return>', hi)
btn = Button(root, text="OK")
btn.pack()
root.mainloop()
When I delete Text widget contents, a blank line is left. How to prevent that?
The built-in bindings cause a newline to be inserted when you press the return key. When you create a binding, your bound function is called before the default behavior. Thus, when you press return, your function deletes everything, and then the default action inserts a newline.
If you want to prevent the default action from happening, your function needs to return the string break:
def hi(event):
print(txt.get('1.0',END))
txt.delete('1.0',END)
return "break"
What happens is that the text widget still gets the return you entered. Binding the Enter does not inhibits the propagation of the event after your function ends, unless you alter it to cancel event propagation by returning the break string, like this:
def hi(event):
print(txt.get('1.0',END))
txt.delete('1.0',END)
return 'break'
You must ask tkinter not to propagate that event to other handlers as follows:
from tkinter import *
def hi(event):
print(txt.get('1.0',END))
txt.delete('1.0',END)
return "break"
root = Tk()
lbl = Label(root, text="client")
lbl.pack()
txt = Text(root, height=10, width=50)
txt.pack()
txt.bind('<Return>', hi)
btn = Button(root, text="OK")
btn.pack()
root.mainloop()

Updating a label from an entry field on button push with tkinter in Python 3.5.2

I am trying to create a window with a line label, an entry field, a current value label, and an "Update Value" button.
Here is an example:
This is what I have so far. I can get the entered value to print to console, but I can't seem to work out how to get an entered value and change the currentValue Label to reflect that value by pressing the button:
from tkinter import*
main=Tk()
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
#This kills the program
def exitProgram():
exit()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry=Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue)
currentValue.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValues=Button(text='Set Values',width=30,command=setValues)
setValues.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton=Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
There are a couple of problems with your code.
Firstly, you are overwriting the setValues function with the setValues Button widget, and similarly, you are overwriting the currentValue StringVar with the currentValue Label.
To set a StringVar, you use its .set method.
Don't use plain exit in a script, that's only meant to be used in an interactive interpreter session, the proper exit function is sys.exit. However, in a Tkinter program you can just call the .destroy method of the root window.
Here's a repaired version of your code.
import tkinter as tk
main = tk.Tk()
#StringVar for currentValue in R0C2
currentValue = tk.StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
currentValue.set(content)
#This kills the program
def exitProgram():
main.destroy()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
tk.Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry = tk.Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValueLabel = tk.Label(textvariable=currentValue)
currentValueLabel.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValuesButton = tk.Button(text='Set Values',width=30,command=setValues)
setValuesButton.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton = tk.Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
BTW, it's a Good Idea to avoid "star" imports. Doing from tkinter import * dumps 130 names into your namespace, which is unnecessary and creates the possibility of name collisions, especially if you do star imports from several modules. It also makes the code less readable, since the reader has remember which names you defined and which ones came from the imported module(s).
In my opinion the easiest way to do this would be using an object orientated method. This way you could declare a button with a command that calls a def which runs self.label.configure(text=self.entry.get()).
This can be seen below:
import tkinter as tk
class App:
def __init__(self, master):
self.master = master
self.label = tk.Label(self.master)
self.entry = tk.Entry(self.master)
self.button = tk.Button(self.master, text="Ok", command=self.command)
self.label.pack()
self.entry.pack()
self.button.pack()
def command(self):
self.label.configure(text=self.entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
The above creates a label, entry and button. The button has a command which calls a def within the class App and updates the value of the label to be the text contained within the entry.
This all works very smoothly and cleanly and more importantly is drastically easier (in my opinion) to read and update in the future.
From your code you are setting the 'currentValue', which is a StringVar:
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
to an object Label further down in your code. You cannot do this!
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue) ** this line is wrong
currentValue.grid(row=0,column=2)
You should name the label something different like:
#Displays what the value is currently set to.
lblCurrentValue = Label(textvariable=currentValue)
lblCurrentValue.grid(row=0,column=2)
Then in your "setValues" method you should use 'StringVar.set(value) to update the label like so:
def setValues():
content = entry.get()
currentValue.set(entry.get())------------------Here I set the value to the entry box value
print(content)
I tend to avoid stringVar and just use:
Label.config(text='*label's text*')
If you need more help I can post you my solution but try and solve it first becasue its the best way to learn. My tip is to make sure you are using correct naming conventions. In tkinter I tend to use lbl..., entryBox... etc before widgets so I know what they are and not to confuse them with variables.

Categories

Resources