- Joined
- Dec 10, 2022
- Messages
- 108
- Reaction score
- 26
I don't see any forum for code sharing and feedback so, I'll post here.
Was helping someone with an exercise creating an overlay with tkinter and python. Their project was to sound an alarm when the countdown reaches 10 seconds. After countdown reaches 0, it resets. This is what I came up with. I added key binds although their project did not call for them.
I welcome any thoughts and feedback.
Keybinds:
Was helping someone with an exercise creating an overlay with tkinter and python. Their project was to sound an alarm when the countdown reaches 10 seconds. After countdown reaches 0, it resets. This is what I came up with. I added key binds although their project did not call for them.
I welcome any thoughts and feedback.
Keybinds:
- shift+r = reset
- pause = stop/start counter
- esc = exit
Python:
import tkinter as tk
import pygame
from pathlib import Path
from os.path import exists
# Get path to executing script
path = Path(__file__).parent
# Initialize pygame.mixer
pygame.mixer.init()
class Sound:
''' Sound class loads, plays, and stops sound file '''
def __init__(self, audio=''):
self.audio = audio
self.verify = False
if self.audio and exists(f'{path}/{self.audio}'):
pygame.mixer.music.load(f'{path}/{self.audio}')
self.verify = True
def play(self):
''' Method for playing sound file '''
if self.verify:
pygame.mixer.music.play()
def stop(self):
''' Method to stop playing sound file '''
if self.verify:
pygame.mixer.music.stop()
class DragIt:
''' DragIt class takes a widget and moves it around the desktop '''
def __init__(self, widget):
self.widget = widget
# Mouse binds
widget.bind('<1>', self.grab)
widget.bind('<B1-Motion>', self.move)
widget.configure(cursor='hand1')
widget.bind('<ButtonRelease>', self.reset)
def grab(self, event):
''' Method for getting start position '''
self.widget.configure(cursor='hand2')
self.start_x = event.x
self.start_y = event.y
def move(self, event):
''' Method for moving widget '''
dx = event.x - self.start_x
dy = event.y - self.start_y
left = self.widget.winfo_x() + dx
top = self.widget.winfo_y() + dy
self.widget.geometry(f'+{left}+{top}')
def reset(self, event):
''' Method resets cursor pointer '''
self.widget.configure(cursor='hand1')
class Timer:
''' Timer class sets a countdown timer - default is 2 minutes '''
def __init__(self, duration=120):
self.duration = duration+1
def update(self):
''' Method for updating count '''
self.duration -= 1
hours = divmod(self.duration, 3600)
minutes = divmod(hours[1], 60)
seconds = divmod(minutes[1], 60)
return f'{hours[0]:02}:{minutes[0]:02}:{seconds[1]:02}'
class Window:
''' Window class is for displaying window '''
def __init__(self, parent):
parent.geometry('+50+50')
parent.minsize(130,30)
parent.maxsize(130,30)
parent.wait_visibility(parent)
parent.wm_attributes('-topmost', True)
parent.wm_attributes('-alpha', 0.5)
parent.wm_attributes('-type', 'splash')
parent.focus_force()
self.parent = parent
self.label = tk.Label(parent, anchor='w', padx=10)
self.label.pack(fill='x', expand=True)
self.label.configure(font=(None, 18, 'normal'))
class Controller:
''' Controller class handles communications between Timer and Window class '''
def __init__(self, window, timer):
# Create instance variables
self.window = window
self.timer = timer
self.action = False
self.duration = self.timer.duration
# Create the alarm sound
# Path to sound file example media/myalarm.mp3
# If in the same directory as script, just filename.mp3 will work
# This can also be empty for no sound or if file not found
# should still work
self.alarm = Sound()
# Make the window draggable
widget = DragIt(self.window.parent)
# Key binds
self.window.parent.bind('<R>', self.reset)
self.window.parent.bind('<Pause>', self.pause)
self.window.parent.bind('<Escape>', lambda event: self.window.parent.destroy())
# Get it started
self.update()
def update(self):
''' Method updates thw window '''
self.window.label.configure(text=self.timer.update())
# If counter is 10 or below, play alarm and change bgcolor
if self.timer.duration <= 10:
self.alarm.play()
if self.timer.duration % 2 == 0:
self.window.label.configure(bg='red', fg='white')
else:
self.window.label.configure(bg='orange', fg='red')
# If timer reaches 0 - reset
# I tried calling the reset function here but, caused problems with counter
if self.timer.duration <= 0:
self.timer.duration = self.duration
self.alarm.stop()
self.window.label.configure(bg='gray86', fg='black')
# Call .after to update window
self.updating = self.window.parent.after(1000, self.update)
def reset(self, event):
''' Method for resetting everything - key bind is shift+r '''
self.window.parent.after_cancel(self.updating)
self.timer.duration = self.duration
self.action = False
self.window.label.configure(bg='gray86', fg='black')
self.alarm.stop()
self.update()
def pause(self, event):
''' Method for pausing counter '''
self.action = True if not self.action else False
if self.action:
self.window.parent.after_cancel(self.updating)
self.timer.duration = self.timer.duration
self.alarm.stop()
else:
self.update()
if __name__ == '__main__':
root = tk.Tk()
# Timer excepts milaseconds - example 120 = 2 minutes (60x2 = 120 milaseconds = 2 minutes)
# If Timer left blank will default to 2 minutes
controller = Controller(Window(root), Timer(15))
root.mainloop()
Last edited: