tkinter countdown app stop and reset button not working - python

I have written a first attempt at a tkinter countdown app and I'm struggling with the stop and reset buttons as well as parts of the interface design.
My stop button does nothing at all whereas my reset button resets the values, but then continues the countdown immediately. I suspect it's because I have set self.running to True in the init method and that my app loops through those lines again and again. How do I prevent that from happening?
The other issue is that my width argument in all of my entry fields gets seemingly ignored. How can I assign the same width to all three fields?
Thanks for your help.
from tkinter import *
import time
from tkinter import messagebox
class Application(Frame):
def __init__(self,master):
super(Application,self).__init__(master)
self.place()
self.widgets()
self.running = True
def widgets(self):
self.hour = StringVar()
self.hour.set("00")
# Why is the width ignored?
self.hour_entry = Entry(width = 7, font=("Calibri",14,""), textvariable = self.hour)
self.hour_entry.place(x = 10, y = 10)
self.minute = StringVar()
self.minute.set("00")
self.minute_entry = Entry(width = 7, font=("Calibri",14,""), textvariable = self.minute)
self.minute_entry.place(x = 30, y = 10)
self.second = StringVar()
self.second.set("00")
self.seconds_entry = Entry(width = 7, font=("Calibri",14,""), textvariable = self.second)
self.seconds_entry.place(x = 50, y = 10)
self.start_btn = Button(text = "Start", command=self.clock)
self.start_btn.place(x = 30, y = 100)
self.stop_btn = Button(text = "Stop", command=self.stop)
self.stop_btn.place(x = 70, y = 100)
self.reset_btn = Button(text = "Reset", command=self.reset)
self.reset_btn.place(x = 110, y = 100)
def clock(self):
if self.running == True:
self.time_total = int(self.hour_entry.get())*3600 + int(self.minute_entry.get())*60 + int(self.seconds_entry.get())
while self.time_total > -1:
# returns 3600/60 = 60 with 0 left: so that's 60 min, 0 seconds
self.mins, self.secs = divmod(self.time_total,60)
self.hours = 0
if self.mins > 60:
self.hours, self.mins = divmod(self.mins, 60)
self.hour.set("{0:02d}".format(self.hours))
self.minute.set("{0:02d}".format(self.mins))
self.second.set("{0:02d}".format(self.secs))
self.time_total -= 1
root.update()
time.sleep(1)
if self.time_total == 0:
messagebox.showinfo("Time Countdown", "Time's up!")
def start(self):
self.running = True
self.clock()
def stop(self):
self.running = False
def reset(self):
self.running = False
self.hour.set("00")
self.minute.set("00")
self.second.set("00")
if __name__ == '__main__':
root = Tk()
app = Application(root)
mainloop()

For the timer, use the after method instead of sleep.
The entry widths are being used, but the place method puts one entry on top of another.
Also - Call the start method from the button click to reset the running flag.
Try this code:
from tkinter import *
import time
from tkinter import messagebox
class Application(Frame):
def __init__(self,master):
super(Application,self).__init__(master)
self.place()
self.widgets()
self.running = False
def widgets(self):
self.hour = StringVar()
self.hour.set("00")
# Why is the width ignored?
self.hour_entry = Entry(width = 4, font=("Calibri",14,""), textvariable = self.hour)
self.hour_entry.place(x = 10, y = 10)
self.minute = StringVar()
self.minute.set("00")
self.minute_entry = Entry(width = 4, font=("Calibri",14,""), textvariable = self.minute)
self.minute_entry.place(x = 60, y = 10)
self.second = StringVar()
self.second.set("00")
self.seconds_entry = Entry(width = 4, font=("Calibri",14,""), textvariable = self.second)
self.seconds_entry.place(x = 110, y = 10)
self.start_btn = Button(text = "Start", command=self.start)
self.start_btn.place(x = 30, y = 100)
self.stop_btn = Button(text = "Stop", command=self.stop)
self.stop_btn.place(x = 70, y = 100)
self.reset_btn = Button(text = "Reset", command=self.reset)
self.reset_btn.place(x = 110, y = 100)
def clock(self):
if self.running == True:
# returns 3600/60 = 60 with 0 left: so that's 60 min, 0 seconds
self.mins, self.secs = divmod(self.time_total,60)
self.hours = 0
if self.mins > 60:
self.hours, self.mins = divmod(self.mins, 60)
self.hour.set("{0:02d}".format(self.hours))
self.minute.set("{0:02d}".format(self.mins))
self.second.set("{0:02d}".format(self.secs))
root.update()
#time.sleep(1)
root.after(1000, self.clock) # wait 1 second, re-call clock
if self.time_total == 0:
self.running = False
messagebox.showinfo("Time Countdown", "Time's up!")
self.time_total -= 1
def start(self):
self.time_total = int(self.hour_entry.get())*3600 + int(self.minute_entry.get())*60 + int(self.seconds_entry.get())
self.running = True
self.clock()
def stop(self):
self.running = False
def reset(self):
self.running = False
self.hour.set("00")
self.minute.set("00")
self.second.set("00")
if __name__ == '__main__':
root = Tk()
app = Application(root)
mainloop()

Related

What is the fastest Method to display an Image in a Frame?

I use 2 different Threads to Capture and to Display a Webcamstream.
I checked the FPS in the Capture and in the Display Function.
While the Capture Function can deliver full FPS, the Display Function is slower and getting slower with higher Resolutions.
Is there a faster Way to display the (now synced)Webcamstream in a TKinter Frame?
My goal would be getting synced 20 FPS in Display (and later in Record) after Resizing.
My Testprogramm:
global myfps
myfps = 0
global my_width
my_width = 640
global my_height
my_height = 480
def fakefunc():
_=7
def display_start():
print('display_start activated')
gv.test_wiedergabe = 'True'
mts = multi_thread_stream(ready)
def display_stop():
print('display_stop activated')
gv.test_wiedergabe = 'False'
def aufnahme_start():
aufnahme = 'True'
class multi_thread_stream:
def __init__(self, ready=None):
self.ready = ready
self.cap = cv2.VideoCapture(0)
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)
self.AIM_FPS = 1/25
self.AIM_FPS_MS = int(self.AIM_FPS * 1000)
self.cap.set(3, my_width)
self.cap.set(4, my_height)
self.frame = {}
self.fps = 0
self.wiedergabe = 'False'
#Create the Threads
self.t1 = threading.Thread(target=self.capture_stream)
self.t2 = threading.Thread(target=self.display_image)
self.t1.name = 'capture_thread'
self.t2.name = 'display_thread'
#die starts in eine funktion packen, für start und stop ?
self.t1.start()
self.t2.start()
#self.t1.join()
#self.t2.join()
self.aufnahme = 'False'
frames_per_second = 20
basepathinc = r'C:/Videotool/Videos'
isExist = os.path.exists(basepathinc)
print('path exists :',isExist)
print('video fullpath :',basepathinc)
if not isExist:
os.makedirs(basepathinc)
print(f'path {basepathinc} created')
os.chdir(basepathinc)
def set_res(key):
global my_width
global my_height
match key:
case 480:
my_width = 640
my_height = 480
print('480p selected')
case 720:
my_width = 1280
my_height = 720
print('720p selected')
case 1080:
my_width = 1920
my_height = 1080
print('1080p selected')
case 4:
my_width = 3840
my_height = 2160
print('4k selected')
case _:
print('wrong selection')
def capture_stream(self):
capture_counter = 1
old_seconds = 0
seconds = 0
while True:
self.ret, self.frame = self.cap.read()
self.ready.set()
now = datetime.now()
seconds = int(now.strftime("%S"))
#print(f'Frame: {capture_counter} in Second :{seconds}')
if seconds > old_seconds:
print(f'Captured Frames: {capture_counter-1} in Second : {old_seconds}')
capture_counter = 2
else:
capture_counter += 1
old_seconds = seconds
time.sleep(self.AIM_FPS)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
def display_image(self):
self.start_time = time.time()
self.framerate_trigger = 1
self.fps_counter = 0
display_counter = 0
old_seconds = 0
seconds = 0
while True:
# Display the resulting frame
if gv.test_wiedergabe == 'True':
self.ready.wait()
#_________________________________
now = datetime.now()
seconds = int(now.strftime("%S"))
if seconds > old_seconds:
print(f'Displayed Frames: {display_counter-1} in Second : {old_seconds}')
display_counter = 2
else:
display_counter += 1
old_seconds = seconds
#_________________________________
framexd=cv2.cvtColor(self.frame,cv2.COLOR_BGR2RGB)
self.ready.clear()
self.fps_counter+=1
if (time.time() - self.start_time) > self.framerate_trigger :
#print("FPS: ", self.fps_counter / (time.time() - self.start_time))
self.fps = round(int(self.fps_counter / (time.time() - self.start_time)))
self.fps_counter = 0
self.start_time = time.time()
global myfps
myfps = self.fps
stream_width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
stream_height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
global_cam_display_fps = int(self.cap.get(cv2.CAP_PROP_FPS))
#print('FPS:',self.fps)
fps_x_string = str(self.fps)
font = cv2.FONT_HERSHEY_TRIPLEX
fontScale = 1
fontColor = (255,0,0)
thickness = 2
lineType = 2
frame_wfps = cv2.putText(framexd,fps_x_string, (0,30), font, fontScale,fontColor,thickness,cv2.LINE_AA)
framexd=Image.fromarray(framexd)
#framexd=Image.fromarray(framexd)
framexd=ImageTk.PhotoImage(framexd)
videolabel.configure(image=framexd)
videolabel.image=framexd
infolabel.configure(text = f'myFPS: {myfps} /n Auflösung: {stream_width}x{stream_height}')
#Aufnahme________________________________________________________________________________________
"""
if self.aufnahme == 'True':
self.out.write(frame_wtext_startup)
else:
self.out.release()
"""
#________________________________________________________________________________________________
if cv2.waitKey(1) & 0xFF == ord('q'):
break;
else:
self.cap.release()
#self.t1._stop() funkst erst wenn der thread daemonisch ist! also thread daemonisch öffnen
#self.t2._stop()
break
def __del__(self):
# When everything done, release the capture
self.cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
print('start.py was started directly')
ready = threading.Event()
ready = ready
root = Tk()
root.title('Test Videoverarbeitung')
root.geometry('800x600')
helv36 = font.Font(family='Helvetica', size=12, weight=font.BOLD)
mainframe = Frame(root)
mainframe.place(relheight = 1, relwidth = 1)
videoframe = Frame(mainframe)
videoframe.place(x = 0, y = 0, relheight = 1, relwidth = 0.8)
videolabel = Label(videoframe, text = "Videoframe")
videolabel.place(relx = 0.5, rely = 0.5, relwidth = 1, relheight = 1, anchor="center")
infolabel = Label(videoframe, text = "Videoframe")
infolabel.place(relx = 0, rely = 0.8, relwidth = 1, relheight = 0.2)
controlframe = Frame(mainframe)
controlframe.place(relx = 0.8, y = 0, relheight = 1, relwidth = 0.2)
button1 = Button(controlframe, width=50, text = "Wiedergabe", command=lambda:display_start(), bd = 2, relief = "groove", overrelief = "sunken", font = helv36)
button1.place(relx = 0, rely = 0, relheight=0.2, relwidth = 1)
button2 = Button(controlframe, width=50, text = "Wiedergabe Stop", command=lambda:display_stop(), bd = 2, relief = "groove", overrelief = "sunken", font = helv36)
button2.place(relx = 0, rely =0.2, relheight=0.2, relwidth = 1)
button3 = Button(controlframe, width=50, text = "Aufnahme", command=lambda:aufnahme_start(), bd = 2, relief = "groove", overrelief = "sunken", font = helv36)
button3.place(relx = 0, rely =0.4, relheight=0.2, relwidth = 1)
button3 = Button(controlframe, width=50, text = "Aufnahme Stop (oF)", command=lambda:fakefunc(), bd = 2, relief = "groove", overrelief = "sunken", font = helv36)
button3.place(relx = 0, rely =0.6, relheight=0.2, relwidth = 1)
checkbutton1 = Checkbutton(controlframe, text = '480p',command=lambda:multi_thread_stream.set_res(480))
checkbutton1.place(relx = 0, rely = 0.8)
checkbutton1 = Checkbutton(controlframe, text = '720p',command=lambda:multi_thread_stream.set_res(720))
checkbutton1.place(relx = 0, rely = 0.84)
checkbutton1 = Checkbutton(controlframe, text = '1080p',command=lambda:multi_thread_stream.set_res(1080))
checkbutton1.place(relx = 0, rely = 0.88)
checkbutton1 = Checkbutton(controlframe, text = '4k',command=lambda:multi_thread_stream.set_res(4))
checkbutton1.place(relx = 0, rely = 0.92)
root.mainloop()
else:
_=7
I am through many Threads about this, but didnt find a Solution.
I created this as my minimal Code Example, to prevent the "use Threads" Suggestion.
But to cut out the Part I would like to improve:
framexd=cv2.cvtColor(self.frame,cv2.COLOR_BGR2RGB)
self.ready.clear()
framexd=Image.fromarray(framexd)
framexd=ImageTk.PhotoImage(framexd)
videolabel.configure(image=framexd)
videolabel.image=framexd
This Part is working up to 50% slower then the Capture.
I would like to speed it up with other Ideas like Reducing Resolution and the Use of better Hardware.
Next Time I will avoid the bizarre Capitalizations in my Code.

Switching between frames in tkinter, clock app

Good day, guys. I've got a problem with switching between frames in tkinter. I'm trying to make a clock app with time, stopwatch, timer and alarm. If I click on the first frame it's working, but if I select the second frame it's breaks. The previous frame just don't open because the second frame refreshing too fast (lbl.after). Can you help me with switching between frames?
from tkinter import *
from tkmacosx import *
from time import strftime
root = Tk()
root.title('Time')
root.geometry('500x500')
root.resizable(False, False)
counter = -1
running = False
def time_frame():
string = strftime("%H:%M:%S %A %d %Y")
frame_time = Frame(root, width = 480, height = 410, bg = 'gray70')
frame_time.place(x = 10, y = 80)
lbl1 = Label(frame_time, text = list, width = 32, height = 10, font = ('Arial', 25))
lbl1.place(x = 10, y = 80)
lbl1.config(text=string)
lbl1.after(1000, time_frame)
def stopwatch_frame():
frame_stopwatch = Frame(root, width = 480, height = 410, bg = 'gray70')
frame_stopwatch.place(x = 10, y = 80)
lbl2 = Label(frame_stopwatch, text = '0', width = 25, height = 5, font = ('Arial', 25))
lbl2.place(x = 10, y = 80)
start_button = Button(frame_stopwatch, width = 80, height = 40, text = 'Start')
start_button.place(x = 10, y = 240)
stop_button = Button(frame_stopwatch, width = 80, height = 40, text = 'Stop')
stop_button.place(x = 100, y = 240)
reset_button = Button(frame_stopwatch, width = 80, height = 40, text = 'Reset')
reset_button.place(x = 190, y = 240)
lbl2.after(1, stopwatch_frame)
frame_top = Frame(root, width = 500, height = 70, bg = 'gray64')
frame_top.place(x = 0, y = 0)
time_btn = Button(frame_top, text = 'Time', width = 50, height = 50, command = time_frame)
time_btn.place(x = 10, y = 10)
stopwatch_btn = Button(frame_top, text = 'Stopwatch', width = 50, height = 50, font = ('Arial', 9), command = stopwatch_frame)
stopwatch_btn.place(x = 80, y = 10)
timer_btn = Button(frame_top, text = 'Timer', width = 50, height = 50)
timer_btn.place(x = 150, y = 10)
alarm_btn = Button(frame_top, text = 'Alarm', width = 50, height = 50)
alarm_btn.place(x = 220, y = 10)
root.mainloop()
Here is a simplified example of a clock and stop watch class.
import tkinter as tk
from tkmacosx import *
from time import strftime, process_time
class timer:
delaya = None
delayb = None
def __init__( self ):
self.root = tk.Tk()
self.root.title('Time')
self.buttona = tk.Button( self.root, text = 'clock', width = 40, command = self.setclock )
self.buttonb = tk.Button( self.root, text = 'watch', width = 40, command = self.setwatch )
self.buttona.grid( row=0, column=0, sticky='ew' )
self.buttonb.grid( row=0, column=1, sticky='ew' )
self.root.update_idletasks()
self.root.resizable( False, False )
def clock( self ):
self.root.after_cancel( self.delaya )
self.delaya = None
self.buttona[ 'text' ] = strftime("%H:%M:%S %A %d %Y")
self.delaya = self.root.after( 1000, self.clock )
def watch( self ):
self.root.after_cancel( self.delayb )
self.delayb = None
self.buttonb[ 'text' ] = f'{process_time():0.5f}'
self.delayb = self.root.after( 1, self.watch )
def setclock( self ):
if self.delaya == None:
self.delaya = self.root.after( 1000, self.clock )
else:
self.root.after_cancel( self.delaya )
self.delaya = None
self.buttona[ 'text' ] = 'clock'
def setwatch( self ):
if self.delayb == None:
self.delayb = self.root.after( 1, self.watch )
else:
self.root.after_cancel( self.delayb )
self.delayb = None
self.buttonb[ 'text' ] = 'watch'
if __name__ == '__main__':
device = timer( )
tk.mainloop()

time.sleep skips multiple lines in my script

def fctCountdown():
t = 3
countdown = Label(Pong, text = "3", font = ("Fixedsys", 30), bg = "#CB997E", fg = 'black')
countdown.place(x = 387, y = 300)
while t >= 0:
if t == 2:
countdown.config(text = "2")
if t == 1:
countdown.config(text = "1")
if t == 0:
countdown.config(text = "Start !")
sleep(1)
t -= 1
countdown.place_forget()
def fctGoDown():
global x1, y1
def fctGameMenu():
background_image = PhotoImage(master = Pong, file = "fondpong0.png")
background_label = Label(Pong, image = background_image)
background_label.place(x = 0, y = 0)
background_label.image = background_image
def fctStartGame(evt):
global state
if state == 0:
state = 1
fctGameMenu()
sleep(1)
fctCountdown()
Hi guys ! I'm asking you for help because the sleep(1) line in fctStartGame is executed before the fctGameMenu() and i don't understand why. Because of it my countdown doesn't work.

Python Tkinter Entry not placing

In Croatia we have competitions where you make apps. I'm making an app to help 1st and 2nd graders learn and revise math. I just started recently so it doesn't have almost anything.
The problem is that when variable: mode = "number" , entries aren't placed.
Btw when opening the app you will be presented with 4 options.
They are addition, subtraction, multiplication and division.
I translated it from Croatian to English so you can understand.
When you run the program press the Addition button.
Then you will see the task but entries missing.
If you change the value of the variable mode to mode = "result", entries are placed.
I tried everything but couldn't get it to work.
Here's the code:
import tkinter as tk
import random
wn = tk.Tk()
wn.config(width = 550, height = 500)
wn.resizable(False, False)
wn.title("Learn and Revise")
number1_1 = 0
number1_2 = 0
number2_1 = 0
number2_2 = 0
number3_1 = 0
number3_2 = 0
number4_1 = 0
number4_2 = 0
number5_1 = 0
number5_2 = 0
def makeRandomNumbers():
global number1_1, number1_2
global number2_1, number2_2
global number3_1, number3_2
global number4_1, number4_2
global number5_1, number5_2
if mode == "number":
while True:
number1_1 = random.randint(0, 10)
number1_2 = random.randint(0, 10)
if number1_1 > number1_2:
pass
else:
break
while True:
number2_1 = random.randint(0, 10)
number2_2 = random.randint(0, 10)
if number2_1 > number2_2:
pass
else:
break
while True:
number3_1 = random.randint(0, 10)
number3_2 = random.randint(0, 10)
if number3_1 > number3_2:
pass
else:
break
while True:
number4_1 = random.randint(0, 10)
number4_2 = random.randint(0, 10)
if number4_1 > number4_2:
pass
else:
break
while True:
number5_1 = random.randint(0, 10)
number5_2 = random.randint(0, 10)
if number5_1 > number5_2:
pass
else:
break
elif mode == "result":
number1_1 = random.randint(0, 10)
number1_2 = random.randint(0, 10)
number2_1 = random.randint(0, 10)
number2_2 = random.randint(0, 10)
number3_1 = random.randint(0, 10)
number3_2 = random.randint(0, 10)
number4_1 = random.randint(0, 10)
number4_2 = random.randint(0, 10)
number5_1 = random.randint(0, 10)
number6_2 = random.randint(0, 10)
def placeTasks(oper):
global operation
operation = oper
makeTasks()
wipeMenu()
button_check.place(x = 310, y = 225)
label1.place(x = 150, y = 125)
label2.place(x = 150, y = 175)
label3.place(x = 150, y = 225)
label4.place(x = 150, y = 275)
label5.place(x = 150, y = 325)
if mode == "number":
entry1.place(x = 240, y = 130)
entry2.place(x = 240, y = 180)
entry3.place(x = 240, y = 230)
entry4.place(x = 240, y = 280)
entry5.place(x = 240, y = 330)
elif mode == "result":
entry1.place(x = 240, y = 130)
entry2.place(x = 240, y = 180)
entry3.place(x = 240, y = 230)
entry4.place(x = 240, y = 280)
entry5.place(x = 240, y = 330)
task1 = 0
task2 = 0
task3 = 0
task4 = 0
task5 = 0
def makeTasks():
global task1, task2, task3, task4, task5
global label1, label2, label3, label4, label5
makeRandomNumbers()
operation_sign = ""
if operation == "addition":
operation_sign = '+'
elif operation == "subtraction":
operation_sign = '-'
elif operation == "multiplication":
operation_sign = '•'
elif operation == "division":
operation_sign = '÷'
if mode == "result":
task1 = "{} {} {} =".format(number1_1, operation_sign, number1_2)
task2 = "{} {} {} =".format(number2_1, operation_sign, number2_2)
task3 = "{} {} {} =".format(number3_1, operation_sign, number3_2)
task4 = "{} {} {} =".format(number4_1, operation_sign, number4_2)
task5 = "{} {} {} =".format(number5_1, operation_sign, number5_2)
elif mode == "number":
task1 = "{} {} = {}".format(number1_1, operation_sign, number1_2)
task2 = "{} {} = {}".format(number2_1, operation_sign, number2_2)
task3 = "{} {} = {}".format(number3_1, operation_sign, number3_2)
task4 = "{} {} = {}".format(number4_1, operation_sign, number4_2)
task5 = "{} {} = {}".format(number5_1, operation_sign, number5_2)
label1 = tk.Label(wn, text = task1, font = ("Arial", 15))
label2 = tk.Label(wn, text = task2, font = ("Arial", 15))
label3 = tk.Label(wn, text = task3, font = ("Arial", 15))
label4 = tk.Label(wn, text = task4, font = ("Arial", 15))
label5 = tk.Label(wn, text = task5, font = ("Arial", 15))
operation = ""
mode = "number"
button_check = tk.Button(wn, width = 20, text = "Check")
label1 = tk.Label(wn, text = task1, font = ("Arial", 15))
entry1 = tk.Entry(wn, width = 7)
label2 = tk.Label(wn, text = task2, font = ("Arial", 15))
entry2 = tk.Entry(wn, width = 7)
label3 = tk.Label(wn, text = task3, font = ("Arial", 15))
entry3 = tk.Entry(wn, width = 7)
label4 = tk.Label(wn, text = task4, font = ("Arial", 15))
entry4 = tk.Entry(wn, width = 7)
label5 = tk.Label(wn, text = task5, font = ("Arial", 15))
entry5 = tk.Entry(wn, width = 7)
def placeMenu():
menu_label1.place(x = 175, y = 75)
button1.place(x = 200, y = 150)
button2.place(x = 200, y = 200)
button3.place(x = 200, y = 250)
button4.place(x = 200, y = 300)
def wipeMenu():
menu_label1.destroy()
button1.destroy()
button2.destroy()
button3.destroy()
button4.destroy()
menu_label1 = tk.Label(wn, text = "Revise", font = ("Arial", 35))
button1 = tk.Button(wn, width = 20, text = "Addition", command = lambda: placeTasks("addition"))
button2 = tk.Button(wn, width = 20, text = "Subtraction")
button3 = tk.Button(wn, width = 20, text = "Multiplication")
button4 = tk.Button(wn, width = 20, text = "Division")
placeMenu()
wn.mainloop()
TL;DR
Postavi font prilikom inicijalizacije entrya, preimenuj if statement i ukloni globalnu varijablu te koristi lokalnu (oper)
Prvo što vidim je mnoštvo ponavljanja. Napravi funkciju s više parametara na kojima ćeš izvršavati for loopove - kod će biti kraći i čitljiviji 20 puta. Tkinter je jednostavan, ali nije praktičan ukoliko nije objektno orijentiran (preporučujem tk.Toplevel klase za prebacivanje prozora, onda samo switchaš screenove i nema potrebe za brisanjem i postavljanjem, što dodatno ubrzava izvrašavanje). Idealno bi bilo kreirati class za svaku aritmetičku operaciju i uz to ne koristiti (spore) lambda anonimne funkcije samo za pozivanje s atributima.
Dakle, kod bi izgledao otprilike ovako:
for (akcija, klasa) in zip(["Addition", "Substraction", ...], lista_klasi):
button = tk.Button(wn, width=20, text=akcija, command=klasa);
...
button.pack() # Izbjegavaj .place(), uvijek samo pack() ili grid()
...
class Solution(tk.Toplevel):
def __init__(self, atributi):
super().__init__()
self.attributes("-topmost", False)
self.solution = tk.Canvas(self, bg="white" ...)
self.solution.pack()
...
# Za najbolji izgled postavljaj widgete na canvas...
self.solution.create_window(w, h, window=atribut[neki])
# Također, ako želiš ostati u istom prozoru, spremi widgete i koristi:
self.canvas.delete("all")
Osobno koristim pack jer je najjednostavniji, u početku nezgodan jer ne dobivaš izgledom točno kako zamišljaš ali kad pohvataš sve ne bi se nikad vratio na place(), osim ako nije nužno :)
Zasad prvo prouči sve pojašnjene primjere ovdje.
Tek kad savladaš tkinter kreni na kivy ako misliš stvarno rasturit na natjecanju.

How to allow Tkinter GUI to detect key presses when not selected

I am trying to make a full GUI, for A game borderlands 2, however it only detects my key presses when the tkinter box is selected (in focus). In a game this is not happening, so I need a way for tkinter to detect my key presses while not selected.
This is the code so far. It is not finished, ignore the "Auto Exec" and "Auto Exit" the problem is "Auto Reload". Clicking once will turn it ON and again will turn it OFF. Just choose any number (1-4) for the weapon slot, does not affect the error.
from easygui import *
from pyautogui import *
from time import *
import os
from tkinter import *
count = 1
slot = "0"
allowReload, allowExit = False, False
def poop():
sleep(3)
print("poop")
def countdown(count):
autoexecB.config(text = count)
if count == 2:
autoexecB.config(bg = "orange")
if count == 1:
autoexecB.config(bg = "yellow")
if count == 0:
autoexecB.config(text = "Running...", bg = "#44ff00")
if count > -1:
root.after(1000, countdown, count-1)
else:
for i in range(1, 3):
sleep(1)
press("'")
sleep(0.5)
type("exec Patch.txt")
press("enter")
press("esc")
press("enter")
sleep(4)
press("esc")
pressMult("down", 4)
press("enter")
press("up")
press("enter")
sleep(1)
press("'")
sleep(0.5)
type("exec STV.txt")
press("enter")
press("esc")
autoexecB.config(text = "Auto Exec", bg = "red")
def type(text):
typewrite(text)
def pressMult(key, amount):
for i in range(1, amount+1):
press(key)
def autoexec():
countdown(3)
def info():
msgbox("Auto Exec: Runs Mods automaticly\nAuto Exit: Exits the game automaticly to the main menu using INSERT\nAuto Reload: Automaticly reloads your gun using LEFT SHIFT")
def exit():
global count
count = 1
press("esc")
pressMult("down", 4)
press("enter")
press("up")
press("enter")
sleep(2.1)
press("enter")
if choose == "FARM at Hero's Pass":
sleep(3)
keyDown("w")
sleep(0.7)
keyUp("w")
keyDown("d")
sleep(1)
keyUp("d")
keyDown("ctrl")
sleep(0.5)
press("e")
keyUp("ctrl")
count += 1
def reloadslot():
global allowReload, slot
while True:
if allowReload == True:
break
slot = str(integerbox("Enter in the weapon's slot to be reloaded"))
if slot not in ("1","2","3","4"):
msgbox("A weapon can only be in slot 1, 2, 3 or 4")
else:
break
if allowReload == True:
allowReload = False
else:
allowReload = True
def on_press(event):
print(event.keysym)
if event.keysym == "Insert" and allowExit == True:
print("exit")
exit()
if event.keysym == "Shift_L" and allowReload == True:
print("running reload")
press(";")
press("e")
press(slot)
print("done")
root = Tk()
root.bind('<KeyPress>', on_press)
root.geometry("378x134")
root.config(bg = "blue")
autoexecB = Button(text = "Auto Exec", bg = "red", font = ("calibri","13"), height = 3, width = 13, command = lambda: autoexec())
autoexitB = Button(text = "Auto Exit", bg = "red", font = ("calibri","13"), height = 3, width = 13)
autoreloadB = Button(text = "Auto Reload", bg = "red", font = ("calibri","13"), height = 3, width = 13, command = lambda: reloadslot())
infoB = Button(text = "INFO", bg = "blue", width = 26, height = 3, command = lambda: info())
exitB = Button(text = "EXIT", bg = "blue", width = 25, height = 3, command = lambda: root.destroy())
autoexecB.place(x = 0, y = 0)
autoexitB.place(x = 126, y = 0)
autoreloadB.place(x = 252, y = 0)
infoB.place(x = 0, y = 78)
exitB.place(x = 193, y = 78)
root.mainloop()
root.mainloop()
I need a way for tkinter to detect my key presses while not selected
You can't use tkinter for that. It can only detect keyboard events when it has the keyboard focus. You will have to use some platform-specific library to do what you want.

Categories