Related
With the help of the command button, I am able to disconnect the frame in Tkinter. But is there any way which helps to use the same button to start also?
import tkinter as tk
counter = 0
def counter_label(label):
def count():
global counter
counter+=1
label.config(text=counter)
label.after(1000, count)
count()
root = tk.Tk()
root.title("Counting Seconds")
label = tk.Label(root, fg="green")
label.pack()
counter_label(label)
button = tk.Button(root, text='Stop', width=25, command=root.destroy)
button.pack()
root.mainloop()
Suggestions will be grateful
You could simple use an if/else statement to check if the buttons text is Start or Stop then change a Boolean variable that controls the counter while also update the text on the button.
import tkinter as tk
counter = 0
active_counter = False
def count():
if active_counter:
global counter
counter += 1
label.config(text=counter)
label.after(1000, count)
def start_stop():
global active_counter
if button['text'] == 'Start':
active_counter = True
count()
button.config(text="Stop")
else:
active_counter = False
button.config(text="Start")
root = tk.Tk()
root.title("Counting Seconds")
label = tk.Label(root, fg="green")
label.pack()
button = tk.Button(root, text='Start', width=25, command=start_stop)
button.pack()
root.mainloop()
Here is an OOP example as well:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Counting Seconds")
self.counter = 0
self.active_counter = False
self.label = tk.Label(self, fg="green")
self.label.pack()
self.button = tk.Button(self, text='Start', width=25, command=self.start_stop)
self.button.pack()
def count(self):
if self.active_counter:
self.counter += 1
self.label.config(text=self.counter)
self.label.after(1000, self.count)
def start_stop(self):
if self.button['text'] == 'Start':
self.active_counter = True
self.count()
self.button.config(text="Stop")
else:
self.active_counter = False
self.button.config(text="Start")
if __name__ == "__main__":
App().mainloop()
This code is overly complicated (my answer), I suggest improving it. But it shows how one could use the same button for both start and stop as well as keeping most of your code.
import tkinter as tk
def counter_label(label):
a = 0
label.config(text=str(a))
def count():
nonlocal a
label.config(text=str(a))
a += 1
label.after(1000, count)
return count
def start_stop(root, btn_text, counter):
first = True
def call():
nonlocal first
if first:
counter()
first = False
btn_text.set('Stop')
else:
root.destroy()
return call
root = tk.Tk()
root.title("Counting Seconds")
label = tk.Label(root, fg="green")
label.pack()
counter = counter_label(label)
btn_text = tk.StringVar()
button = tk.Button(root, textvariable=btn_text, width=25, command=start_stop(root, btn_text, counter))
btn_text.set('Start')
button.pack()
root.mainloop()
Python 3.6.
This is a class i made for tkinter, where the text of self.compteur changes every second once i press start :
motsCount = 0
temps_total = 3600
class ChronoAspi:
def __init__(self, master):
self.master = master
self.mainframe = tkinter.Frame(self.master, background ='#28aae1')
self.mainframe.pack(fill = tkinter.BOTH, expand= True)
self.timer_text = tkinter.StringVar()
self.timer_text.trace('w', self.build_timer)
self.time_left = tkinter.IntVar()
self.time_left.set(temps_total)
self.running = True
self.buildGrid()
self.build_buttons()
self.build_timer()
self.build_compteur()
self.update()
def buildGrid(self):
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.rowconfigure(1, weight=1)
self.mainframe.rowconfigure(2, weight=0)
def build_buttons(self):
buttons_frame = tkinter.Frame(self.mainframe)
buttons_frame.grid(row=2, column=0, sticky='nsew', padx=10, pady=10)
buttons_frame.columnconfigure(0, weight=1)
buttons_frame.columnconfigure(1, weight=1)
self.start_button = tkinter.Button(buttons_frame, text='Start', command=self.start_timer )
self.stop_button = tkinter.Button(buttons_frame, text='Stop', command=self.stop_timer)
self.start_button.grid(row=0, column=0, sticky = 'ew')
self.stop_button.grid(row=0, column=1, sticky = 'ew')
self.stop_button.config(state=tkinter.DISABLED)
def build_timer(self, *args):
timer = tkinter.Label(self.mainframe, text=self.timer_text.get(), background = '#28aae1', fg='white', font=("Helvetica", 30))
timer.grid(row=1, column=0)
def build_compteur(self, *args):
self.compteur = tkinter.Label(self.mainframe, text='Aucun mot compté.', background = '#28aae1', fg='white', font=("Helvetica", 20))
self.compteur.grid(row=0, column=0)
def start_timer(self):
self.time_left.set(temps_total)
self.running = True
self.stop_button.config(state=tkinter.NORMAL)
self.start_button.config(state=tkinter.DISABLED)
def stop_timer(self):
self.running = False
self.stop_button.config(state=tkinter.DISABLED)
self.start_button.config(state=tkinter.NORMAL)
def heures_minutes_secondes(self, seconds):
return int(seconds/3600), int(seconds%3600/60), int(seconds%60)
def update(self):
global motsCount
time_left = self.time_left.get()
if self.running and time_left:
heure, minutes, seconds = self.heures_minutes_secondes(time_left)
self.timer_text.set('{:0>2}:{:0>2}:{:0>2}'.format(heure ,minutes, seconds) )
self.time_left.set(time_left-1)
motsCount += 1
else:
heure, minutes, seconds = self.heures_minutes_secondes(time_left)
self.timer_text.set( '{:0>2}:{:0>2}:{:0>2}'.format(heure ,minutes, seconds))
self.compteur['text'] = 'Compteur stoppé.'
self.stop_timer()
if motsCount > 0:
self.compteur['text'] = str(motsCount) + ' mots comptés.'
self.master.after(1000, self.update)
if __name__ == '__main__':
root = tkinter.Tk()
ChronoAspi(root)
root.mainloop()
As long as the chronometer is running, the text of self.compteurchanges every second. But when i hit the self.stop_button, self.running becomes False and the else part in the update() function is executed. So the chronometer stops but the self.compteur text doesn't change and I don't know why!
Sorry I can't comment but I think your if statement is gonna run after self.stopTimer returns and gonna change the text back
i have made a double countdown timer with python and tkinter but it seemed that it cannot be run if the tkinter window is not on the foreground and it cannot simultaneously run. This is my code:
import tkinter as tk
import tkinter.ttk as ttk
import time
class app:
def __init__(self):
self = 0
def mainGUIArea():
def count_down_1():
for i in range(79, -1, -1):
timeCount = "{:02d}:{:02d}".format(*divmod(i, 60))
time_str.set(timeCount)
root.update()
time.sleep(1)
def count_down_2():
for j in range(10, -1, -1):
timeCount = "{:02d}:{:02d}".format(*divmod(j, 60))
time_str1.set(timeCount)
root.update()
time.sleep(1)
#Main Window
root = tk.Tk()
root.title("Super Timer V1.0")
root.minsize(300,300)
root.geometry("500x300")
#Timer1
time_str = tk.StringVar()
label_font = ('Courier New', 40)
tk.Label(root, textvariable = time_str, font = label_font, bg = 'white', fg = 'blue', relief = 'raised', bd=3).pack(fill='x', padx=5, pady=5)
tk.Button(root, text=' Start Timer! ',bg='lightgreen',fg='black', command=count_down_1).pack()
tk.Button(root, text='Stop and Exit',bg='red',fg='white', command=root.destroy).pack()
#Timer2
time_str1 = tk.StringVar()
label_font = ('Courier New', 40)
tk.Label(root, textvariable = time_str1, font = label_font, bg = 'white', fg='blue', relief='raised', bd=3).pack(fill='x', padx=5, pady=5)
tk.Button(root, text=' Start Timer! ',bg='lightblue',fg='black', command=count_down_2).pack()
tk.Button(root, text='Stop and Exit',bg='red',fg='white', command=root.destroy).pack()
def main():
app.mainGUIArea()
main()
Do you have any suggestion? Thank You :)
The calls to time.sleep are at least part of the problem. When you call sleep it does literally that -- it puts the application to sleep. No events can be processed and the GUI freezes. This is the wrong way to do a countdown timer.
The other problem is the calls to update inside the loops alongside the calls to time.sleep. This call will process events, which means that when one of the loops is running and you click a button, you may end up calling the other function, interleaving your two loops.
The proper way to do something periodically is to use after to repeatedly call a function. The general pattern is this:
def update_display(self):
<do whatever code you want to update the display>
root.after(1000, self.update_display)
You can have as many of these running in parallel that you want (up to practical limits, obviously), and your GUI will be completely responsive between updates.
Here's a quick example:
class Countdown(tk.Label):
def __init__(self, parent):
tk.Label.__init__(self, parent, width=5, text="00:00")
self.value = 0
self._job_id = None
def tick(self):
self.value -= 1
text = "{:02d}:{:02d}".format(*divmod(self.value, 60))
self.configure(text=text)
if self.value > 0:
self._job_id = self.after(1000, self.tick)
def start(self, starting_value=60):
if self._job_id is not None: return
self.value = starting_value
self.stop_requested = False
self.after(1000, self.tick)
def stop(self):
self.after_cancel(self._job_id)
self._job_id = None
This is a simple tkinter(gui) stopwatch I made which works perfectly. Check it out
__author__ = 'Surya'
from tkinter import *
import time
class StopWatch(Frame):
def __init__(self, parent = None, ** kw):
Frame.__init__(self, parent, kw)
self._timeelapsed = 0.0
self._start = 0.0
self._run = 0
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
l = Label(self, textvariable=self.timestr)
self._setTime(self._timeelapsed)
l.pack(fill=X, expand=NO, pady=2, padx=2)
def _update(self):
self._timeelapsed = time.time() - self._start
self._setTime(self._timeelapsed)
self._timer = self.after(50, self._update)
def _setTime(self, elap):
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
def Start(self):
if not self._run:
self._start = time.time() - self._timeelapsed
self._update()
self._run = 1
def Stop(self):
if self._run:
self.after_cancel(self._timer)
self._timeelapsed = time.time() - self._start
self._setTime(self._timeelapsed)
self._run = 0
def Reset(self):
self._start = time.time()
self._timeelapsed = 0.0
self._setTime(self._timeelapsed)
def main():
root = Tk()
sw = StopWatch(root)
sw.pack(side=TOP)
Button(root, text='Start!', command=sw.Start).pack(side=LEFT)
Button(root, text='Stop!', command=sw.Stop).pack(side=LEFT)
Button(root, text='Reset!!!', command=sw.Reset).pack(side=LEFT)
Button(root, text='Quit!!!', command=root.quit).pack(side=LEFT)
root.mainloop()
if __name__ == '__main__':
main()
I am just learning python and jumping into classes. I have been using the following code for a simple stopwatch that I found. I now want to create a class with this code. I will have 1-4 timers on the screen at any one time. I need to start them all at the same time but end each timer independently. I am still plugging away at this but any help would be much appreciated.
import Tkinter as tk
def update_timeText():
if (state):
global timer
timer[2] += 1
if (timer[2] >= 100):
timer[2] = 0
timer[1] += 1
if (timer[1] >= 60):
timer[0] += 1
timer[1] = 0
timeString = pattern.format(timer[0], timer[1], timer[2])
timeText.configure(text=timeString)
root.after(10, update_timeText)
def start():
global state
state = True
def pause():
global state
state = False
def reset():
global timer
timer = [0, 0, 0]
timeText.configure(text='00:00:00')
def exist():
root.destroy()
state = False
root = tk.Tk()
root.wm_title('Simple Kitchen Timer Example')
timer = [0, 0, 0]
pattern = '{0:02d}:{1:02d}:{2:02d}'
timeText = tk.Label(root, text="00:00:00", font=("Helvetica", 150))
timeText.pack()
startButton = tk.Button(root, text='Start', command=start)
startButton.pack()
pauseButton = tk.Button(root, text='Pause', command=pause)
pauseButton.pack()
resetButton = tk.Button(root, text='Reset', command=reset)
resetButton.pack()
quitButton = tk.Button(root, text='Quit', command=exist)
quitButton.pack()
To create a class simply try this:
import Tkinter as tk
class StopWatch(object): # sub-class the object class maybe
def update_timeText(self):
#your code logic...
def start(self):
#your code logic...
#etc...
#the rest of your functions are the same
#just specify the 'self' Python keyword before any other arguments in the function. :D
Let me know if this works. :D
Here is my solution. It took me so long to get my head around the class creation of this. Any ideas for improving this?
import Tkinter as tk
root = tk.Tk()
root.wm_title('Stopwatch Class')
root.geometry("300x350")
class StopWatch(object):
def __init__(self):
self.pattern = '{0:02d}:{1:02d}:{2:02d}'
self.timer = [0, 0, 0]
self.state = False
self.timeText = tk.Label(root, text='00:00:01', font=("Helvetica", 15))
self.timeText.pack()
self.startButton = tk.Button(root, text='Start', command=self.start)
self.startButton.pack()
self.resetButton = tk.Button(root, text='Reset', command=self.reset)
self.resetButton.pack()
self.pauseButton = tk.Button(root, text='Pause', command=self.pause)
self.pauseButton.pack()
def update_timeText(self):
if (self.state):
self.timer[2] += 1
if (self.timer[2] >= 100):
self.timer[2] = 0
self.timer[1] += 1
if (self.timer[1] >= 60):
self.timer[0] += 1
self.timer[1] = 0
self.timeString = self.pattern.format(self.timer[0], self.timer[1], self.timer[2])
self.timeText.configure(text=self.timeString)
root.after(10, self.update_timeText)
def start(self):
self.state = True
def pause(self):
self.state = False
def reset(self):
self.timer = [0, 0, 0]
self.timeText.configure(text='00:00:00')
# create a new stopwatch
swatch1 = StopWatch()
swatch1.update_timeText()
# create a new stopwatch
swatch2 = StopWatch()
swatch2.update_timeText()
# create a new stopwatch
swatch3 = StopWatch()
swatch3.update_timeText()
root.mainloop()
I'm looking to add a timer for my simple math game. So far everything works just fine, the user gets questions when pressing the button and is given feedback on the answer. I want to add a timer for the user to see how much time it takes to answer the multiplication. This is the final part of my prototype to this mathgame. I want the timer to start when the user clicks "nytt tal" which means new number in swedish, and to stopp when the user clicks "svar" which means answer in swedish. Here is my code.
from Tkinter import *
import tkMessageBox
import random
import time
import sys
# Definition for the question asked to user
def fraga1():
global num3
num3 = random.randint(1, 10)
global num4
num4 = random.randint(1, 10)
global svar1
svar1 = num3 * num4
label1.config(text='Vad blir ' + str(num3) + '*' + str(num4) + '?')
entry1.focus_set()
#The answer giving feedback based on answer
def svar1():
mainAnswer = entry1.get()
if len(mainAnswer) == 0:
tkMessageBox.showwarning(message='Skriv in några nummer!')
return
if int(mainAnswer) != svar1:
tkMessageBox.showwarning(message='Tyvärr det rätta svaret: ' + str(svar1))
else:
tkMessageBox.showinfo(message='RÄTT!! :)')
#The quit button definition
def quit():
global root
root.destroy()
#Definition for the timer this part doesnt work
def start():
global count_flag
fraga1()
count_flag = True
count = 0.0
while True:
if count_flag == False:
break
label['text'] = str(count)
time.sleep(0.1)
root.update()
count += 0.1
#Window code
root = Tk()
root.title("multiplikations tidtagning")
root.geometry('800x500')
count_flag = True
# Welcome message in labels
label2 = Label(root, text="Hej!\n Nu ska vi lösa lite matteproblem!")
label2.config(font=('times', 18, 'bold'), fg='black', bg='white')
label2.grid(row=0, column=0)
#Instructions how to play in labels
label3 = Label(root, text="Instruktioner!\n För att starta ett spel tryck på nyttspel")
label3.config(font=('times', 12, 'bold'), fg='black', bg='white')
label3.grid(row=2, column=2)
#other label
label1 = Label(root)
label1.grid(row=2, column=0)
# entry widget for the start button
entry1 = Entry(root)
entry1.grid(row=3, column=0)
# restart gives a new question
entry1.bind('', func=lambda e:checkAnswer())
#Buttons
fragaBtn = Button(root, text='Nytt tal', command=fraga1)
fragaBtn.grid(row=4, column=0)
svarButton = Button(root, text='Svar', command=svar1)
svarButton.grid(row=4, column=1)
quit_bttn = Button(root, text = "Avsluta", command=quit)
quit_bttn.grid(row=5, column=0)
root.mainloop()
I think what you need is this .
from Tkinter import *
import time
class StopWatch(Frame):
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self._start = 0.0
self._elapsedtime = 0.0
self._running = 0
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
""" Make the time label. """
l = Label(self, textvariable=self.timestr)
self._setTime(self._elapsedtime)
l.pack(fill=X, expand=NO, pady=2, padx=2)
def _update(self):
""" Update the label with elapsed time. """
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._timer = self.after(50, self._update)
def _setTime(self, elap):
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
def Start(self):
""" Start the stopwatch, ignore if running. """
if not self._running:
self._start = time.time() - self._elapsedtime
self._update()
self._running = 1
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self.after_cancel(self._timer)
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._running = 0
def Reset(self):
""" Reset the stopwatch. """
self._start = time.time()
self._elapsedtime = 0.0
self._setTime(self._elapsedtime)
def main():
root = Tk()
sw = StopWatch(root)
sw.pack(side=TOP)
Button(root, text='Start', command=sw.Start).pack(side=LEFT)
Button(root, text='Stop', command=sw.Stop).pack(side=LEFT)
Button(root, text='Reset', command=sw.Reset).pack(side=LEFT)
Button(root, text='Quit', command=root.quit).pack(side=LEFT)
root.mainloop()
if __name__ == '__main__':
main()
P.S: Fit this in your code I just implemented the basic timer in tkinter.
well if you're using a tkinter you're use the function
object.after(100, defineFunction)
the first parent is the milisecond
this'll apply to python3.x but 2.7 i cant be sure since i dont pratice that format
Use a global variable storing current time when person presses start. Then when the user presses svar in your function svar, just fetch current time, substract them from one another and you get time taken, then reset the global var to 0 also and voila, you have the time taken
I can suggest additional solution with threading.
I hold a small timer in one of my projects and it run as a different thread and gets updated each second:
import threading
import tkinter as tk
import time
root = tk.Tk()
timer = tk.Text(root, width=5, height=1)
timer.insert(tk.INSERT, "00:00")
timer.pack()
def show_time():
start = time.time()
seconds = 0
while True:
if time.time() - start > 1:
seconds += int(time.time() - start)
start = time.time()
cur_index = timer.index(tk.INSERT)
cur_index = str(int(cur_index[0]) - 1) + cur_index[1:]
timer.delete(cur_index, tk.INSERT)
timer.insert(tk.INSERT, str(int(seconds / 60)) + ":" + str(seconds % 60))
root.update()
thread = threading.Thread(target=show_time)
thread.start()
I think you can use "after".
You create a widget...
time = 60 #60 seconds for example
widget = Tkinter.Label().pack()
def count():
global time
if time > 0:
time -= 1
widget.config(text="Time left: " + str(time))
widget.after(1000, count)
I use python 3x
Sorry