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

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()

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()

Make Buttons on Tkinter automatically go to next line when reach border of the frame

I'm trying to make a tkinter script that will print a lot of buttons with functions like this.
from tkinter import *
def printbutton(x:int):
print(x)
root = Tk()
root.geometry('500x500')
for x in range(1,100):
Button(root, text = str(x), command = lambda:printbutton(x)).pack(side="left")
root.mainloop()
But it print all button in a line and expanded outside of my GUI. I want my button automatically go to the next line for convenient, but I don't know how to do it.
Firstly, I would suggest use grid() instead of pack() for more clarity in such cases.
Next, to pass the value of the button in function, bind your printbutton(b) with your button itself. This is because at the end of for loop, the value of x is 99 and this x will be then passed with every button.
Now, to print buttons in a new line after you reach up to the width of the window, I used a simple logic, you can edit the value of width variable according to your window and button size.
So, here's what you are looking for:
from tkinter import *
def printbutton(btn):
print(btn["text"])
root = Tk()
root.geometry('500x500')
r,c = 0,0
width = 21
for x in range(1,100):
b = Button(root, text = str(x))
b.grid(row=r, column=c, sticky=N+S+E+W) #use grid system
b["command"] = lambda b=b: printbutton(b) #now b is defined, so add command attribute to b, where you attach your button's scope within a lambda function
#printbutton(b) is the function that passes in the button itself
c+=1
if c==width:
r+=1
c=0
root.mainloop()

Tkinter remove/overwrite elements from Frame

I created a button that retrieves a list from a DataFrame based on some input from a text field. Everytime the button is pressed, the list will be refreshed. I output the list (as an OptionMenu) in a separate Frame (outputFrame). However, every time I press this button, a new OptionMenu is added to the Frame (instead of overwriting the previous one). How can I make sure that the content of 'ouputFrame' is overwritten each time I press the button?
# start
root = Tkinter.Tk()
# frames
searchBoxClientFrame = Tkinter.Frame(root).pack()
searchButtonFrame = Tkinter.Frame(root).pack()
outputFrame = Tkinter.Frame(root).pack()
# text field
searchBoxClient = Tkinter.Text(searchBoxClientFrame, height=1, width=30).pack()
# function when button is pressed
def getOutput():
outputFrame.pack_forget()
outputFrame.pack()
clientSearch = str(searchBoxClient.get(1.0, Tkinter.END))[:-1]
# retrieve list of clients based on search query
clientsFound = [s for s in df.groupby('clients').count().index.values if clientSearch.lower() in s.lower()]
clientSelected = applicationui.Tkinter.StringVar(root)
if len(clientsFound) > 0:
clientSelected.set(clientsFound[0])
Tkinter.OptionMenu(outputFrame, clientSelected, *clientsFound).pack()
else:
Tkinter.Label(outputFrame, text='Client not found!').pack()
Tkinter.Button(searchButtonFrame, text='Search', command=getOutput).pack()
root.mainloop()
We can actually update the value of the OptionMenu itself rather than destroying it (or it's parent) and then redrawing it. Credit to this answer for the below snippet:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar(root)
choice = [1, 2, 3]
var.set(choice[0])
option = tk.OptionMenu(root, var, *choice)
option.pack()
def command():
option['menu'].delete(0, 'end')
for i in range(len(choice)):
choice[i] += 1
option['menu'].add_command(label=choice[i], command=tk._setit(var, choice[i]))
var.set(choice[0])
button = tk.Button(root, text="Ok", command=command)
button.pack()
root.mainloop()

CTRL + a select all in entry widget tkinter 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()

tkinter: press button to get its own grid

I am trying to create a column of buttons which when they are pressed, the grid that down a row show pop up another two buttons.
for i in tlist:
opl.append(Tk.Button(self.frame, width = 12 , text=i[3],command = lambda:self.pressedop(i[2], a)))
opl[a].grid(row=a, column=0, columnspan=2)
a = a + 1
That successfully create a column of buttons, with text(i[3]) correctly shown, but when a button is pressed, i[2] will be last button's info, and a will be last button's row +1.
Is there a way which I can pass the i[2] and it's own grid_info down?
I don't understand your question, but you can use widget.grid_forget() to remove a widget from the geometry manager. If you then want it in a different row, just widget.grid() with the new row and column. A simple example
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
opl = []
top = tk.Tk()
def move_it(num):
for ctr in range(num, 5):
opl[ctr].grid_forget()
opl[ctr].grid(row=6, column=ctr)
for but_num in range(5):
opl.append(tk.Button(top, width = 12 , text=str(but_num),
command = partial(move_it, but_num)))
opl[-1].grid(row=but_num, column=0, columnspan=2)
top.mainloop()

Categories

Resources