Button click counter in Python - python

I am trying to write a Python program to count how many times a button is clicked. I have written the following code:
import tkinter
from tkinter import ttk
def clicked(event):
event.x = event.x + 1
label1.configure(text=f'Button was clicked {event.x} times!!!')
windows = tkinter.Tk()
windows.title("My Application")
label = tkinter.Label(windows, text="Hello World")
label.grid(column=0, row=0)
label1 = tkinter.Label(windows)
label1.grid(column=0, row=1)
custom_button = tkinter.ttk.Button(windows, text="Click on me")
custom_button.bind("<Button-1>", clicked)
custom_button.grid(column=1, row=0)
windows.mainloop()
I know that event.x is used to capture the location of mouse. And hence the result of the program is not as expected. I want something else. Can you please help me solve the issue.

You don't need event for this. You need own variable to count it.
And it has to be global variable so it will keep value outside function.
count = 0
def clicked(event):
global count # inform funtion to use external variable `count`
count = count + 1
label1.configure(text=f'Button was clicked {count} times!!!')
EDIT: Because you don't need event so you can also use command= instead of bind
import tkinter as tk
from tkinter import ttk
count = 0
def clicked(): # without event because I use `command=` instead of `bind`
global count
count = count + 1
label1.configure(text=f'Button was clicked {count} times!!!')
windows = tk.Tk()
windows.title("My Application")
label = tk.Label(windows, text="Hello World")
label.grid(column=0, row=0)
label1 = tk.Label(windows)
label1.grid(column=0, row=1)
custom_button = ttk.Button(windows, text="Click on me", command=clicked)
custom_button.grid(column=1, row=0)
windows.mainloop()

You can add 'counter' as an attribute with a decorator like this:
def static_vars(**kwargs):
def decorate(func):
for k in kwargs:
setattr(func, k, kwargs[k])
return func
return decorate
#static_vars(counter=0)
def clicked(event):
clicked.counter += 1

I don't know is this best way or not, but this code is worked
import tkinter as tk
class Main():
def __init__(self, root):
self.root = root
self.count = 0
frame = tk.Frame(self.root)
frame.pack()
btn = tk.Button(frame, text ='click me')
btn.pack()
btn.bind('<Button-1>', self.click)
self.lbl = tk.Label(frame, text = 'Count is 0')
self.lbl.pack()
def click(self, event):
self.count += 1
self.lbl.config(text=f'count {self.count}')
if __name__=="__main__":
root = tk.Tk()
Main(root)
root.mainloop()

Related

Tkinter is returning same value for every radio button?

I am popping up a custom dialog box using Tkinter.
I am opening it from another Tkinter window.
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
self.sub_root = Tk()
self.sub_root.title("Intovex")
self.sub_root.iconbitmap("Icon.ico")
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar()
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
c=1
print(names)
for i in names:
print(i)
r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
r.pack(anchor=W)
c+=1
self.var.set(1)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
def ask(self):
self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
var2.set(str(list.choice))
label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()
However, it works when it is run alone by directly instantiating the class and invoking ask() method.
You may have seen that I have print statements everywhere in the code(it is for debugging) and I found where isn't it working
The print statement to print the names list parameter is printing the whole list correctly.
Inside the for-loop also it prints the elements in the names list correctly
When I click on a radio button it invokes the end() method. There it always prints the default value.
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
self.sub_root = Tk()
self.sub_root.title("Intovex")
self.sub_root.iconbitmap("Icon.ico")
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar()
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
c=1
print(names)
for i in names:
print(i)
r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
r.pack(anchor=W)
c+=1
self.var.set(1)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
def ask(self):
self.sub_root.mainloop()
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
print(list.choice)
But it works if I open it as a TopLevel widget. But then the main window doesn't wait till the popup windows returns the value(choice).
The problem with the code in the first snippet is because you're calling Tk() more that once within the tkinter application — it confuses the interface code and can cause a variety of problems, as you're finding out.
If you replace the call inside the __init__() method of the ListDialog class, with one to tk.Toplevel() instead, then your code will start working.
I also streamlined the for loop that creates the Radiobuttons by changing it so to use the built-in enumerate() function to automatically keep a count of the names. In conjunction with that, I made the initial value of the IntVar zero which is not one of the values that selecting one the radiobuttons will assign it. This is so when the ListDialog is first displayed, none of them will be selected. It also ensures that the end() callback function will get called whenever the user presses one of them, so your app will always be informed when that happens.
Note that, although I didn't change it, you shouldn't name a variable list because that hides the name of the built-in class by that name. In general you should avoid naming anything something that conflicts with an existing standard Python name.
from tkinter import *
from tkinter.font import Font
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
# self.sub_root = Tk() # Wrong - don't call Tk() more than once.
root.withdraw() # Hide root window.
self.sub_root = Toplevel() # Create another top-level window.
self.sub_root.title("Intovex")
# self.sub_root.iconbitmap("Icon.ico") # I don't have this file...
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar(value=0) # Define and init value to one *not* produced by btns.
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
print(names)
for c, name in enumerate(names, start=1):
print(c)
r = Radiobutton(self.sub_root, text=c, variable=self.var, value=c,
command=self.end)
r.pack(anchor=W)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0",
fg="White", bd=0, width=12, pady=4, padx=4, height=1,
font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
root.deiconify() # Reshow root window.
def ask(self):
self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
var2.set(str(list.choice))
label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()

only last modification to Label being shown in Python - Tkinter . Why?

I am learning GUI development using Tkinter. I want to show multiple messages on the label which I have stored in a string. I used sleep to view the changes.However only the last message string is shown at execution.
from tkinter import *
import time
master = Tk()
def onClick():
for i in range(0,len(list_of_str)):
w.configure(text=list_of_str[i])
time.sleep(5)
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = onClick)
w.pack()
b.pack()
mainloop()
I am a noobie. So thanks for helping !
A simple solution to your problem is to use a combination of the try/except method and using after().
In tkinter sleep() will pause the application instead of providing a timer. For tkinter you want to use the after() method to scheduled an event after a set amount of time instead. The after() method is meant for this exact problem and is what you will always use in tkinter for a delayed event.
In my below example I modified your onClick function to take 1 argument and to use that in our after() method to select the next item in the list after 5 seconds. Note that for the after() method time is done in milliseconds so 5000 is 5 seconds.
from tkinter import *
master = Tk()
def onClick(ndex):
try:
w.configure(text=list_of_str[ndex])
master.after(5000, onClick, ndex+1)
except:
print("End of list")
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = lambda: onClick(0))
w.pack()
b.pack()
mainloop()
I think you want this:
from tkinter import *
import time
master = Tk()
global i
i = 0
def onClick():
master.after(1, change)
def change():
global i
if i == len(list_of_str):
pass
else:
w.configure(text=list_of_str[i])
i += 1
master.after(1000, onClick)
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = onClick)
w.pack()
b.pack()
mainloop()
time.sleep is a no-no in tkinter. I advise you make your gui in a class and it wil be easier.
example with class:
import tkinter as tk
from tkinter import *
class GUI:
def __init__(self, master):
self.list_of_str = ['first','second','third','fourth','fifth']
self.count = 0
self.master = master
self.w = Label(master, text="Hello, world!")
self.w.pack()
self.b = Button(master,text='Click me',command = self.onClick)
self.b.pack()
def onClick(self, event=None):
if self.count == len(self.list_of_str):
pass
else:
self.w.configure(text=self.list_of_str[self.count])
self.count += 1
self.master.after(1000, self.onClick)
def main():
root = tk.Tk()
app = GUI(root)
root.mainloop()
if __name__ == '__main__':
main()

How to retrieve the word before space from text widget of tkinter each time space is pressed

I am trying to retrieve the word before everytime space is entered. For example, If a user types " iam a" i want to retrieve "iam" and then if user types "iam a girl" i want to retrieve "a" .
Following is my code:
import tkinter as tk
import time
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
lsum = tk.Label(self,borderwidth=2,text="suggestions")
lsum.grid(row=2, column=5, sticky=tk.W)
def func3():
contents = self.text.get("1.0", tk.END)
lsum["text"] = contents
def func4():
self.text.bind("<space>",func3())
self.text= tk.Text(self, height=5, width=30,borderwidth=2)
self.text.pack(side=tk.RIGHT)
self.text.grid(row=0, column=4)
self.text.insert(tk.END, "")
self.v = tk.IntVar()
self.d = tk.IntVar()
self.e = tk.IntVar()
self.radio1=tk.Radiobutton(self, text="آيک گرآم مآڈل ", variable=self.v, value=1,command=func2).grid(column=0,row=1,columnspan=2)
self.radio2=tk.Radiobutton(self, text="دو گرآم مآڈل ", variable=self.d, value=2,command=func3).grid(column=0,row=2,columnspan=2)
self.radio3=tk.Radiobutton(self, text="تین گرآم مآڈل", variable=self.e, value=3).grid(column=0,row=3,columnspan=2)
if __name__ == '__main__':
run = ExampleApp()
run.mainloop()
Kindly Help please.
Assuming that I understand what you're asking (which is tricky enough) you can call .bind() on the Text widget with the event being "<space>".
This would look something like the below:
from tkinter import *
root = Tk()
text = Text(root)
text.pack()
def callback(*args):
print(text.get("1.0", END).split(" ")[len(text.get("1.0", END).split(" "))-1])
text.bind("<space>", callback)
root.mainloop()
This means that everytime the space key is pressed in the Text widget we get a callback which prints the word you're looking for in the widget.

Tkinter: all radio buttons are selected

Why in this code when clicking a button when a new window opens, all the radio buttons are selected?
class CodeButton:
def __init__(self, root):
self.btn = Button(root, text="Code",width=20, height=1,bg="white", fg="black")
self.btn.bind("<Button-1>", make_code_window)
self.btn.pack()
def make_code_window(event):
new_root = Toplevel()
new_root.minsize(width=300, height=300)
var = IntVar()
var.set(0)
for i in range(8):
Radiobutton(new_root, text=str(i), variable=var, value=i).pack()
def main():
root = Tk()
root.minsize(width=400, height=250)
CodeButton(root)
root.mainloop()
It's got something to do with storing the IntVar in a local variable in the function that will be discarded as soon as the make_code_window() function returns. You can fix the problem by making the IntVar an attribute of the new_root window widget, so it will exist at least as long as the widget using it does.
The code in your example isn't very realistic in the sense that typically one would want to use the current value of the IntVar for something somewhere else in the Python code, but that wouldn't be possible since it's only stored temporarily in local variable which exists only during the execution of the function that created it.
try:
from tkinter import *
except ImportError: # Python 2
from Tkinter import *
class CodeButton:
def __init__(self, root):
self.btn = Button(root, text="Code",width=20, height=1,bg="white", fg="black")
self.btn.bind("<Button-1>", make_code_window)
self.btn.pack()
def make_code_window(event):
new_root = Toplevel()
new_root.minsize(width=300, height=300)
var = new_root.var = IntVar() # changed
var.set(0)
for i in range(8):
Radiobutton(new_root, text=str(i), variable=var, value=i).pack()
def main():
root = Tk()
root.minsize(width=400, height=250)
CodeButton(root)
root.mainloop()
main()
(Following-up on the discussion we were having in the comments section of my other answer.)
Yes, passing the IntVar as an argument to the event handler function is a little tricky—in fact it's sometimes called The extra arguments trick. ;-)
Here's an example of applying it to your code:
try:
from tkinter import *
except ImportError: # Python 2
from Tkinter import *
class CodeButton:
def __init__(self, root):
self.btn = Button(root, text="Code",width=20, height=1,bg="white", fg="black")
self.btn.bind("<Button-1>",
# Extra Arguments Trick
lambda event, var=root.var: make_code_window(event, var))
self.btn.pack()
def make_code_window(event, var): # note added "var" argument
new_root = Toplevel()
new_root.minsize(width=300, height=300)
var.set(-99) # deselect by using value not associated with any RadioButtons
for i in range(8):
Radiobutton(new_root, text=str(i), variable=var, value=i).pack()
def main():
root = Tk()
root.minsize(width=400, height=250)
root.var = IntVar() # create it here to give access to it in the rest of your code
CodeButton(root)
root.mainloop()
main()

In Tkinter, How I disable Entry?

How I disable Entry in Tkinter.
def com():
....
entryy=Entry()
entryy.pack()
button=Button(text="Enter!", command=com, font=(24))
button.pack(expand="yes", anchor="center")
As I said How I disable Entry in com function?
Set state to 'disabled'.
For example:
from tkinter import *
root = Tk()
entry = Entry(root, state='disabled')
entry.pack()
root.mainloop()
or
from tkinter import *
root = Tk()
entry = Entry(root)
entry.config(state='disabled') # OR entry['state'] = 'disabled'
entry.pack()
root.mainloop()
See Tkinter.Entry.config
So the com function should read as:
def com():
entry.config(state='disabled')
if we want to change again and again data in entry box we will have to first convert into Normal state after changing data we will convert in to disable state
import tkinter as tk
count = 0
def func(en):
en.configure(state=tk.NORMAL)
global count
count += 1
count=str(count)
en.delete(0, tk.END)
text = str(count)
en.insert(0, text)
en.configure(state=tk.DISABLED)
count=int(count)
root = tk.Tk()
e = tk.Entry(root)
e.pack()
b = tk.Button(root, text='Click', command=lambda: func(e))
b.pack()
root.mainloop()

Categories