Change tkinter entry widget multiple times after pressing a button - python

I have a tkinter Entry widget and when a user presses a button the contents update:
from tkinter import *
window = Tk()
def abcdef(num):
ent.config(state=NORMAL)
ent.delete(0, 'end')
ent.insert(0, num)
ent.config(state = "readonly")
print(num) #Just to check the code is being run
def changeEntry():
for j in range(3):
ent.after(1000, abcdef(j))
ent = Entry(widow, text="", state = "readonly", readonlybackground="white", font = "20")
ent.grid(row = 0, column = 0, columnspan = 3, sticky = "E")
btn = Button(window, text="Button", command=changeEntry)
btn.grid(row = 1, column = 0, sticky = "NESW", pady = 10, padx = 10)
window.mainloop()
When I press the button the window freezes for 3 seconds and then just displays the final number. How can I make it so when the user presses the button, the entry changes every second instead of just freezing for 3 seconds and only displaying the final one?
Thanks in advance

You have two problems with that .after call. The .after method tells Tkinter to call the function you pass it after the time interval has passed. But you're telling Tkinter to do 3 things after 1000 milliseconds have passed, so they'll all happen on top of each other. So you need to stagger the delays.
Secondly, you need to give .after a function to call when its time to call it. But your code calls the function and gives .after the return value of your function. We can fix that by wrapping the function call inside another function. A convenient way to do that is using lambda, giving the lambda a default argument it can pass to abcdef
import tkinter as tk
window = tk.Tk()
def abcdef(num):
ent.config(state=tk.NORMAL)
ent.delete(0, 'end')
ent.insert(0, num)
ent.config(state = "readonly")
print(num) #Just to check the code is being run
def changeEntry():
for j in range(3):
ent.after(1000 * j, lambda num=j: abcdef(num))
ent = tk.Entry(window, text="", state = "readonly", readonlybackground="white", font = "20")
ent.grid(row = 0, column = 0, columnspan = 3, sticky = "E")
btn = tk.Button(window, text="Button", command=changeEntry)
btn.grid(row = 1, column = 0, sticky = "NESW", pady = 10, padx = 10)
window.mainloop()
I've also replaced that "star" import with the neater import tkinter as tk. That makes it obvious which names come from Tkinter and which names are local to your program.
Bryan Oakley points out that we don't need that lambda, we can pass in arguments after the function name. See the Basic Widget Methods in the Tkinter docs for details. So we can re-write changeEntry like this:
def changeEntry():
for j in range(3):
ent.after(1000 * j, abcdef, j)
Thanks, Bryan!

Related

How to close a tkinter dialog box after running another function?

I am working with python tkinter and I have a dialog box window that pops up. I have two buttons in the box:
from tksheet import Sheet
from tkinter import *
import os
import sys
import mapMaker2
root=Tk()
root.title('Map Tool')
root.geometry("750x750")
sheetframe = Frame(root)
sheetframe.grid(row = 0, column = 0,)
buttonEditlabel = Button(sheetframe, text='Edit Labels', width=12, command=lambda: [openEditWindow()], bg='#cacccf',fg='black')
buttonEditlabel.grid(row = 0, sticky=W, column = 0, pady = (25,5), padx = (50,0))
def openEditWindow():
top = Toplevel(root)
top.geometry("260x195")
top.title('Edit Axes Labels')
frm = Frame(top, borderwidth=0, relief='ridge')
frm.grid(row = 0, column = 0, pady = (20,0),padx=(20,0))
b_cancel = Button(frm, text='Close', width=10)
b_cancel['command'] = top.destroy
b_cancel.grid(column = 0, row = 6, pady = (15,0),padx=(0,0))
b_save = Button(frm, text='Save', width=10)
b_save['command'] = lambda: editLabels()
b_save.grid(column = 1, row = 6, sticky = E, pady = (15,0),padx=(0,0))
def editLabels():
pass
mainloop()
Cancel button closes the window with top.destroy command. I would like the Save button to also close the window after running the editLabels() function first. I have tried:
b_save['command'] = [lambda: editLabels(), top.destroy]
but this doesn't work.
Here is one way you can do it. Create a function to destroy the top window.
def kill_main():
top.destroy()
top.update()
Then call the function wherever you want. You don't have to add kill_main() to the button itself. Just put it inside the next function you are opening so that it will close the Top windows first and then run the rest of the editlabels() function. Hopefully, it makes sense to you.
def editLabels():
kill_main()
pass

Tkinter : How to pass a button's text as an argument to a function when button is clicked

I have the following code that generates a random button grid of 5x5 dimensions :
import tkinter as tk
from tkinter import *
from tkinter import messagebox
import random
def numberClick(num):
messagebox.showinfo('Message', 'You clicked the '+str(num)+' button!')
root = Tk()
#root.geometry("200x200")
w = Label(root, text='Welcome to Bingo!')
linear_array = [i for i in range(1,26)]
random_array = []
for i in range(1,26):
temp = random.choice(linear_array)
linear_array.remove(temp)
random_array.append(temp)
for i in range(25):
num = random.choice(random_array)
#tk.Label(root,text=num).grid(row=i//5, column=i%5)
redbutton = Button(root, text = num, fg ='red',height = 3, width = 5,command=lambda: numberClick(num))
redbutton.grid(row=i//5, column=i%5)
root.mainloop()
I have implemented the command function and passed a parameter with lambda as shown :
redbutton = Button(root, text = num, fg ='red',height = 3, width = 5,command=lambda: numberClick(num))
Now when I click a button , the function call should print the text value assigned with it. Instead it just prints the same value , that is the last assigned value in num variable :
Output when i clicked on button 20
Any workarounds?? TIA.
Just change your button to:
redbutton = Button(root, text = num, fg ='red',height = 3, width = 5,command=lambda num=num: numberClick(num))
This should fix the issue, this will store the value of num in lambda rather than just looping and using the last value of num.
I was just about to point out the same thing a Cool Cloud, but I'd also like to add that you are randomizing twice such that you might get duplicate numbers.
The first for loop randomizes the numbers 1-25 in random_array, but then in the second loop you randomly select one element from that list without removing it when you initialize num. I'd write the second loop as:
for i in range(25):
num = random_array[i]
redbutton = Button(root, text = num, fg ='red',height = 3, width = 5, command=lambda n=num: numberClick(n))
redbutton.grid(row=i//5, column=i%5)

How to bind a mouse click with a tkinter witdges? [duplicate]

This question already has answers here:
Entry box text clear when pressed Tkinter
(2 answers)
Closed 2 years ago.
I want to clear text box as soon as i click on it to write, Please help me to do this
from tkinter import *
from tkinter import ttk
root = Tk()
fileLable = Text(root, height = 1, width = 26 )
fileLable.insert(INSERT, "this text")
fileLable.grid(row = 0, column = 0,columnspan = 2, padx = (10,0),pady = (3,0))
submit = ttk.Button(root, text = "Submit", command = some_method)
submit.grid(row = 1, column = 1, padx = (10,0), pady = (10,0))
root.mainloop()
I know that this one :
def some_callback(event): # note that you must include the event as an arg, even if you don't use it.
e.delete(0, "end")
return None
So then how I catch this event when it is clicked on the text box to include above code to my main code
Please help thanks in advance 😊
You could try this below, bind events <FocusIn> and <FocusOut>:
from tkinter import *
from tkinter import ttk
def delText(event=None):
fileLable.delete("1.0", END)
root = Tk()
fileLable = Text(root, height = 1, width = 26 )
fileLable.insert(INSERT, "this text")
fileLable.grid(row = 0, column = 0,columnspan = 2, padx = (10,0),pady = (3,0))
fileLable.bind("<FocusIn>", delText)
submit = ttk.Button(root, text = "Submit")
submit.grid(row = 1, column = 1, padx = (10,0), pady = (10,0))
root.mainloop()
To call a function when a user clicks in a widget, you can bind to the <1> event. The function is passed an argument which contains information about the event, including a reference to the widget that received the event.
To delete all of the text in a text widget, call the delete method with the arguments "1.0" and "end".
Putting that together, it looks like this:
def clearText(event):
event.widget.delete("1.0", "end")
fileLable.bind("<1>", clearText)

Tkinter pop-up window with check button, not able to get check button states

I am making a pop up window ,on press of a button on main window. The pop up window have many check buttons, I need to get the state of the check buttons back to main window and also to make use of a select all and deselect all button. But I am getting error using the variables, and not able to pass the states back to main window.
Here is the program I wrote:
import Tkinter
import tkMessageBox
top = Tkinter.Tk()
state=[[]]
def popup(x):
def select_clear_states():
global Vars, all_states
states = all_states.get()
if states == 0:
for I in range(len(Vars)):
Vars[I].set(0)
if states == 1:
for I in range(len(Vars)):
Vars[I].set(1)
Vars = []
root = Tkinter.Tk()
all_states = Tkinter.IntVar()
select_all = Tkinter.Checkbutton(root, text = "select/deselect all",variable = all_states, command = select_clear_states)
select_all.grid(row = 0, column = 0, padx = 5, pady = 1)
for n in range(10):
var = Tkinter.IntVar()
Vars.append(var)
checkbox = Tkinter.Checkbutton(root, text = n+1, variable= Vars[n])
checkbox.grid(row = n+1, column = 0, padx = 5, pady = 1)
root.mainloop()
A = Tkinter.Button(top, text ="Hello1",command=lambda: popup(1))
A.pack()
B = Tkinter.Button(top, text ="Hello2",command=lambda: popup(2))
B.pack()
C = Tkinter.Button(top, text ="Hello3",command=lambda: popup(3))
C.pack()
top.mainloop()
am not able to make use of the variables Vars all_state and state[[]], to get he state of the check button.am getting this error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1536, in __call__
return self.func(*args)
File "C:\Users\Synapse\Documents\Neo\Tutorial\python_tutorial\button.py", line 11, in select_clear_states
states = all_states.get()
NameError: global name 'all_states' is not defined
I should be able to use select/deselectall button and also able to get back all states of each check button to main window.
Example code using class.
It use Toplevel() to create PopUp because Tkinter should have only one Tk() window.
And it use only one mainloop() because more loops make problem with values in variables.
It can created different number of checkboxes.
It needs more work - ie. to return values - but now you can use win.states to get states of all checkboxes (or to set them).
import Tkinter as tk
import tkMessageBox
# --- classes --- (CamelCase names)
class PopUp(tk.Toplevel):
def __init__(self, number=10):
tk.Toplevel.__init__(self)
# - global checkbox to set all "small" checkboxes -
self.global_state = tk.BooleanVar()
cb = tk.Checkbutton(self, text="select/deselect all",
variable=self.global_state,
command=self.select_clear_states)
cb.grid(row=0, column=0, padx=5, pady=1)
# - "small" checkboxes -
self.states = []
for n in range(1, number+1):
var = tk.BooleanVar()
cb = tk.Checkbutton(self, text=str(n), variable=var)
cb.grid(row=n, column=0, padx=5, pady=1)
self.states.append(var)
def select_clear_states(self):
# get global checkbox
state = self.global_state.get()
# set all "small" checkboxes
for x in self.states:
x.set(state)
# --- functions --- (lower_case names)
def popup(num):
win = PopUp(num)
#win.states[1].set(True)
# --- main --- (lower_case names)
root = tk.Tk()
b = tk.Button(root, text="5 checkboxes", command=lambda:popup(5))
b.pack()
b = tk.Button(root, text="10 checkboxes", command=lambda:popup(10))
b.pack()
b = tk.Button(root, text="3 checkboxes", command=lambda:popup(3))
b.pack()
root.mainloop()

getting around ints and floats with button initialisation for Tkinter calculator

Below is a follow on from this question...
Python & Tkinter - buttons command to set label textvariable issue
I've finished the calculator but here is the problem:
My buttons were all built using a for-loop; function buttons included. I like the fact that the code is short and don't really want to go and remove all function buttons from the for-loop but i might have to to get around the problem where I don't get a float value returned for the division of two integers.
i.e 12/8 = 1 according to this calculator.
Any clever ideas how I could do that without removing the operators form the for-loop?
from Tkinter import *
import Tkinter as tk
import tkMessageBox
# main window
root = Tk()
root.title('Calculator')
# button set
buttons = ['1','2','3','4','5','6','7','8','9','0','+','-','/','*','.']
sum_value = StringVar()
def appear(x):
return lambda: output_window.insert(END,x)
# output window
output_window = tk.Entry(root, textvariable=sum_value, width=20, font = 'courier 10')
output_window.grid(row=0, columnspan=3, sticky=(E,W))
def equals():
try:
result = eval(output_window.get())
except:
result = 'INVALID'
output_window.delete(0,END)
output_window.insert(0,result)
def refresh():
output_window.delete(0,END)
# button creation
r=1
c=0
for i in buttons:
if c < 2:
tk.Button(root, text = i, command = appear(i), pady = 3).grid(row = r, column = c, sticky = (N,S,E,W))
c += 1
else:
tk.Button(root, text = i, command = appear(i), pady = 3).grid(row = r,column = c,sticky = (N,S,E,W))
r += 1
c = 0
# clear and equal button
equal = tk.Button(root,text='=',padx = 5, pady=3, command=equals)
equal.grid(row=6,column=0,sticky=(N,S,E,W))
clear = tk.Button(root,text='CLEAR',padx = 5, pady=3,command = refresh)
clear.grid(row=6,column=1, columnspan = 2,sticky=(N,S,E,W))
#menu
menubar = Menu(root)
def quit1():
if tkMessageBox.askokcancel("Quit","Are you sure you want to quit?"):
root.destroy()
viewMenu = Menu(menubar)
viewMenu.add_command(label='Quit', command = quit1)
menubar.add_cascade(label="Home", menu=viewMenu)
root.config(menu=menubar)
root.mainloop()
Write from __future__ import division as the first line of your program. This will make / into the floating point division operator. Of course, now 8/4 will give 2.0 not the integer 2. (If you wanted integer division also, you could add a // button, but I gather you want this to work like a standard hand-held calculator.)

Categories

Resources