Code sharing

Joined
Dec 10, 2022
Messages
93
Reaction score
25
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:
  • 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:

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,933
Messages
2,570,089
Members
46,546
Latest member
QZKLaurene

Latest Threads

Top