I'm coding a timer app, which has encountered a lot of difficulties, but one of the first ones has been in making a loop that can be broken by the press of a button. I've looked into it, and my research shows I should use threading, but I couldn't figure out how it would work.
What I decided to try, then, was to make an exception when I invoked a keyboard interrupt, and then make a button that calls that same interrupt. However, my current code refuses to interrupt when I ctrl-c.
My sample code looks like this
from Tkinter import *
from sys import exit
class Timer:
def __init__(self, master):
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command= quit)
buttonquit.grid(row = 1, column = 2)
global timertext
timertext = DoubleVar()
timertext.set(0)
display = Label(master, textvariable = timertext)
display.grid(row = 0, column = 0)
timertext.set(timertext)
def timerlogic(self):
pass
def pause(self):
pass
def start(self):
global timertext
try:
while True:
#do things
except KeyboardInterrupt:
print "Interrupted"
def lap(self):
pass
root = Tk()
app = Timer(root)
root.mainloop()
root.destroy()
Basically, I don't think my code as it stands is viable, but I don't know how to edit it to make a loop I can interrupt as needed.
You set some variable to True or False. Also, a while loop interrupts the Tkinter loop so Tkinter will do nothing until the while loop exits. Use Tkinter's after function instead.
from Tkinter import *
##from sys import exit
class Timer:
def __init__(self, master):
self.master=master
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command=self.quitit)
buttonquit.grid(row = 1, column = 2)
self.timertext = DoubleVar()
self.timertext.set(0)
display = Label(master, textvariable = self.timertext)
display.grid(row = 0, column = 0)
## timertext.set(timertext) ## Huh!!
self.timeit=False
def increment_timer(self):
ctr=int(self.timertext.get())
self.timertext.set(ctr+1)
if self.timeit:
self.master.after(500, self.increment_timer)
def start(self):
self.timeit=True
self.increment_timer()
def quitit(self):
self.timeit=False
root = Tk()
app = Timer(root)
root.mainloop()
Related
I'm trying to create a coundown timer using Tkinter in Python. The code works fine when I run without using Class but when I introduce class, for some reason the code is not running: the reset button does not make the first cell 00 and the 00 is not there by default to get started with. Thanks for the help!
#**--This is working--*
#Practicisng above Code#
#-------------------------------------------
import time
from tkinter import *
WinD = Tk()
WinD.geometry("300x350")
WinD.title("Time Counter")
Hours = StringVar()
Hours.set("00")
hoursEntry = Entry(WinD, width=3, font=("Cambria",18,""),
textvariable=Hours)
hoursEntry.place(x=80,y=20)
def reset():
#change the value of global variable
# global count
# count=1
Hours.set("00")
btn = Button(WinD, text='Reset', bd='5',
command= reset)
btn.place(x = 70,y = 120)
WinD.mainloop()
#**--This is not Working--**
#My Stopwatch Project
#importing req. modules
from tkinter import *
import sys
import time
global count
class Stopwatch():
def __init__(self):
#Create object of Tkinter, providing geometry and title
self.root = Tk()
self.root.title("Stop Watch")
self.root.geometry("600x200")
#Declaration of variables
self.hour = StringVar()
#Set the default value to 0
self.hour.set = ("00")
#Use of Entry class to take inputs from the user
self.hourEntry = Entry(self.root, width = 3, font = ("Arial", 18, ""), textvariable=self.hour)
self.hourEntry.place(x=80, y=20)
self.bt3 = Button(self.root, text = "Reset", command = self.reset, font=("Times", 12, "bold"), bg=("#DE1738"), bd=5)
self.bt3.place(x=320, y=100)
self.root.mainloop()
def reset(self):
#change the value of global variable
self.hour.set = ("00")
a = Stopwatch()
I would need a bit of help with my code I'm writing... and although when I start the code then comes directly such a window which asks me if I want to close it.
Does anyone have an idea where this could come from?
The code should open a window where I have some buttons to open some other windows with sliders to control DMX Lights.
Here is the code:
import tkinter as tk
from tkinter import ttk
from tkinter import *
from print_dict import pd
from time import sleep
from tkinter import messagebox
Pultdatas = {'DMXtype':512,
'columns':2,
'rows':8,
'slider':8,
'modes':4
}
root = Tk()
root.configure(background='gray')
root.title('DMX-Pult v2')
Screens = {'mainscreen':[root,{}]}
def on_closing(screen):
if messagebox.askokcancel("Quit", f"Do you want to quit Scanner {screen}?"):
Screens[screen][0].destroy()
def setmode(screen,mode):
print(f'setmode on screen {screen} to {mode}')
for i in Screens[screen][1]['modesel']:
print(i)
i.config(bg='gray')
Screens[screen][1]['modesel'][mode].config(bg='green')
def pressed(btn):
print('pressed Button: ' + str(btn))
if not Screens.__contains__(btn):
Screens[btn] = []
Screens[btn].append(Tk())
Screens[btn][0].configure(background='gray')
Screens[btn][0].title(f'Scanner {btn}')
Screens[btn].append({})
Screens[btn][1]['modesel'] = []
for i in range(Pultdatas['modes']):
Screens[btn][1]['modesel'].append(Button(Screens[btn][0], text=f"mode {i+1}", bg='gray', fg='white', command=lambda name = i:setmode(btn,name)))
Screens[btn][1]['modesel'][i].grid(row=i,column=0)
setmode(btn,0)
else:
if Screens[btn][0].winfo_exists() == 1:
sleep(0.2)
print('This window exist.')
Screens[btn][0].attributes('-topmost', True)
Screens[btn][0].update()
Screens[btn][0].attributes('-topmost', False)
else:
Screens[btn] = []
Screens[btn].append(Tk())
Screens[btn][0].configure(background='gray')
Screens[btn][0].title(f'Scanner {btn}')
Screens[btn].append({})
Screens[btn][1]['modesel'] = []
for i in range(Pultdatas['modes']):
Screens[btn][1]['modesel'].append(Button(Screens[btn][0], text=f"mode {i + 1}", bg='gray', fg='white',
command=lambda name=i: setmode(btn, name)))
Screens[btn][1]['modesel'][i].grid(row=i, column=0)
Screens[btn][0].protocol("WM_DELETE_WINDOW", lambda name = btn:on_closing(name))
setmode(btn, 0)
print()
def close():
if messagebox.askokcancel("Quit", f"Do you want to quit?"):
for screen in Screens:
print(f'closed {Screens[screen][0].title()} Succesfully')
Screens[screen][0].destroy()
tmpint = 0
Screens['mainscreen'][1]['Back'] = Button(root, text='close all',bg='gray',fg='white',command=close)
Screens['mainscreen'][1]['Back'].grid(row=0,column=0)
for column in range(Pultdatas['columns']):
for row in range(Pultdatas['rows']):
tmpint += 1
Screens['mainscreen'][1][tmpint] = Button(root, text=f"Scanner {tmpint}", bg='gray', fg='white', command=lambda name = tmpint:pressed(name))
Screens['mainscreen'][1][tmpint].grid(row=row+1,column=column)
pd(Screens)
root.protocol("WM_DELETE_WINDOW", close())
root.mainloop()
You can just change this line of code
root.protocol("WM_DELETE_WINDOW", close())
To this:
root.protocol("WM_DELETE_WINDOW", close)
You don't need to write (), we can just write handler not as function.
For more info you can visit here
I have a program that works with an user interface : I start it using a button and I would like to have the possibility to stop it during the process using the user interface:
The problem is : When I start the program, I cannot click on any buttons because when I drag the mouse in the tkinter window I have the "looping" / "waiting" cursor and it doesn't allow me to click on any buttons inside the interface.
I tried to start the infinit loop function in a new thread, but I don't know if it is really a good way to solve my problem... Any of you have any solutions ?
I try to simulate my problem with a simple code :
from tkinter import *
import loop
window = Tk()
window.title("test")
start = Button(window, text="start", bg ="green", command=loop.start)
stop = Button(window, text="stop", bg ="green", command=loop.stop)
thread = Button(window, text="threads", bg ="green", command=loop.how_many_threads)
start.grid(column = 1, row = 1)
stop.grid(column = 2, row = 1)
thread.grid(column = 3 , row = 1)
window.mainloop()
and few functions :
import threading
continu = True
def infinit(x):
cpt = 0
while(x):
cpt += 1
#print(cpt)
print("loop is over !")
def start():
threadSimulation = threading.Thread(target= infinit(continu)).start()
def stop():
self.continu = False
def how_many_threads():
for t in threading.enumerate():
print("thread names : ",t.getName())
Here's how to fix your implementation. The biggest error is that you have to provide arguments to the thread in the args part, not like a normal function call.
import tkinter as tk
import threading
import time
def infinit(x):
cpt = 0
while(continu):
cpt += 1
print(cpt)
time.sleep(1)
print("loop is over !")
def start():
threadSimulation = threading.Thread(target= infinit, args=(continu,))
threadSimulation.daemon = True
threadSimulation.start()
def stop():
global continu
continu = False
def how_many_threads():
for t in threading.enumerate():
print("thread names : ",t.getName())
continu = True
window = tk.Tk()
window.title("test")
start = tk.Button(window, text="start", bg ="green", command=start)
stop = tk.Button(window, text="stop", bg ="green", command=stop)
thread = tk.Button(window, text="threads", bg ="green", command=how_many_threads)
start.grid(column = 1, row = 1)
stop.grid(column = 2, row = 1)
thread.grid(column = 3 , row = 1)
window.mainloop()
If you want to close the program, you can use "window.quit" in the command section of your button like this:
stop = Button(window, text="stop", bg ="green", command=window.quit)
Trying a scenario where in I wanted to proceed in the for loop based on threading mechanism [for syncing/wait mechanism] as shown in the code below.
#!/tools/bin/python
# Global Import Variables
import Tkinter
from Tkinter import *
import subprocess
import shlex
import os
from PIL import Image, ImageTk
import time
import string
import threading
class test_template:
def __init__(self, master):
self.master = master
self.next_button = None
self.fruit_lbl = None
self.yes_button = None
self.no_button = None
self.yes_no_button_done = threading.Event()
# For Loop Goes From 0 To 10
for i in range (10):
if not (self.fruit_lbl):
self.fruit_lbl = Label(root, text="Do You Want To Change The Color Of Fruit %d"%i)
self.fruit_lbl.grid(row=1, column=0)
if not (self.yes_button):
self.yes_button = Button(root, background="lawn green", activebackground="forest green", text="YES", command=self.yes_button_code).grid(row=2, column=1)
if not (self.no_button):
self.no_button = Button(root, background="orange red", activebackground="orangered3", text="NO", command=self.no_button_code).grid(row=2, column=2)
while not self.yes_no_button_done.isSet():
self.yes_no_button_done.wait()
def yes_button_code(self):
print "Inside YES Button Config"
def no_button_code(self):
print "Inside No Button Config"
self.yes_no_button_done.set()
# Top Local Variables
root = Tk()
#root.configure(background='black')
# Top Level Default Codes
my_gui = test_template(root)
root.mainloop()
Its like the for loop needs to wait until I press either Yes Or No, until then the while loop should be waiting for the event set.
But somehow the threading/wait mechanism is not working fine i.e. as soon I launch the code, its goes into the while loop and goes haywire. Am I missing anything on the above code ? Share in your comments !!
If I have undertood you correctly I dont think that you need to use threading at all. Consider the following:
from Tkinter import *
class test_template:
def __init__(self, master):
self.master = master
self.loop_count = IntVar()
self.loop_count.set(1)
l = Label(self.master, text='Change colour of fruit?')
l.pack()
count = Label(self.master, textvariable = '%s' % self.loop_count)
count.pack()
yes = Button(self.master, text = 'YES', command = self.loop_counter)
yes.pack()
no = Button(self.master, text = 'NO', command = self.loop_counter)
no.pack()
def loop_counter(self):
if self.loop_count.get() == 10:
print 'Finished'
self.master.quit()
else:
self.loop_count.set(self.loop_count.get()+1)
print self.loop_count.get()
root = Tk()
GUI = test_template(root)
root.mainloop()
Let me know what you think.
Cheers,
Luke
My Senior Project involves a robot I can control over wifi. I am using a Raspberry Pi and a Tkinter window to send commands to the robot. I have the rough draft of my Tkinter window, but I am wondering if there is a way to bind the button press to the arrow keys. That way I can control the robot by using my arrow keys rather than clicking on each button. Here is my code, what would I have to add?
Code:
from Tkinter import *
message = ""
class App:
def __init__(self, master):
frame=Frame(master)
frame.grid()
status = Label(master, text=message)
status.grid(row = 0, column = 0)
self.leftButton = Button(frame, text="<", command=self.leftTurn)
self.leftButton.grid(row = 1, column = 1)
self.rightButton = Button(frame, text=">", command=self.rightTurn)
self.rightButton.grid(row = 1, column = 3)
self.upButton = Button(frame, text="^", command=self.upTurn)
self.upButton.grid(row = 0, column = 2)
self.downButton = Button(frame, text="V", command=self.downTurn)
self.downButton.grid(row=2, column = 2)
def leftTurn(self):
message = "Left"
print message
def rightTurn(self):
message = "Right"
print message
def upTurn(self):
message = "Up"
print message
def downTurn(self):
message = "Down"
print message
root = Tk()
root.geometry("640x480")
root.title("Rover ")
app = App(root)
root.mainloop()
I believe what you want is to bind the key press to the frame/function. Tkinter has its own event and binding handling built in which you can read up on here.
Here is quick example which you should be able to adapt your program.
from tkinter import *
root = Tk()
def yourFunction(event):
print('left')
frame = Frame(root, width=100, height=100)
frame.bind("<Left>",yourFunction) #Binds the "left" key to the frame and exexutes yourFunction if "left" key was pressed
frame.pack()
root.mainloop()