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