Welcome to Code Forum!

Join a community that supports you and your coding journey from day one. We strive to be a friendly, supportive community that empowers everyone to be better developers. By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Guest, before posting your code please take these rules into consideration:
    • It is required to use our BBCode feature to display your code. While within the editor click < / > or >_ and place your code within the BB Code prompt. This helps others with finding a solution by making it easier to read and easier to copy.
    • You can also use markdown to share your code. When using markdown your code will be automatically converted to BBCode. For help with markdown check out the markdown guide.
    • Don't share a wall of code. All we want is the problem area, the code related to your issue.

    GIF shows where to locate </> in the thread and or post editor toolbar.
    To learn more about how to use our BBCode feature, review our "How to post your code into threads" here.

    Thank you, Code Forum.

Tkinter overlay

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

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()

To view this content we will need your consent to set third party cookies.
For more detailed information, see our cookies page.
 
Last edited:

Buy us a coffee!

Buy me a coffee.
Back
Top Bottom