menator01
Gold Coder
Was helping someone with their project for a tkinter overlay. It's just a countdown time with an alarm after reaching 10 seconds.
Haven't added a drag feature yet. Any thoughts?
Can now drag widget around desktop
Key binds are
shift+r = reset
pause = stop/start counter
esc = exit program
Can now drag widget around desktop
Key binds are
shift+r = reset
pause = stop/start counter
esc = exit program
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: