My Cross-Platform Home Brewed Reminder Sysyem - on Windows

I needed a way to put persistent reminders on my desktop to remind me of daily tasks and appointments, because I'm easily distracted, and I can become so engrossed in what I'm doing that I lose track of the passage of time, not a good thing on days I have appointments, or fail to feed my dog or cat on schedule. To make things even more complicated, I dual-boot Windows 11 Pro 25H2 with Garuda Mokka Linux, so I wanted solutions for both OSes.

After a lot of searching on the Internet I came to the conclusion that what I want isn't available, at least not in a way I'm comforted with using. Across my search efforts I used Google's AI overview extensively in an effort to discover a solution that would do what I wanted, in a way I wanted (I' can be picky when it comes to my computers). After a few months of this on-again - off-again search, I gave up and decided to put my efforts on a back burner for a while.

A few weeks later, I read an item about Windows Task Scheduler, and it occurred to me that if I could find an app that would put a window on my desktop, displaying a message I could provide, it wouldn't be too difficult to use it to set up my reminders, but again, I couldn't find anything that would suffice, so I decided to try my hand at Vibe-Coding.

I've studied computer programming off-and-on since my MS-DOS v3.1 days when I learned to write Assembly language scripts that I compiled using debug to get COM files to make DOS do things it couldn't out-of-the-box, so I knew the time and effort it would take to create what I wanted for Windows, let alone all that would be required to learn the intricacies of GNU/Linux development. I decided on Python because it run's natively on both Windows and GNU/Linux and because it's a scripted language, so the final product (My Show-It.py script) would only take a few KB of storage space. Even though I'm the only user on my computers, I installed it for my User account rather than for all users, so it's located within my User account's storage space, at C:\Users\ernie\AppData\Local\Python

Since I'd be building what I want for myself, I decided on putting a window on the desktop that would get a title and message to display from the command-line, and that it should 'beep' when it launches to alert me, so I could schedule tasks with Microsoft's task scheduler in Windows, then using systemd's scheduling functionality in Garuda, but that's another story (suffice it to say that I got my app working successfully and reliably in both OSes.

Up to this point most of my experience with AI was in my Firefox web browser with Google's AI Overview, and I knew that Firefox now has AI support, so I enabled it in the side-bar, choosing Google's Gemini agent because I've become familiar with their AI search overviews.

I started with a generic prompt in Windows to see what Gemini would come up with, then when I got something that worked, I refined my prompt until I ended up with a script that succeeded, then I took my efforts to Garuda, finding that further refinement was needed on the GNU/Linux side, so I enabled AI in Firefox there too, just as I had in Windows. It only took a few hours to get a good result that worked on both OSes, and the script below is that result (Windows Task Scheduler configuration information follows the script):

My Show-It.py Python script (Updated 05/09/2026 for cross-platform functionality and 05/10/2026 to add GPL notifications - see the Linux version - comming soon - for more):

  
#!/usr/bin/python

# Show-It.py Displays a small window on your desktop that can be used for scheduled reminders
# Copyright (C) 2026  Ernest N. Wilcox Jr.
# Contact: ewilcox@gmail.com
# Leave a comment at https://ewilcox.blogspot.com/2026/05/my-home-brewed-reminder-sysyem.html

# This program is free software: you can redistribute it and/or modify it under the terms of the
# NU General Public License as published by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.

# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.

# You should have received a copy of the GNU General Public License along with this program.
# If not, see .

import tkinter as tk
import platform
import subprocess
import argparse
import sys
import os

def play_beep():
    """Plays a system-appropriate notification sound."""
    os_type = platform.system()

    if os_type == "Windows":
        try:
            import winsound
            # MessageBeep uses the modern Windows notification sound
            # If that fails (e.g., sound scheme disabled), it falls back to a 600Hz Beep
            try:
                winsound.MessageBeep(winsound.MB_ICONASTERISK)
            except:
                winsound.Beep(600, 500)
        except Exception:
            # Final fallback: generic console bell (might not work in Task Scheduler)
            sys.stdout.write('\a')
            sys.stdout.flush()
    else:
        # Linux / Garuda logic
        try:
            subprocess.run([
                "canberra-gtk-play",
                "--id", "message-new-instant-message"
            ], check=False)
        except FileNotFoundError:
            try:
                sound_file = "/usr/share/sounds/freedesktop/stereo/message-new-instant-message.oga"
                subprocess.run(["paplay", sound_file], check=False)
            except Exception:
                print('\a', end='', flush=True)

def main():
    # 1. Argument Handling
    # Logic to handle both single-string (Title-Message) and two-string (Title Message) inputs
    if len(sys.argv) == 2 and ("-" in sys.argv[1] or "/" in sys.argv[1]):
        raw_input = sys.argv[1]
        separator = "/" if "/" in raw_input else "-"
        parts = raw_input.split(separator, 1)

        raw_title = parts[0]
        raw_message = parts[1] if len(parts) > 1 else ""
    else:
        # Standard CLI behavior: script.py "Title" "Message"
        # Fixed 'nargs' typo here
        parser = argparse.ArgumentParser(description="Cross-platform Notification Tool")
        parser.add_argument("title", help="Window title")
        parser.add_argument("message", help="Message body", nargs='?', default="")
        args = parser.parse_args()
        raw_title = args.title
        raw_message = args.message

    # 2. String Cleaning
    display_title = raw_title.replace("_", " ").strip()
    display_message = raw_message.replace("_", " ").strip()

    # 3. Sound
    play_beep()

    # 4. GUI Setup
    root = tk.Tk()
    root.title(display_title)
    root.attributes('-topmost', True)
    root.minsize(380, 150)

    # UI Elements
    tk.Label(
        root,
        text=display_title,
        font=("Arial", 12, "bold")
    ).pack(pady=(20, 5))

    msg_label = tk.Label(
        root,
        text=display_message,
        font=("Arial", 11),
        wraplength=340,
        justify="center"
    )
    msg_label.pack(pady=15, padx=20)

    tk.Button(
        root,
        text="Dismiss",
        command=root.destroy,
        width=12
    ).pack(pady=(5, 20))

    # 5. Dynamic Sizing
    root.update_idletasks()
    width = max(380, root.winfo_reqwidth())
    height = max(150, root.winfo_reqheight())
    root.geometry(f"{width}x{height}")

    root.mainloop()

if __name__ == "__main__":
    main()
  

Here you have the code for my Show-It.py script (above). Because I don't want to make any assumptions whatsoever, I'm trying to write this so a child can understand it, so please don't be offended if you're an advanced reader. For completeness, I'll present you with a detailed description of the Windows Task Scheduler's user interface so you understand what you're looking at when we get that far as well as to act as a reference for it's use when you create and schedule any tasks you want/need in the future. Then I'll guide you through the steps required to create our test task, so let's get started:

To start, we open Windows Task Scheduler:

In the search box on the task bar, start entering the term 'task'
    A new option named 'Task Scheduler' should appear in the options list
        If desired, pin it to your start menu:
            ALT+Click the item and choose Pin to start
        Click the option to open the Windows Task Scheduler

Introduction to the Windows Task Scheduler's User's Interface:

Windows Task Scheduler's window contains three panes:

The left pane (Console Tree/Library Pane):

Consists of a hierarchical folder tree structure: The root folder/node named Task Scheduler (Local): Contains the Main folder (Task Scheduler Library): Contains various sub-folders created by: Windows, services, or apps that require scheduling You can create your own folders The purpose of this pane: Organize all scheduled tasks in an orderly manner Simplify task management

The Middle Pane (Details/Task Pane):

Upon Task Scheduler opening: Displays a summary of recently run and active tasks) Displays detailed information about tasks: Contained in the selected folder from the left pane Lists: Task name(s) Status (per task) Trigger(s) Last/next scheduled run.

The Right Pane (Actions Pane):

Provides direct access to commonly used actions create new tasks enable/disable tasks import/export tasks delete tasks Purpose: simplify task management

Get my Show-It.py script saved to your computer:

Create a folder for Say-It.py in your User account's storage space:
    In File Explorer navigate to your User's home folder
        C:\users\<UserName>
    ALT+Click an empty place in the right pane to open it's context menu.
    Choose New -> Folder and press Enter to create one
        It's named New Folder by default, and is highlighted for editing
    Enter Say-It in the new folder's Text entry field
    Press Enter to give it the Say-It folder name

Create the empty Show-It.py file:

In File Explorer, double-Click your new Say-It folder to enter it From within the folder (right pane) ALT+Click to open a context menu. Hover your mouse pointer over the New option In the resulting sub-menu, choose Text Document (bottom) (creates an empty text file). A file entry will appear in the right pane Named New Text Document.txt by default and ready for renaming Enter 'Say-It.py' in the text entry field to name it Don't forget to remove the TXT file extension (end) Press Enter to rename and save your new, empty Say-It.py file

Copy the Say-It.py code (above) into the new file on disk:

Copy the code for my Python script (above) to your clipboard:
    Highlight all the Say-It.py code above:
        Press and hold the primary mouse button
            Drag the mouse over all the code to highlight it
                (Beginning of first code line to end of the last code line)
    Press CTRL+C to copy the highlighted text to your clipboard
Paste the content of your clipboard to your new text file:
    ALT+Click your new text file and choose Open with -> Notepad
    Click in the empty Notepad window and press CTRL+V
        (Pastes the content of your clipboard into Notepad)

Save the finished Say-It.py script to your computer:

Press CTRL+S to save your changes to the file on disk
    Close Notepad

Prepare your computer to run Python scripts:

Install Python:
    If you haven't already, install Python for Windows:
        In your web browser, go to:
            https://www.python.org/downloads/
            Follow the website's prompts to download and run the installer

Check your system's environment for Python:

our computer's environment variables are in the System Properties dialog:
    In the search box on the taskbar, enter env
    In the resulting list, Click Edit the System Environment variables
        (Opens the System Properties dialog)
    Click the "Environment Variables" button (lower-right)
        (Opens the Environment Variables dialog)
        User level variables are located in the top pane.
        System wide variables are in the bottom pane.
        We'll be working with the top pane exclusively here.
    Check that the Path variable contains an element for Python:
        Click the 'Path' variable to highlight it.
        Click Edit (under top pane) to see all individual elements
        One of the elements should contain
            C:\Users\$(USER)\AppData\Local\Python\bin
Check that the RunPythonw environment variable exists
    I can't remember whether I or Python created it
    If not, Click New under the User variables pane
        (Opens the New User Variable dialog):
        Variable name:
            RunPythonw
        Variable value:
            C:\Users\ernie\AppData\Local\Python\pythoncore-3.14-64\pythonw.exe
Click OK to save your work and exit the dialog

Before continuing, check that Show-It works as expected:

Navigate into your Show-It folder.
ALT+Click an empty place in the right pane, and choose 'Open in Terminal'
    A Terminal window will open in the current folder.
    At the Terminal's command line, enter:
        Pythonw .\Show-It.py "Test" "This is a simple test!"
    If your computer has sound enabled it should beep
    In any case, a small window should pop up
        The text "Test" is displayed in the title bar
            and above the message text
    The message "This is a simple test!" is displayed in the Window body
If this test succeeds it's time to schedule our first task!

Create and schedule our Test task:

Step 1. Create a folder in Task Scheduler for our new task:
    ALT+Click Task Scheduler Library
        Select New Folder
        Name it Test in the resulting folder naming dialog
        Press OK to create the new folder
Step 2. Create our new Test task:
    Expand Task Scheduler Library to see our new Test folder
    Click the test folder to put the focus on/in it
        ALT+Click the new Test folder
    Select Create Basic Task... in the resulting context menu
        (Opens the Create a Basic Task dialog)
        Name field:
            Enter Test to name this task
        Description text field:
            Enter Simple test (Optional) to describe this task
        

Click Next to proceed to the Task Trigger dialog:

Triggers dialog description:
Left column:
Listed steps to create the task Defaults to Daily Steps may change for each option in the right column
Right column:
Options: Daily Weekly Monthly One time When the computer starts When I log on When a specific event is logged For our test task, Select One time

Click Next to proceed to the One time dialog:

Time dialog description:
Set the date/time the task will start/run Options: Start: Date: Month Day Year Time: Hour Minute Second AM/PM For our Test task, keep the current Date and Time

Click Next to proceed to the Action dialog:

Action dialog description:
Options: Start a program Send an email (deprecated-can no longer be used) Display a message (Deprecated-can no longer be used) For our Test task select Start a program Click Next to proceed to the Start a Program dialog:
Start a program description:
Options: Program/script: Text entry field Enter the path to your program/script Note: For any task running any Python script Enter %RunPythonw%
Explanation:
%RunPythonw% is an Environment variable Points to the pythonw.exe executable Or Browse button: Navigate to the program/script on your computer Add arguments (Optional): Text entry field For our Test task (Add Arguments (Optional) text entry field): C:\Users\ernie\Show-It\Show-It.py "Test" "This is a simple test" Explanation: Command line argument for RunPythonw.exe (3 parts) 0 Path to my Python script C:\Users\ernie\Show-It\Show-It.py 1 Title (1st command line argument for the script) "Test" $2 Message (2nd command line argument for the script) "This is a simple test"

Click Next to proceed to the Summary dialog:

    If everything's correct:
        Click on Check Box:
            Open the Properties dialog for this task when I click Finish
    If not use the Back button to go back to correct what's wrong
Click Finish to create the task and continue to it's Properties dialog:
    
Properties dialog description:
Consists of 5 functional tabs & 1 disabled
General:
Name: Test (Task's name) Location: \Test (Task Scheduler Library's file hierarchy location) Author: TRAVELER4\ernie (Computer_Name\User_Name) Description: Simple test
Security options:
When running the task, use the following user account: ernie (your information will differ) To change, use the Change User or Group button (Right) Run only when user is logged on Selected by default (keep for this task) Run whether user is logged on or not Not selected by default Do not store password The task will have access only to local resources. Not enabled by default Run with highest privileges Disabled by default Enable Hidden (Configure for Windows Vista, Windows Server 2008) Not applicable for Windows 11 family of OSes
Triggers
Edit this if you need to update or change the Trigger
Actions
Edit this to change or add an action
Conditions
Idle: Start the task only if the computer is idle for <Time in minutes> Stop if the computer ceases to be idle Available only if the parent is active Restart if the Idle state resumes Available only if parent and grandparent are active Power: Start the task only if the computer is on AC power Enabled by default Disable (recommended for most tasks) Stop if the computer switches to battery power Available only if the parent is active Wake the computer to run this task Disabled by default Enable (recommended for most tasks) Network: Start only if the following network connection's available Disabled by default Enable only if the task requires a Network connection or access to network resources
Settings
Allow task to be run on demand Enabled by default (Recommended) Run the task as soon as possible after scheduled start missed Disabled by default Enable (Recommended for most tasks) if the task fails, restart every <Time in minutes> Disabled by default (Recommended) If task fails, restart every <time in minutes> Disabled by default (Recommended) Attempt to restart <count> times Available only if parent enabled Stop task if it runs longer than: <Time> (Measured in hours/days): 3 Days (default) Enabled by default (Recommended) if the running task does not stop when requested, force stop Enabled by default (Recommended) If the task is not scheduled to run again, delete it after Immediately or Time in days> Disabled (Default) No recommendation if the task is already running, the following rule applies: Do not start a new instance (Default) Other options: Run a new instance in parallel Queue a new instance Stop the existing instance
History (disabled)
Because this tab's disabled, I won't cover it
When you're satisfied with your settings
    Click OK to save your Properties settings and exit.

Test this task:

Click on the Test task in the middle pane to highlight it In the Actions pane, Click Run under Selected Item to test the task My Test task ran successfully. My computer beeped The window popped up and remained until I Clicked the Dismiss button This is a template I created as a guide for future task creation using my Python script that you may want to keep:
For Show-It:
Program/script: %RunPythonw%
Add arguments (optional): C:\Users\ernie\Show-It\Show-It.py "<Title>" "<Message>"
Start in (optional): C:\Users\ernie\Show-It
Selected/Recommended settings in the task's Properties:
General:
    Run only when the user is logged on
        Enabled by default (Recommended)
    Run with highest privileges
        Disabled by default (Not Recommended)
        Enable
Conditions:
    Wake the computer to run this task
        Disabled by default (Not Recommended)
        Enable
    All other options:
        Disable (Recommended) unless you have a reason specific to the current task
Settings:
    Allow task to be run on demand
        Enabled by default (Recommended)
    Run task as soon as possible after a scheduled start is missed
        Disabled by default (Not Recommended)
        Enable
    Stop the task if it runs longer than: <Time in Hours/Days>
        Disabled by default (Not Recommended)
        Enable
        Set time = 1Day (Recommended)
        If the running task does not end when requested, force it to stop
        Enabled by default (Recommended)
        All other options:
        Disable (Recommended) unless you have a reason specific to the current task

Comments

Popular posts from this blog

How to - Enable/Set-up secure boot on Arch-based distributions

Common Debian App Commands With Descriptions

Completely remove OneDrive from your Windows computer/installation