frigcal — a "Refrigerator"-Style Desktop GUI Calendar
|Version ||1.7, August 27, 2016
|Summary ||A stand-alone desktop GUI with basic calendar functionality, using portable iCalendar event files.
|Requires ||Python 3.X and its tkinter standard library.
|Launch ||To start the program, extract the zip file, and run its frigcal.py, the main script
(or frigcal-noconsole.pyw on Windows).
|Author ||M. Lutz (http://learning-python.com), © 2014-2016
|License || Provided freely, but with no warranties of any kind. Redistributions
are welcome, but must include the package's "Readme-frigcal.html" file
and "docetc" and "screenshots" folders, in full and unaltered form.
This document describes the frigcal calendar GUI program. In this document:
- Using the Program
- Using Calendars
- Using the GUI
- iCalendar Implementation
- Release History
- Project Goals and Review
This section provides a quick first look at frigcal and its startup details.
In this section:
The frigcal program implements basic personal calendar functionality, in a
standalone desktop GUI. Among its features:
- Portable iCalendar files for event storage
- Multiple calendar files support, and new file creation
- Automatic backup of calendar files, with multiple backups management
- Unicode support for non-English text in both GUI and calendar files
- Portable, open source program, for Windows, Linux, Macs
- Month display and navigation
- Display and editing of persistent events, with summaries and descriptions
- Event color coding by both calendar name and event category
- Multiple resizable month view windows, with independent or tandem navigation
- Month images display option
- Quick overviews on mouse hovers via footer option
- Event cut, copy, and paste, across days and month windows
- Event selection lists for days with too many events to display
- User configurability: fonts and colors, mouse or touch event models, and more
This program does not implement more advanced calendaring tools such as task
scheduling, journaling, alarms, event invitations, or recurring or multiday events,
though some can be emulated manually (e.g., copy-and-paste for recurring and multiday
events, and event description text for date-based journal entries).
In exchange for this deliberately restricted utility, this program minimizes
both code and GUI complexity; reduces the risk of data loss; and provides an
open and portable interface sufficient for most personal calendar needs, which does
not require a network connection, account login, or proprietary system or device.
This section gives the complete set of examples of this program in action on Windows and Linux,
also available in this package's screenshots folder.
This folder and its subfolders also have newer thumbnail
index pages displayed automatically on a server; click the "index.html" files along the way
when viewing these offline.
The list and tables below itemize the salient images in the set.
Many are from the prior 1.2 release, but are unchanged in 1.3 and later apart from
updates captured in the second table below. The first table below gives examples of core behavior,
mostly in Version 1.2. The second table's pictures were largely captured on Windows 7. As usual
with GUIs, install and run live to get a better feel for the dynamic interaction absent here.
The upcoming sections cover program, calendar, and GUI usage in detail, but here's a quick summary
of startup fundamentals:
This program uses Python 3.X, tkinter, and portable and standard-conforming
iCalendar ".ics" Unicode text files to store event data. It's known to run on all recent versions
of Windows, Linux, and Python 3.X. Because it's coded in Python and tkinter, it is expected to also
work on Macs and future Python 3.X releases. Its source code is viewable both in its zip
file and online.
To install and run this program, fetch its package's zip file at
Download and unzip, and then click or otherwise launch the package's frigcal.py
(or frigcal-noconsole.pyw) to run, after installing Python 3.X if required.
Drag these scripts out to desktop shortcuts as desired, and see ahead for more launching hints.
Required packages are included; an optional library requires an extra
install for some month image types and Pythons.
To upgrade to a new release, copy over your own data from the prior version's
top-level folder to the new version's top-level folder, after unzipping. If you're
overwriting the prior version, be sure to save this data from the prior version first,
to restore it later. Your top-level folder data consists of:
- The frigcal_configs.py file—always in the program's top-level folder.
- The Calendars folder—unless you've configured it to reside elsewhere.
- The MonthImages folder—if it resides in the program's folder and you've customized it.
To use this program, you'll perform operations in the GUI,
maintain one or more calendar files, and configure appearance and behavior
settings as desired. The following sections document these topics.
This section gives basic program usage details, covering its important files and
user-visible aspects of its operation. For information on calendar files,
see Using Calendars; for help with using the GUI itself,
see Using the GUI (both ahead).
In this section:
This program's source files reside in its zip file, available at this index page.
You can view them and other package content both on your computer after extracting the zip file,
and in an unzipped online copy maintained at this site.
To start the program, click or otherwise run either of the following in the top-level folder (a.k.a. directory) of
your copy's unzipped content:
- Windows users:
- You can launch either of these scripts by simply clicking their icons. If desired, drag them out to desktop
shortcuts to make them easier to start, and set their shortcuts' icon to "frigcalset.ico" in the top-level folder
(right-click on the shortcut to Properties). You can also launch from command lines in frigcal's folder:
"frigcal.py", "py frigcal.py", or "py -3 frigcal.py".
- Linux users:
- A "python3 frigcal.py" command line in frigcal's folder will likely launch the program as is,
but you may wish to edit the script's initial "#!" line for your system's install paths. Some Linux systems
may require an extra install for tkinter support: see ahead.
- Mac users:
- Testing resources are limited, but feedback is welcome. Initial reports suggest that frigcal runs correctly
on your platform (after a minor Jan-2016 patch for Tk version testing), but startup
pragmatics are TBD; see the Linux pointers above and the Mac tkinter link ahead.
Other user-related source files located in this program's top-level directory and covered in full later:
- Module frigcal_configs.py has user configurable options, in Python code.
- Script makenewcalendar.py creates a new, empty, named calendar on demand.
- Utility unicodemod.py converts a calendar (or other text) file to a new Unicode encoding.
- Utility pickcolor.py displays the #RRGGBB string of a selected custom color for configs.
- Utility docetc\fixeoln.py converts configs (or other) file end-lines to Windows or Unix format.
The first of these two is user-editable and described ahead.
Because the program creates a default calendar automatically, the second of these is
an optional step. The last three files listed above are described in version 1.7 release notes.
In addition, files
are modules used only by the system itself, not its users; __sloc.py__ is used for metrics only;
and other ".py" files in the docetc folder are supplemental examples not used by the frigcal program.
Browse the top-level folder for more details.
A note for programmers: by code structure, the program consists of a primary module with classes
that mirror main GUI windows (months and dialogs), plus a module for non-GUI calendar file access,
and a set of smaller utility and configuration modules and scripts. See its code for more details.
Calendar files and month image files are stored in folders, which by default are located in this
program's top-level directory. These folders may be located elsewhere via configurations file
path settings. Calendar and backup folders are fully described ahead, in
Using Calendars; month image usage is also covered ahead in Using the GUI.
- The Calendars folder (or one you configure) stores calendar ".ics" files.
- The MonthImages folder (or one you configure) stores month image files.
- The Calendars\Backups subfolder stores calendar ".ics" file automatic backups.
You can store arbitrary unrelated subfolders and files in any of these three folders.
Specifically, the program uses only ".ics" files in the first and third of these, and
only image files (of any type) in the second, and ignores any other items present. In the images
folder, non-file items are skipped as of version 1.4, and non-image files as of
To configure this program's appearance and behavior to suit your tastes and usage,
edit source-code file frigcal_configs.py
in the program's top-level folder.
This file supports a wide variety of user customizations—fonts, colors, window size, click modes,
user directories for calendars and month images, and more. These settings, coded as Python assignments and
loaded at program start-up, are described in full in the file's comments; are shipped preset to example
values that demonstrate usage; and may be freely changed by users. Whenever possible, this system employs these
Python-coded configurations (a.k.a. configs) instead of GUI devices, for both flexibility
and code and interface simplicity.
There are more details on the configuration process in the configs file itself, but a few general
usage points are worth noting here.
Edit with care:
Errors in this file don't terminate the GUI (you'll get a pop-up message with error details), and omissions
in this file apply defaults and produce detailed console messages. Still, users are advised to take care
with edits: import the file separately to check it for errors, and make a backup of its shipped version.
Unicode encoding and text editors:
The configs file is shipped in UTF-8 Unicode encoding form as of version 1.7, to both support and
demonstrate non-English settings text. Most modern text editors—including Python's IDLE,
Notepad on Windows, and gedit and vim on Linux—support this common text file
format, and usually automatically. The
text editor program from book PP4E also
uses UTF-8 as its default encoding.
See version 1.7 release notes for background details on the
As fallback option, in the very unlikely event that users cannot edit the
config file in its shipped UTF-8 form, there is a plain ASCII equivalent in the docetc folder
here. This file is identical to that in the
script's main folder, sans three non-English category names which cannot be converted to
narrower encodings like ASCII or Latin-1. If required for edits, copy it up to '..' and
rename without its "--ascii" suffix.
You can change this file's content and encoding declaration to use Latin-1 instead, but the
shipped UTF-8 version supports a much broader character range. Also note that the included
unicodemod.py utility can convert the shipped UTF-8 configs
to a similarly-general encoding like UTF-16, but the file's content precludes automatic
conversion to narrower schemes like ASCII.
Linux end-line conversion options:
Like all text files in the frigcal package, the configs file is shipped in Windows (a.k.a. DOS)
end-line format, which may be inconvenient for some users on Linux and other Unix-like platforms.
The gedit text editor on Linux handles Windows end-lines automatically, and there are a variety
of well-known Linux conversion techniques, including the dos2unix shell command,
and vim text-editor commands such as ":e ++ff=dos" and ":set ff=dos". Some commands
may require an install, however, and editor techniques can be complex.
To address this, as of version 1.7 frigcal ships with a Python-coded end-line
conversion script, fixeoln.py, which can be used to convert
text files in any Unicode encoding—ASCII, UTF-8, Latin-1, UTF-16, UTF-32, and others—when no
other tools are available or convenient. The configs file can be converted from Windows to Linux
end-line format by this script with a simple console/shell command of this form (the "utf8" at
the end of this is the optional default encoding, which works for ASCII files too):
~/Downloads/frigcal/docetc$ python fixeoln.py tounix ../frigcal_configs.py utf8
For more details, see version 1.7 release notes,
the script's docstring, and its usage
and test run log.
Because of the language used for its implementation, this program is generally as portable as Python 3.X
and its standard tkinter GUI toolkit—which run on most desktop-metaphor computers. In more detail
regarding platform, language, and data portability, this program:
Is known to run on Windows (7,
and Linux (Fedora and
Ubuntu, using Gnome),
and is expected to run on Macs (OS X) as well as other Windows and Linux platforms.
Is known to run under Python 3.3, 3.4, and 3.5 specifically, but any
Python 3.X version is likely to suffice (see also the next section).
Uses and generates ".ics" iCalendar text files in the UTF-8 Unicode encoding that
are portable to other calendar programs which support this standard (see Using Calendars ahead).
Futurism: it's not impossible that tkinter, and hence this program,
may someday also work on Android (and other) devices; see ahead for more details.
This a desktop-based program, but its interaction modes support touch-enabled devices such
as tablets (see Using the GUI ahead).
As shipped, this program works if you install just Python 3.X.
In full detail, it relies on the following components:
Python 3.X, as well as its standard library's tkinter module for its GUI.
This program is written entirely in the Python programming language—specifically Python 3.X,
where "X" means the latest in the version 3 line.
If it's not already installed on your computer, Python 3.X can be fetched from
for Windows, simply get and run the latest 3.X self-installing executable there.
tkinter is included with most Pythons (and always with the python.org Windows installers),
so this is not normally a separate fetch/install step. If tkinter doesn't work for
you on Ubuntu Linux, try a "sudo apt-get install python3-tk tk"; Mac OS X users may
wish to read this.
To date, this program has been developed and tested under Python 3.3, 3.4, and 3.5;
while compatibility is expected for future 3.X releases (and perhaps beyond the 3.X line), it naturally
cannot be guaranteed. If you experience anomalies in later releases, try using 3.3 through 3.5.
The 3rd-party icalendar parser/generator package, which in turn requires pytz.
These platform-neutral third-party packages are both required to
load and save calendar files. For convenience, open source versions of both are included
with this program and used automatically, and need not be fetched or installed separately.
If ever required, you can see and fetch these packages at:
The 3rd-party Pillow (formerly known as PIL) package.
This image-processing package is required only if you wish to use the program's optional
month Images feature for some image types. frigcal still works without Pillow;
if not installed, unsupported month images are simply disabled in the GUI (and you'll get a
popup message if you select them).
Because Pillow's installed code may vary per platform and Python version, it must be installed separately
if some image types are used. Commonly-used Pillow self-installers for Windows and Python 3.X are included
and documented in frigcal's dependencies folder for convenience (click to run),
but see the following if these don't apply to your platform or version, or fail on your machine:
Update: as of frigcal version 1.6, depending on your image
file types and Python version, a Pillow install may no longer be required to use the optional month images
display feature. Month images now display with just Python itself if you use either PNG images and
a Python that uses Tk 8.6 or later (including Python 3.4 and later with the standard Windows installer at
python.org), or GIF or PPM/PPG images and any Python 3.X. For all other combinations of image
type and Python release, Pillow is still required for the image display feature, and you'll still get a
popup if images are enabled and a month image cannot be displayed. A
new popup per image failure replaces
the former popup on Images toggle-on.
Using PNG images and Tk 8.6+ (which is standard in Python 3.4+ on Windows) is generally recommended,
as it provides both image quality and install simplicity.
This code may also work on Python 2.X with minor changes (e.g., tkinter imports
would use Tkinter), but cross-version compatibility was dropped after grappling
with it on the recent mergeall archive synchronization project—an
admittedly more system-intensive program, which may represent a worst case
for dual-version complexity
(see mergeall's main page).
Futurism: a frozen executable option could obviate some of the dependencies above—including
the Python 3.X install—though source-code distributions could not, and this option
has not been explored to date (see future ideas ahead).
This section lists calendar file usage details. Because the system makes a new
calendar for you the first time you run it, most of the following items can be skipped
or skimmed if you do not have existing calendar files you wish to use, and a single
default and automatic calendar suffices for your goals.
Calendar event data is stored in portable, program-neutral iCalendar
".ics" text files, using a text format defined by the iCalendar standard.
This is the calendar world's equivalent to CSV or JSON files for data
exchange, and uses an application-specific format similar in spirit to
GEDCOM files in genealogy data. This format provides calendar data
portability both to and from other calendar programs.
Additionally, frigcal both loads and saves calendar files using the
common and general UTF-8 Unicode encoding scheme. This encoding handles simple
ASCII text, but also allows calendar event descriptions to contain both
non-English and special-character
and is similarly portable among calendar programs—frigcal can use any
existing UTF-8 iCalendar file, and creates UTF-8 iCalendar files which can be
used by any other program that supports this scheme. Frigcal also provides an
encoding converter, and can display any Unicode character supported by its underlying
GUI library. See version 1.7 release notes for the full
story on Unicode files and support.
Your calendars are stored as text data files, one per calendar, in your calendars folder.
This folder is either the Calendars folder where this program resides (the default),
or a folder whose path you name in frigcal_configs.py (see its setting "icspath").
For instance, a travel calendar's data is stored in a file of this form, where
calendars refers to your default or configured calendar files folder:
Calendar files managed by frigcal in this folder should be usable in other calendar programs
that support the iCalendar standard. Conversely, frigcal can use iCalendar files created by
other programs if they are placed in this folder.
This program supports viewing and editing multiple calendar ".ics" files.
Each ".ics" file present in the calendars folder on startup is assumed to
be a user calendar; the combination of all their events is displayed in the GUI.
Event summaries displayed on days are sorted (i.e., ordered and grouped)
by calendar filename first, and then creation date within calendar. If you use
multiple calendars, you can choose calendar filenames to group day events as desired
(e.g., the first filename by sort order gets precedence when screen space
is limited, though the 1.3 event selection list displays all).
Save and Backup:
A calendar's data is saved to its ".ics" file only under four conditions—
on program quit requests; if the data has changed;
if the save is verified by the user; and after a successful and automatic backup copy is made of
the file's prior version.
Files are not updated after each change. Rather, for speed and accuracy, calendar
data is stored and updated in memory only, and saved to files later on program quit
requests. On a quit (the main window's "X" close button), a two-step dialog sequence
confirms a save and then an exit, and either step can be confirmed or cancelled.
Hence, you can backup and save at any time without exiting—click the
main window's close button, and confirm the save dialog but cancel the exit dialog.
If no data has changed, the save dialog does not appear on quit requests.
The system maintains multiple backup copies of each calendar file, created when changed data is saved.
Backups files show up in the automatically-created Backups subfolder of your
calendars folder, with date/time name prefixes to make them unique.
For instance, a backup of a travel.ics calendar is stored on saves in a file of this form:
Backup files are simply copies of iCalendar text files, and can be used directly as calendars;
rename to strip their date/time prefixes if desired. To change the maximum number of backups
retained for each ".ics" file, set its variable in frigcal_configs.py
(the current default is 10)—the system will keep up to that many most recent backup
copies per file in Backups subfolder. Older backups are pruned as needed when new backups
are written, and saves overwrite calendar files only if a backup succeeds.
By default, the program creates and uses one ".ics" file if none are present
on initial (or later) startup. If you are starting from scratch and can
group and colorize your events by category name instead of calendar file,
this single, default, and automatic calendar will probably suffice. frigcal
creates your default calendar in your calendars folder, at:
You can remove this calendar if you add others later and don't wish to use the default
(see Removing Calendars ahead).
frigcal also generates a required initial event in the default calendar that looks
like this, which you may
also later remove.
Making New Calendars:
To make new calendars manually, run the makenewcalendar.py script to make any
number of new ".ics" calendar files in your calendars folder. You can run this script from a
command line, or by simply clicking its filename/icon on Windows
All calendars you create this way will be loaded on startup. This script respects any calendar folder
settings in frigcal_configs.py; new calendars created this way are stored
wherever your calendar folder is located when the script is run. frigcal generates a required
initial event in new calendars that looks
which you may later remove.
Using Existing Calendars:
To use existing calendars, simply copy any number of existing ".ics" files to your
calendars folder. All calendars you copy there will be loaded
on program startup, and backed up on saves in the Backups subfolder.
As noted earlier, frigcal fully supports non-English and special-character Unicode text
in calendar files and displays, and uses the UTF-8 encoding to load and save calendar files.
No special handling is required for existing calendar files already in UTF-8 or ASCII encoding forms.
Existing calendar files not in UTF-8 format may require a one-time conversion; see
version 1.7 release notes for options for using existing non-UTF-8
files, including a provided encoding-converter script.
To remove a calendar, simply delete its ".ics" file from the calendars
folder. All remaining ".ics" files in that folder will be used on the next run.
Note: if you remove a calendar file from your calendars folder, frigcal will
not automatically remove any backups of that calendar from the Backups
subfolder. This is by design—backup files are small, may be useful, and are your property.
If you're sure you'll never need them, also manually remove any backup files
of a removed calendar when removing the calendar's main file.
If you attempt to save a calendar after removing its last event, frigcal will create
a required sole event in the calendar that looks
because empty calendar files aren't allowed. This keeps the calendar active in the next session,
and you may remove the added event later. If you wish to instead remove a calendar altogether,
delete its file per the prior bullet.
Using Outlook Calendars:
To view Outlook (or other calendar program) events: export calendars to ".ics" files
if needed, and copy/move them to your calendars folder. Event text may be viewed and
edited and all existing calendar data is retained on saves. For simplicity, though,
not all Outlook functionality is supported by this program; see the
Overview earlier and
Why frigcal? ahead
for more on program features and limitations.
Despite its text-based storage medium, this program performs well by most measures.
As an example: for a very large ".ics" file used in testing (currently 876K bytes,
35K lines, and 12 years of events), the GUI can take from 5 to 10 seconds to appear
on a Windows 7 Ultrabook and Window 8.1 tablet, due to the initial parsing step.
Once the file is loaded, though, this program runs very quickly thereafter,
except for a generally shorter multi-second delay to generate and write the file
on exit. Memory usage is also reasonable by modern standards of reasonable—with
over a decade of calendar data, this program takes no more space than a typical
web page viewed in a typical browser. Update: Python 3.5 now runs
the initial calendar load almost twice as fast in use cases tested—see
version 1.6 notes.
Handling Backup/Save Errors:
File backup and save errors should rarely (if ever) occur, and the system goes to
great lengths to maintain multiple backups as a safeguard, as described above.
However, if you get an error message dialog that begins with "Error while backing up..."
it means that your calendar file was not overwritten in any way, as saves are cancelled on
If you instead receive an error message dialog that begins with "Error while generating..."
the system failed while trying to either generate your calendar's text, or write it to a file.
Your simplest recourse in both cases is to simply try again; calendar data is saved
intact in memory until program exit (and changed calendars are still marked as changed after
a backup or save failure), so a successful later save implies that a temporary system glitch
was the error's likely cause.
If your calendars backup and save correctly on a retry, you can safely ignore a prior
error. Otherwise, an error during backups warrants closing without a save and restarting,
as your data file is unchanged. An error during generation instead may also
be harmless if your calendar file is unmodified (per its mod time); this implies an
icalendar library failure prior to opening or writing the file. In the worst case,
a failure while writing to the calendar file may corrupt its data; in this unlikely
event, copy the most recent version of the failed file from your Backups folder (stripping
date and time from its name) up to your Calendars folder, and restart.
You'll lose any updates in the failed session, but your prior calendar version's data should be
intact. Repeat as needed with older versions in Backups, if the most recent is invalid.
the new startup and pruning failure error handling in version 1.5
For more technical details on this program's usage of the iCalendar
iCalendar implementation ahead.
This section provides GUI usage details. It may help to refer to a
screenshot or live window while reading this section. One
main-window image is included inline below for reference, but there are both clickable thumbnails
and links to relevant examples along the way—open these in a new window for easier viewing.
Also keep in mind that this GUI was designed to be used on both
touch and mouse/keyboard devices, and this
influences both its layout and its action triggers. The GUI's operation, for example,
includes interactions both common to and differing between selectable usage modes.
In this section:
The GUI's top level is composed of one or more month view windows,
which may be opened on the same or differing months, and moved to new months and years either independently or in tandem.
Month windows in turn open dialogs for event view/edit, creation, list selection, cut/paste, and so on, as well as associated
use a resizable display that always shows 6 weeks for consistency and clarity,
with event summary text widgets positioned on days arranged in a grid, and control widgets placed around window borders.
Both border controls and day-grid days, numbers, and events invoke actions when selected.
An optional footer display in the lower portion of the window gives event details
on hovers or clicks. Event summaries—single-line text that provides short captions—appear
on days, in the footer, in event dialogs, and in the event
selection list dialog. Event descriptions—multiline
text large enough for full details and journal entries—appear in footers, and in event dialogs.
ease of use on touch interfaces, there is no top-of-window menu (which takes space and can be cramped),
or event drag-and-drop (which can be nearly nondeterministic on a Windows tablet). Instead, controls
appear on window borders, and event cut/copy/paste is fully supported as an alternative to drag-and-drop
(see cut/paste note ahead). For keyboard use, whether physical or on-screen, a
set of key bindings are equivalent to date navigation buttons (see Date Navigation ahead).
For more on the choice of cut/paste over drag-and-drop, see also this futurism.
Both month windows and event dialog windows may be freely resized and moved in the GUI.
Month windows are non-modal
(non-blocking); their initial size may be tailored by either absolute pixels or percentage of full screen size,
and their initial position may be preset (see Configurations File). Event
dialog windows are all modal (blocking), and opened as needed in response to GUI actions described ahead.
The GUI operations in the table below—invoked by controls arranged around
month window borders—are always
available, regardless of your click mode setting in frigcal_configs.py.
||Move to previous year in this window, or in all windows if Tandem.
||Move to next year in this window, or in all windows if Tandem.
||Move to previous month in this window, or in all windows if Tandem.
||Move to next month in this window, or in all windows if Tandem.
||Move to current date in this window only.
||Move to entered mm/dd/yyyy date in this window only.
||Enter in text
||Open a new month view window.
||Enable/disable synchronized moves for all month windows.
||Open/close month image associated with this window's month.
||Open/close event summary text display at bottom of this window.
||Open this help document in a web-browser window.
||Close clone, or backup/save calendar files and/or exit program.
||Minimize this month window and its image, later restored as a pair.
The following list provides more details on border control actions.
The main window's top-right quit button (a.k.a. close, and rendered as an X on most platforms) is used for both
kicking-off a calendar files backup/save operation, and ending the program. A quit button on a Clone popup window,
described ahead in this list, silently closes its window only. A quit button in the main window
(denoted by the "Main" in its title text) invokes two dialogs:
- The first verifies ".ics" calendar files backup and save
(and is skipped if no calendars have been changed).
- The second verifies program exit.
Hence, to backup and save without exiting, confirm the first dialog and cancel the second;
to exit without saving, cancel the first then verify the second;
and to both save and exit, confirm both dialogs.
See Using Calendars for more on saves and backups.
Date Navigation—Buttons and Keys:
These widgets appear at the top and bottom of the month window. They
move the window's display (and its open image) to:
- Previous/next month or year: PrevMo, NextMo, PrevYr, NextYr
- Today's date: Today
- An entered date: GoTo
The keys Up-arrow/Down-arrow and Shift+Up-arrow/Shift+Down-arrow also go to previous/next month and year,
respectively, but are simply redundant with navigation buttons at window bottom, and not required of touch users,
whose on-screen keyboards may be incompatibly large.
(Left and right arrow keys are reserved for text edits in event summary fields.)
Whether invoked by buttons or keys, previous/next month and year navigations are applied to all open month windows (and their
images) if Tandem is enabled, per the next bullet item.
Also as conveniences for keyboard users: an Enter key press in the GoTo date input field is the same
as pressing the GoTo button—moving to the entered date; and as of version 1.5 the Escape key (a.k.a. "Esc")
is the same as pressing the Today button—moving to the current date. Whether invoked by buttons or keys,
both of these operations always effect the currently active window only.
Clone Button and Tandem Checkmark:
The Clone button opens a new, independent month view window, and selecting Tandem causes all open month windows
(and their open images) to navigate in sync on a previous/next month or year action (only)
in any open month window. The Clone option supports
multiple month views.
Each month window can be open on a different month—open 2 to view the current and next or prior
months together; open 3 to view the current, prior, and next months at once; open 6 to see
half a year if your screen is large enough.
Month windows move through months either independently or in synch. If Tandem
is toggled on, all open windows respond to any previous/next month or year navigation,
and thus move together. If Tandem is off, only a single window is moved when its
navigation controls are used. A Tandem setting in any window is GUI-wide, and automatically
propagated to all month windows.
All open month windows also reflect updates made in any: if more than one are viewing
the same month, all are updated immediately for any change made to their
month in any window. As mentioned earlier, only a quit in the main window
(denoted by the "Main" in its title text) ends the program; other windows close themselves only.
This toggle shows/hides the event overview-text display area near the
bottom of the window.
This display is filled with event date, summary, and description when the mouse cursor
enters an event's summary field, or the field is single-clicked/tapped (see click mode
actions ahead in this section). Its text is not erased on mouse exit, as some text
requires scrolling. Its text is also not erased on month changes, as its prior content
may be useful (this is configurable as of version 1.2.3).
Footer text is per-window: each open month window can display a different event's data
in its footer.
Because it gives more details than an event's summary widget, but less than the full
View/Edit event dialog, the footer display is mostly a convenience, designed to minimize
full dialog opens. As of version 1.2, the footer's text
is read-only, as no changes to it would be applied; you can still copy its text
to paste elsewhere (via mouse highlighting, plus Ctrl-C on Windows), but no edits are allowed.
Note that the footer's scrollbar may be difficult to reach without entering another event,
and, due to month window size constraints, footer text may not be displayed at all both on small
screens and in months with many events in any given day. To see more footer text, try expanding
the month window, or changing the footer's font, size, and resizing options in the
configurations file; or simply left-click an event to open the full
event View/Edit dialog as needed.
This toggle shows/hides an image file associated with the window's
displayed month in a separate window.
Month images change automatically as you navigate through months, and are automatically minimized and restored
with their month windows. Like footers, images are per-window: each open month window can display a different
month image in its own image popup. As noted earlier, selecting Tandem causes all
open image windows to be updated simultaneously on month moves (i.e., previous/next month and year navigations)
along with their month windows.
Images are loaded from the images folder, in which an image filename's relative sort order
among the 12 image files present gives the image's relative month number (e.g., the first image file in the
folder by ascending sort order is used for January). The image files folder's location defaults to MonthImages
in the program's folder, and may be customized in the configurations file (see its setting "imgpath").
To use images of your own, either change the default MonthImages folder's content in-place, or change the configurations
file to point to a different folder where your images are stored.
To enable the optional images feature you must install the Pillow extension unless you are using PNG images
with a Python using Tk 8.6+ (including standard Python 3.4+ on Windows), or GIF or PPM/PGM images with any Python 3.X
(see Dependencies). This feature is basic by design in this release; images are simply displayed
in a separate window, synchronized with the associated month window. For speed no auto-resizing
is performed, so this works best if images are all the same size, and small enough
to display well in a popup window. See also Future Directions?
for ideas on this topic, and version 1.4,
1.5, and 1.6 changes which make this feature more usable.
As of version 1.2, the "?" button at top right in month windows opens this document
in a web browser.
Additional GUI actions span click modes but are invoked by clicks in the day grid, and described in the next three
sections. Among these, cut and paste operations invoked by right-clicks on events and days may be used to move
or copy events to other days and month windows; and the event selection list dialog opened by left-clicks on day-number
areas above events is useful when a day has too many events to display. See the next three sections for action triggers, and
Other GUI Actions and Usage Notes for more on cut-and-paste and event selection lists.
The additional event triggers and actions in the table below are defined for calendar days and events
in the GUI's day-grid area, when your "clickmode"
setting is 'mouse' in frigcal_configs.py.
This multi-click mode is generally suitable for devices with a mouse and keyboard.
|Single left-click (tap) event
||Select event field for summary text edits (not updated until Enter pressed).
|Enter key in event summary text
||Update event's possibly-edited summary text only.
|Single left-click (tap) day or day-number
||Move the current-day shading to the selected day (other events move shading too).
|Double left-click (tap) day
||Open Create event dialog for new event on day.
|Double left-click (tap) day-number
||Open event selection list dialog for
all events in day [see ahead for actions].
|Double left-click (tap) event
||Open View/Edit dialog for event.
|Single right-click (tap+hold) event
||Open Cut/Copy/Open options menu for event.
|Single right-click (tap+hold) day or day-number
||Paste the latest cut/copy event in this day, by opening a prefilled
Create event dialog.
|Mouse hover-in on event
||Display footer text if enabled.
On tablets, single-press or stylus may activate this.
The additional event triggers and actions in the table below are defined for calendar days and events
in the GUI's day-grid area, when your "clickmode"
setting is 'touch' in frigcal_configs.py.
This single-click mode is generally suitable for devices with only touch screens.
||There is no in-line edit of displayed summary text: use left-click
|Single left-click (tap) day
||Open Create event dialog for new event on day.
|Single left-click (tap) day-number
||Open event selection list dialog for
all events in day [see ahead for actions].
|Single left-click (tap) event
||Open View/Edit dialog for event,
and display its footer text if enabled.
|Single right-click (tap+hold) event
||Open Cut/Copy/Open options menu for event.
|Single right-click (tap+hold) day or day-number
||Paste the latest cut/copy event in this day, by opening a prefilled
Create event dialog.
|Mouse hover-in on event
||Display footer text if enabled,
and if you have a hover action (e.g., stylus or laptop).
The actions in the table below, repeated here for quick reference, are available in the event
selection lists popped up in response to day-number
left-click actions in the preceding two sections. This popup mimics a single, selected day in a day grid. For more
details, see the next section's first item, and 1.3 notes.
|Single left-click (tap) on event list item
||Open View/Edit dialog for the event clicked.
|Single right-click (tap+hold) on event list item
||Open Cut/Copy/Open options menu for the event clicked.
||Open Create event dialog for new event on day.
This section provides additional GUI-related interaction and usage details. Refer to the preceding sections
for more details on the windows and actions described.
Event Selection Lists—Viewing More Events:
The physical size of day frames imposes a limit on the number of events displayed in month windows.
As of version 1.3, you can view, edit, or cut/copy all events for a day—including any events off-screen—by
left-clicking in the day-number area above events at the top of any day frame, to open the
event selection list dialog.
This list's items look and act nearly the same as events in a day frame in a month window:
single-left and single-right clicks on an event in the list open
Cut/Copy dialogs for
the event, respectively, and a Create button opens the
Create event dialog (like a day left-click).
For more details, see this dialog's full documentation in the release notes for
version 1.3, where it was introduced. The former description that follows is retained, as it contains still-valid
alternatives and hints.
The uniform size of each day's tile imposes a practical limit on the number of events
that can be displayed per day (just as in a real "refrigerator" calendar, this program's metaphor).
To see more events, try expanding the window, or reducing event summary and other font sizes in
frigcal_configs.py. This limitation may be (has been)
resolved in a future release.
As is, if you really have too many events to display on a day even after expanding
the window and reducing summary font sizes, the recommended workaround is to combine
multiple events into one, with individual events listed in the combined event's
multiline description text. A unique title and event category/color for such aggregates
would help make them distinct.
Events may be moved and copied to other days and months as follows: right-click on an event to cut or copy
(via a pop-up actions menu); then right-click on any day or day number to paste the most recently
cut/copy event on that day, via a prefilled Create dialog,
which allows changes prior to pasting.
The event right-click Cut/Copy menu dialog and menu appear at the spot clicked, and are captured
Per the preceding note, right-clicks on event items in the event selection list dialog open the Cut/Copy menu too,
just as they do on events in day frames (though pastes are invoked only on day frames).
More on Pastes:
A cut/copy event can be pasted to a day in any open window,
and as many times as you wish. That is, you can paste a cut or copied
event multiple times to add it to many days—for instance, to manually
propagate a recurring or multiday event. Right-click the event to
copy it once, then right-click days to
paste it on as many days as desired.
As for all updates, pastes show up immediately in all windows open on the target month.
Open on Right-Click:
For convenience, the Cut/Copy menu opened by event right-clicks of the prior paragraphs
also has an Open option, which is
equivalent to an event left-click, and pops up the full event
Hence, most work can be done with right-clicks alone (e.g., cut/copy/open in event,
paste in day), plus day left-clicks to add new events.
Event Changes and Deletes:
The event View/Edit dialog, opened on event left-clicks,
allows you to view full event
details; remove the event with Delete; or change most event fields and update the calendar for the
new data with Update. You can also delete events with a Cut in the Cut/Copy event right-click menu; a
Cut also saves the event for a possible later paste, while a View/Edit dialog's Delete does not.
As of version 1.4, the event edit dialog's Cancel verifies the window close
if you've made any changes; Delete and Update do not (and neither does a right-click Cut), but these operations
can't discard user inputs, and do not update calendar files immediately. To back out unintended
deletes or updates, click the main month window's "X" close button and opt to not save changes.
Changing an Event's Calendar:
The View/Edit dialog for an existing event lets you change most event details, but for implementation
reasons does not allow you to change the calendar to which the event belongs. However, you can
move an event to a different calendar via cut/paste—cut the event via event right-click,
then paste it back to where it was via day right-click, changing its calendar in
the Paste (prefilled Create) dialog that is issued on the paste.
Event summary widget background color defaults to white, but can be configured in
frigcal_configs.py—both for all events in a calendar,
and for all events sharing a category name. Event category colors span calendars,
and override any calendar color settings. To colorize events:
Edit the category and/or calendar color dictionaries
in the configurations file, giving either a color name or
#RRGGBB hex-code string for color values.
- Enter event category and calendar names in the GUI's View/Edit and
Create dialogs opened on event and day clicks, respectively (see prior note for calendar name
changes). An empty category name in the GUI picks up either the calendar's color, or the
color of a category-dictionary entry whose key is an explicit empty string (this can provide
a default category).
New in version 1.7: both the widget background and foreground text can now be colorized,
and the pickcolor.py utility script
can be used for custom color selections; see 1.7 release notes for details.
Summary Text Visibility:
Event summary widget text displayed on days is intended for brief captions only.
In-depth notations are instead entered in an event's multiline description field.
To see more event summary text, try expanding the window and/or changing the day label
and other fonts in the previously described frigcal_configs.py.
You can also see the full summary text in the Footer display (see earlier),
as well as in the full event View/Edit dialog window opened on event left-clicks (see click
mode actions earlier in this section). The 1.3 event selection list dialog
also shows more of the summary (see earlier in this list).
Other Visibility Notes:
This GUI may sometimes resize itself based on its content. You can resize any month window
manually if its automatic sizing changes undesirably as you navigate to months with more
or fewer events displayed. As mentioned earlier, many events per day may also preclude
footer visibility in a given month, though the full View/Edit dialog shows complete details
on demand (see above).
Program messages are printed to the console; if you don't care to see these,
change the script's trace variable; redirect stdout in your shell;
or rename the main script to "frigcal.pyw" or set a shortcut to it to
be Run/Minimized to suppress the console window altogether.
Update: as of version 1.4, you may also run the new
frigcal-noconsole.pyw script to suppress the console on Windows
automatically; see the 1.4 change note.
Run the GUI live for more usage details; see the start-up overview for pointers.
This section is probably of more interest to developers than users.
The frigcal program records calendar events in portable, vendor-neutral iCalendar
(".ics") text files. These files are loaded in full in startup, and regenerated
in full on quit requests (and only after verification and a
successful automatic backup).
The iCalendar files created by this program are standard-conforming, and
should be useable in other calendar programs directly. Some other programs
may require an initial import to use iCalendar files created here, and an
export to create files usable here.
For more technical readers already familiar with the iCalendar standard,
here is a summary of its usage in frigcal:
This program can use iCalendar files created by other programs. This
program also creates iCalendar files having one calendar object per
file, and one or more nested event components per calendar.
This program recognizes and creates the following calendar component
type to represent its calendar event entries:
This program ignores and does not create these component types:
- and others (it's a comprehensive standard)
However, this program retains these component types in the iCalendar
file if already present, for use by other programs.
For VEVENT components in files, this program uses and generates the
|Property ||Role ||Type
|UID ||Unique id, for searches ||Required
|DTSTAMP ||Last modified, type=date-time ||Required
|DTSTART ||Event's date, type=date ||Required, if no calendar 'method'
|SUMMARY ||Label text (one line) ||Optional
|DESCRIPTION ||Extra text (multiline) ||Optional
|CATEGORIES ||Color map text: uses first only ||Optional
|CREATED ||To sort events, type=date-time ||Optional
Notably, this program does not use or generate DTEND, VALARM, or RRULE
specifications for events, which precludes events spanning dates, reminders,
and recurring events. You can simulate the spans and recurrence by simply
copying and pasting an event on multiple consecutive or recurring days via
right-clicks; for simplicity, alarms are outside this program's scope. See
also the suggestions for recurrence implementation ahead.
Journaling is also not supported, though event descriptions support
arbitrarily long date-based notations.
Subtle point: strangely, the UID field's value is not always unique—some
calendar programs may reuse the same UID for each instance of a recurring event.
Although frigcal doesn't support recurring events directly, this won't generally
cause problems: because frigcal indexes and locates events by UID within dates,
events with duplicate UIDs display individually and work properly as long as
events don't recur on the same day.
Though both rare and configurable, when some other programs use the calendar files
this program creates, you may see quoted-printable escapes for literal backslashes
embedded in an event's text fields. See this note for details.
For details on the iCalendar standard, search the web or see:
Less formal sources:
The following is an example of the iCalendar files this program creates,
as of this copy (see also the version 1.7
of such a file with non-English Unicode content):
SUMMARY:frigcal ics file created
DESCRIPTION:Your default iCalendar file was generated.
SUMMARY:fetch latest Windows 10 preview
DESCRIPTION:The current build makes a single-core netbook so slow that it'
s unusable.\nThis is probably due to an early tech preview build\, but nee
d to verify.\n
This section describes program changes and releases, most recent first.
In this section:
||August 27, 2016
||Event foreground colors, Unicode clarifications, utilities
||October 6, 2015
||PNGs without Pillow, Tk 8.6 colors, Linux upgrades
||March 18, 2015
||More error handling, image folder, new release structure
||February 10, 2015
||Edit cancel verify, new launcher, file format, image tweaks
||December 18, 2014
||Event selection lists, cut/copy context, tear-offs dropped
||October 14, 2014
||Icon, help, missing configs, read-only text, day shade
||October 3, 2014
||Smarter saves: changed files only
||September 26, 2014
Highlights: Event foreground colors, Unicode clarifications, utilities
This version adds foreground (text) color configuration for events, and verifies
and extends the support of non-ASCII Unicode characters throughout the system.
New screenshots capturing both the event foreground configurations and Unicode support
of this release can be found
and this version's full set of screenshots can be browsed
For colors, two-item tuples may now be used to give both foreground and background
color values for event summary text, and a new utility
script simplifies custom color
For Unicode, this release adds a utility script for
file-encoding conversions (use this for existing calendars not in UTF-8 format);
provides a precoded non-bold fonts configuration for Asian characters
on Linux (use this if your event text is unreadable); and documents source-based encoding
edits (use this if file conversions are not an option).
In addition, the GUI's Unicode
display range is described, the configs file better supports
both non-English settings and Linux end-line
format, and trace messages no longer assume your
console supports arbitrary Unicode characters.
A handful of minor items were also addressed. Most prominently, a typo was fixed in an error
and screenshot folders now have thumbnail index pages
courtesy of the thumbspage program.
This section provides more information about this version's changes.
Search on "[1.7]" in the code for implementation details underlying the following:
Event foreground colors configuration:
Event summary entry widgets, displayed on days, can now have both background and foreground
colors configured on a category-name or calendar-file
basis. Prior versions supported
only widget background color, and always used black for foreground text. The text itself can now
be colored as well, by providing a ('bgcolor', 'fgcolor') tuple for color settings,
where both bgcolor and fgcolor may be a color name or #RRGGBB hex-value string.
The former 'bgcolor' string setting still works to set background color only, making this
a fully backward-compatible extension (existing configs files work unchanged).
For example, an event summary color value setting of:
'wheat' sets the event widget's background color only, leaving its
foreground (the event text) the default black, as formerly
('navy', 'orange') sets the event widget's background color to 'navy'
and its foreground (the event text) color to 'orange'
In all cases, the default event display background and foreground are white and black, respectively.
Also note that this change applies to event summary display widgets only; other widgets' color
settings differ. See the event color settings near the end of the configs file
for more documentation and examples.
New color selector utility:
Given the pervasiveness of color configurations in frigcal, this version also adds a simple GUI
utility script for selecting custom colors. The new script, pickcolor.py,
allows its user to choose a custom color, displays the color, and gives the color's #RRGGBB hex-value
string in the GUI, ready for cut-and-paste into frigcal config-file settings (and GUIs, webpages, and any other
system that uses these values). See the utility's
screenshot for example usage.
Unicode support validation:
For this release, the system was verified to handle non-ASCII Unicode
with some minor limitations imposed by its GUI library. Specifically,
both non-English language text and many Unicode symbols can be used in event summary and description
test in the GUI,
and may be saved in and loaded from calendar
Basic Unicode support required no changes
to code, as both the icalendar and Tk libraries used by frigcal support Unicode text:
The icalendar parser/generator library uses the general UTF-8 Unicode
encoding everywhere by default. This allows frigcal to load and save calendar files
containing arbitrary Unicode text.
See icalendar for more details.
The Tk GUI library supports input and display of a wide variety of Unicode
characters. Its range allows for most characters used in modern languages and text,
subject to the limitations described in the next item.
Unicode limitations in the GUI:
As mentioned, the Tk GUI library—underlying the Python tkinter module used by frigcal—supports
input and display of Unicode characters, and handles almost all modern languages and a large number of
symbols. Like other software, though, Tk does not support every defined Unicode character, and
frigcal's Unicode display support is naturally limited to that of Tk. This support is partially
font-dependent, though Tk automatically searches all its fonts for a character's glyph to display
if absent from the font used. See this for Tk's
glyph lookup mechanism, and this for Tk fonts
in general. Tk's Unicode support is also prone to evolve like Unicode itself, though Tk is
currently years behind the Unicode curve.
More fundamentally, Tk currently seems to be limited to Unicode character code points in the
range U+0000...U+FFFF—a.k.a. the Unicode Basic Multilingual Plane, or
This effectively precludes GUI use of supplementary characters and Unicode planes outside the
BMP's range. The details and evidence behind this are fairly technical (and can be safely ignored
by most frigcal users), but include tkinter error messages, developer forums, and source code.
For example, inserting a Unicode checkmark character into either the tkinter Text
or Entry widgets with the first line of the following code works as expected, displaying
a checkmark in the GUI:
text.insert(END, 'xyz\U00002713') # works (checkmark symbol)
text.insert(END, 'xyz\U0001F383') # fails (jack-o-lantern emoji)
However, the second code line above fails to display a Unicode jack-o-lantern character,
instead generating an exception and this tkinter error message on the console
(tkinter uses Tk, which uses Tcl):
_tkinter.TclError: character U+1f383 is above the range (U+0000-U+FFFF) allowed by Tcl
Moreover, pasting a checkmark character from another source into either of these two
widgets works fine, but pasting a jack-o-lantern leaves a generic square character on Windows instead.
This is a well-known issue, which also impacts Python's IDLE, and is discussed in multiple
Tcl/Tk and Python developer threads; for a representative sample, see
this and this.
This is also called-out in Tcl/Tk's source code; per its tcl.h header file in version 8.6:
Tcl is currently UCS-2 and planning UTF-16 for the Unicode string rep that
Tcl_UniChar represents. Changing the size of Tcl_UniChar is /not/ supported.
Per all the evidence, Tk appears to currently support only the UCS-2 subset of
Unicode, without the variable-length surrogate extensions of UTF-16. If you're
too new to Unicode to know what that means, see
for UTF-16 and its more limited UCS-2 predecessor, and
for more on UCS-2's limitations.
In sum, Tk's UCS-2 range can represent only the code points in the Unicode BMP.
The net effect is that jack-o-lanterns, emojis in general, and some other silly bits
appear to be right out for the time being in Tk, tkinter, and frigcal.
The good news here is that Tk's Unicode support is very broad: modern text consists almost
entirely of character code points in the UCS-2 range, and is fully supported by frigcal's GUI.
For examples of the sorts of Unicode characters that Tk (and hence frigcal) does support,
even for portable fonts such as Courier, Times, and Helvetica, open file
unicode-cheat-sheet.txt in any
Unicode-aware text editor or viewer. For more symbols to use in frigcal,
and choose characters whose code points are in Tk's current U+0000...U+FFFF BMP range.
Also keep in mind that Tk's limits pertain to already-decoded Unicode characters
used in the GUI, and are independent of Unicode file encodings. Python, icalendar, and
frigcal can still happily load and save UTF-8 calendar-file text having characters outside
the BMP, even if the Tk GUI library has issues inputting or displaying content outside
Unicode trace messages:
As a minor accommodation to Unicode, frigcal's trace messages were specialized in this release
to avoid exceptions on terminals that don't support ASCII text, by printing Unicode '\u....'
escapes when required to produce ASCII. Trace messages are not significant to most users,
and should not trigger exceptions in any event. PYTHONIOENCODING shell variable settings may
address this is some cases, but seem less user-friendly. See sharednames.py
for the new tracing code.
Unicode strings in configs code:
As another minor upgrade for Unicode, the user configs file grew an encoding
declaration line, "# -*- coding: utf-8 -*-", along with a note and example documenting the use of
non-English characters in the file's string literals. Python defaults to UTF-8 for source-file
encoding (which handles ASCII), but the declaration line was added to ease changes to other encodings.
This scheme allows non-English text to be used in the file's strings, for settings such as file paths
and event category names. See screenshots of the latter in action
Note that the configs file is now shipped in UTF-8 form to support and illustrate
this change; see its documentation for usage details.
Unicode conversion options:
Although frigcal already supports Unicode characters in event text and files, its
Unicode model always uses UTF-8 text encoding for both existing and newly-generated
For existing files encoded per other schemes,
this constraint may be inconvenient. Consequently, this version provides a
converter script for existing files, and documents how to change the default encoding
in source files. The new options fall into three use cases, depending on your
existing files, and in decreasingly-recommended order:
- No changes or conversions: when there are no existing calendar
files, or all existing calendar files are encoded per either UTF-8 or ASCII, frigcal's
default UTF-8 encoding suffices, and no file conversions or source-code changes are required.
This case likely applies to the vast majority of users. Note that frigcal's UTF-8 default
works for ASCII files and event text too, because ASCII is a subset of UTF-8.
- Converting existing files—new utility:
for users with existing calendar files encoded
with a Unicode scheme other than UTF-8 or ASCII (e.g., Latin-1), a new file-conversion script
can be used to perform a one-time conversion to UTF-8.
See the new encoding converter script, unicodemod.py and its
usage screenshot for more details.
In short, a simple command line:
C:\...\frigcal> unicodemod.py icsfilename latin-1 utf-8
suffices to convert a file from non-ASCII Latin-1 to the required UTF-8 format.
Converting existing calendar files this
way allows frigcal to use its default UTF-8 encodings unchanged for both input and output.
Crucially, this also allows frigcal to generate newly-saved calendar files with Unicode
characters that may be outside the scope of original narrower encodings of existing files.
Because of this generality, this approach is recommended for users with existing non-UTF-8 files.
- Changing encoding in source:
for users with existing non-UTF-8 calendar files,
source-code changes offer another, though more complex, option.
The underlying icalendar package's setting DEFAULT_ENCODING
in module icalendar.parser_tools controls most or all of its Unicode behavior.
Unfortunately, this setting can be changed only in its source code (file
because it is imported throughout the icalendar package with
"from" statements before any modules outside the package have a chance to change it—thus precluding
a frigcal configs file solution. If are willing to change source code, though, this setting
might be used to avoid file conversions by changing icalendar's (and hence frigcal's) Unicode
encoding scheme globally. Its preset value is UTF-8, but any encoding name string supported
by Python is allowed, and will be used by frigcal's file processing (as of this release).
Due to the icalendar library's implementation, however, this setting is used for
both input and output file text—it will be used to both load existing calendar files and
save generated calendar files. Hence, this setting cannot be used to load a non-UTF-8 existing file and
save newly-generated files in general UTF-8. If changed to a narrower encoding (e.g., Latin-1), this setting
makes Unicode characters outside the encoding's scope unusable in event text, because they cannot be saved
in files. Users are thus encouraged to convert non-UTF-8 files to UTF-8 per option B above, unless they
are both open to changing source code, and sure that their event text will always remain in a different
Unicode fonts on Linux:
Per testing thus far, bold fonts can sometimes make some Asian characters (and possibly others) render
unreadably on Linux. On the other hand, bold seems to work well for all characters and fonts on
and most languages on Linux (here),
and non-bold seems too dull in all other contexts. It's unknown whether the lack of readability
for Linux bold fonts reflects testing parameters or a more general issue, but in either case dropping bold
font presets for a subset of users on a single platform seems too extreme a response.
To address this, bold font defaults were retained, but a new setting in the configs file,
precoded to use non-bold fonts for all event text displays. Set this to True if your non-English
characters are difficult to read on Linux or elsewhere. You may also want to use a larger font
size or a different font family in this same code on Linux, but this is subjective,
and may also need to be specialized by platform (see MONOSPACE_FONTS in the configs file for an example
of platform-specific fonts).
For examples of non-English use on Linux, see the new screenshots
view the first at full size to see its true clarity. These were taken after
editing the configs file in gedit to enable its non-bold fonts code and
increase its font sizes slightly, on a machine with default UTF-8 locale settings.
As always, your mileage may vary.
It may be possible to automate some font selections (e.g., with locale queries), but any such "clever"
automatic choices in this domain would almost certainly be inappropriate for some individuals and use
cases. frigcal instead encourages its users to configure the program for their own context and tastes.
File end-line conversion utility:
For users on Linux and other Unix-like platforms, this version includes a simple script,
fixeoln.py which can be used to convert file
end-lines from the shipped Windows format to the native Linux format, when no other
tools are available. This script is essentially a Python-coded, Unicode-aware, and
portable equivalent to the sometimes-absent Linux commands dos2unix and unix2dos,
and works for files of any Unicode encoding type. See this script's docstring,
run log and
screenshot for usage details.
For additional Linux end-line options, see the documentation of the
configs file—this script's initial motivation.
Highlights: PNGs without Pillow, Tk 8.6 colors, Linux upgrades
This version makes an install of the third-party Pillow (a.k.a. PIL) library optional for some combinations of image file type
and Python version; addresses incompatible changes in color names in the Tk 8.6 library underlying tkinter in Python 3.4
and later on Windows; and on Linux improves the behavior of modal dialogs and window icons.
As none of this release's changes impacted any existing GUI displays
no prior screenshots were retaken, but new shots were taken for the new
image load error handling,
and usage examples on Windows 10
and Ubuntu Linux. This document was also
updated and reformatted for usability.
with minor changes reflecting the fact that Python version does not necessarily
imply Tk version—it does on a standard python.org Windows install, but may not on Mac OS X and elsewhere, as described
in detail here.
Changed the configs file to pick colors based on Tk version (not Python version), and changed
the documentation in this file as well as the text of
READMEs and start-up and
error messages to better
clarify Tk version impact on month images.
Also included the latest Pillow installers for Windows,
for Pythons or image file types that don't satisfy the Tk 8.6 requirement described in #4 below.
with doc changes (new font, header, and toolbar styling; minor content tweaks;
and updated URLs for book site relocation); plus one minor nonfunctional code change (remove a pointless
staticmethod declaration for one method).
The following list provides more information on this release's changes.
Search on "[1.6]" in the code for more implementation details underlying the following:
Verified on Python 3.5, Windows 10, and Ubuntu Linux:
frigcal was verified to run on Python 3.5, and on both Windows 10
and Ubuntu Linux.
In the standard Windows installer, Python 3.5 (and 3.4) ships with and uses the Tk 8.6 library underlying Python's tkinter
GUI toolkit, and changes in the Tk library were the motivation for many of this release's code changes. Note that all 1.6 changes
are backward compatible with earlier Pythons, so frigcal still runs as before on all other Python 3.X releases. Also note that
Python 3.5 appears to speed up initial calendar file loads substantially—reducing load time from 5 to 3 seconds in one
use case, and from 13 to 8 seconds in another. Given this, and the PNG images support described ahead, Python 3.5 and later are
suggested on Windows, and Tk 8.6 is suggested elsewhere.
Color names and notes in configs file for Tk 8.6:
Tk 8.6 changes the meaning of some color names to be compatible with a Web standard—instead of the X11/VGA
standard used for the last 25 years. Namely, "green", "purple", "maroon", and "grey/gray" render more darkly
than before, and are now too dark for use as label backgrounds in most contexts. To address this, the user
configs file was
expanded with a note, and pre-coded assignments that map the four old names to their Tk 8.6 equivalents when
using Tk 8.6+ only. This is now version-specific, as some of the new names aren't available in prior Tk versions.
Tk 8.6 is standard for Python 3.4+ in python.org's Windows installers, though not necessarily so on other platforms
(e.g., Mac OS X may use Tk 8.5 with Python 3.5); Tk version can be queried with tkinter.TkVersion.
Customizable current-day shading color in configs file:
The shading color used for the currently-focused day frame is now user-customizable in the configs file.
It still defaults to the former gray, but uses the pre-Tk 8.6 rendition of gray, and allows for customizations
both for user preference and future Tk change. This new color setting, "currentdaycolor", is used both when days are
clicked, and when moving to the current day via the "Today" button or Escape key presses.
Third-party Pillow library is no longer required for some month images:
Because Tk 8.6 adds support for PNG image display, the rules regarding Pillow installs
have been relaxed.
Rather than making Pillow an absolute requirement for month images, frigcal now attempts to load image
files with Pillow if it's present, and without Pillow if it's absent, and simply catches the exception
that occurs if the image load fails.
The net result is that, depending on your image file types and Python version, a Pillow install may no longer
be required to use the optional month images display. Specifically, month images now display with just Python
itself and do not require a Pillow install for both:
- Image type PNG, and a Python using Tk 8.6 and later—including a standard Windows install of Python 3.4 and later
- Image type GIF or PPM/PPG, and any Python 3.X
For all other combinations of image type and Python release (e.g. JPEGs, and PNGs under Windows Python 3.3 and earlier),
Pillow is still currently required for the image display feature, and a
new error popup is still issued if images are enabled
and a month image cannot be displayed. The new popup is issued on each image load failure, and replaces the
former error popup that was issued once at Images toggle-on
if Pillow was not installed—the new protocol doesn't require Pillow, and allows some images to succeed if others fail.
On image load failures, a default image is also
displayed as a placeholder for the month.
Users are generally encouraged to use the popular PNG format and Python 3.4+ on Windows (or a Tk 8.6+ elsewhere), as this
both allows for high-quality images and removes the requirement to install Pillow altogether. The latter is especially desirable,
given that Pillow releases for new Pythons may be delayed substantially. As shipped, the top-level MonthImages
folder was changed to include PNG files instead of the former JPEGs, to both mirror
suggested usage and support Windows Python 3.4 and later out of the box. Change and configure as desired.
On Linux, keep modal dialogs on top:
Custom modal dialogs on Linux are now kept above their month window, by adding a transient() call for the new dialog
window. Without this call, frigcal's custom modal dialogs—event selection lists, right-click actions, and event create
and edit—pop up on top initially, but can be covered and hence hidden by their disabled month window parent before being
processed by the user. This is especially bad for the right-click dialog, given its small size; the unresponsive month window
can easily cover the dialog if clicked, leaving the GUI in an odd state. With the added call, modal dialogs can never be
covered by their parent on either Fedora or Ubuntu Linux; Fedora also erases the pop up's "X" close button, but the dialogs'
Cancel closes as usual.
This change applies to Linux only: transient() is not required and not used on Windows, as that platform's custom modal
dialogs do the right thing without it, and dialog behavior on Macs is to be tested.
See the "Dialog" superclass in frigcal.py for this change's code.
On Linux, set some window icons:
tkinter's iconphoto(), available in Tk 8.5 and later, is now used to set window icons on Linux.
On both Fedora and
Ubuntu, this change simply provides an image
for the program's icon in the activities/applications bar, not on the program window itself (Fedora also
displays a small version of the icon at the top of screen).
There are additional ways to set icons on Linux (e.g., XBM bitmap files, via iconbitmap('@myicon.xbm');
see the web for pointers.
This change applies to Linux only: the existing iconbitmap() scheme sets icons on Windows correctly
and is retained for that platform, and iconphoto() is documented as not relevant for Macs.
Search for "try_set_window_icon" in frigcal.py for this change's code.
Also for Linux users in this release:
There are new usage notes above, and the configs file automatically
translates any Windows "\" to "/" in calendar and image folder paths—a minor change but useful when
selecting precoded paths in the file.
[Caveat] On Linux, images are not hidden with their months:
Month image windows are not hidden (minimized) and restored with their month window parents on Linux as they are on Windows.
To address this, this version intended to experiment with using tkinter's withdraw() to hide image windows on Linux,
instead of the iconify() used successfully on Windows. The withdraw() call is required on Linux (only) if you
wish to restore later with deiconify(). See the new supplemental example script
__withdraw__.py for a simple demo of this Linux constraint.
Unfortunately, the experiment never got that far—on Linux (again, only), the required "<Unmap>" and "<Map>
events are never generated on window minimize and restore. Moreover, the "<Configure> event is also useless in
this context: it doesn't fire on minimize or restore on Linux either (and only on restore on Windows), and in any event
invokes a function on every size and location change. Hence, this issue has been abandoned. In the end, tkinter's
portability may be less robust than it might be, and too incomplete to support this particular intended behavior.
Run the __hide-unhide__.py supplemental example on Linux for a demo of the events issue.
Note that the withdraw() call also works on Windows, so it is potentially more portable than iconify().
However, withdraw() also prevents the image window from appearing in the taskbar along with the month window
(per __withdraw__.py). It's not clear which flavor is preferable, but the prior
iconify() behavior and code was retained for Windows on the grounds of frigcal compatibility. The event triggers
issue rendered Linux portability a wholly moot point in this instance.
Search for "<Unmap>" in frigcal.py for the related code.
The good news is that the vast majority of frigcal works and looks the same on Windows and Linux with no code changes;
isolated window manager issues might be pardoned as unavoidably platform-specific.
Highlights: more error handling, image folder, new release structure
Version 1.5 adds more explicit error checks to make the system more robust and user-friendly,
and avoid silent shutdowns when used in non-console mode on Windows. It also tightens-up image
folder handling to allow non-image files, and has a new release structure on the web.
Search on "[1.5]" in the code for changes made in support of the following extensions
introduced in this version.
Catch calendar startup errors:
When an error occurs while loading the calendar files(s) at startup, catch
the exception and display a new GUI popup,
not only Python exception text in the console window. This is especially important on Windows, where the
console window may be absent (causing the program to close without any message), or may disappear immediately
before the exception's text can be read (the notorious disappearing window feature). Startup failures are
most likely due to bad calendar path settings (hence the message's hint), though bad data and parser failures
are not impossible. For symmetry, also extended the existing backup/generate error
popups to include basic exception text,
lest the console be absent on Windows.
Catch config and library startup errors:
Also at start-up time, catch errors in the user configurations file and failed imports of
3rd-party library dependencies, and report these as GUI popups as well. The new GUI popups
here. Libraries are unlikely to fail
(they are shipped with the system), but configs file errors may be common. Like calendar errors,
these are both fatal, but the main GUI hasn't been built yet; show a special-case popup, instead of
printing text to the console (which may not be present on Windows), and wait for an OK press,
instead of keeping the console open by pausing for input (which may never come).
Handle pruning delete failures:
File deletions can fail for many reasons, including permission changes, device failures, and in-use locks;
this is especially true on Windows, where even the covert operation of the search indexer or an antivirus
scanner might interfere with deletions.
When deleting an old calendar backup file in the pruning process, don't let a delete failure cancel the entire
backup/save process. Instead, issue a warning popup
but ignore the delete and cancel the pruning step only, and proceed on to backup/save. It's likely that the next
backup/save will be able to prune the file whose delete failed.
Warn about missing image files, bad image path:
Issue a new warning popup in the GUI (once per window,
at first image toggle-on) and a message in the console (on each display attempt) if the image folder does not
contain exactly 12 image files. Otherwise, some months may appear to not load an image, because indexing a
too-short sorted files list triggers an exception, shown only in the console. Having more than 12 images would
work (ignoring items past the 12th by sort order), but most likely also reflects a user error. Also added a
check and simple error popup for bad image file paths
in the configs file; this and an existing
error popup for
Pillow-not-installed are both issued at each image toggle-on and disable images altogether, but don't stop the program.
Update: see version 1.6's relaxation of Pillow install requirements, and the
new image load error popup.
Skip non-image files in images folder:
When loading a month's image, skip and ignore any non-image files in the images folder,
by using Python's mimetypes module to determine type from filename extension. This includes any
".txt" text files present (including the folder's README.txt, which works here now);
an .htaccess web admin file (if ever added to customize indexes for 1.5's web image: see note #6);
and any other non-image items added by the user. This test is in addition to the subfolder skipping added in
version 1.4; with the new check, image file selection proceeds as follows—to
display a month image, the system:
- Lists all items in the images folder
- Removes non-file items (e.g., subfolders)
- Removes non-image files (e.g., text files, web admin files)
- Sorts by item filename
- Indexes to load the item for the month's relative number
The net effect is that users may place arbitrary subfolders and non-image files in their
image folders; only 12 actual image files are expected and used. Unknown or corrupted
image files in this folder may still fail to display, and some image displays may fail
unless you have Pillow (PIL) installed (see Dependencies).
Default calendar file on adds:
In the GUI's create event dialog, select
the "frigcal-default" calendar file as the initial default if it exists. This is somewhat arbitrary,
but the first calendar by name sort order seems always inappropriate in the system's author's usage.
New release structure, docs:
Adjusted links in docs to use a new examples structure that's the same in the
zip file and the new
website that hosts this system for online viewing:
for the web, use links relative to a simple unpacked copy of the zip file. This required adding an
.htaccess file in the top-level folder (only) for indexes, reinstating a stub top-level README.txt
file for index display removed in version 1.2, and renaming this doc file to
"Readme-frigcal.html" (see .htaccess).
Also made minor updates to this docs file, and added new 1.5 screenshots.
After its March release, this version was repackaged—most recently on Apr-29-15—with:
As none of these minor changes impacted normal functionality, a new release number was unwarranted
(and as of this release, frigcal will no longer do three-level X.Y.Z point releases due to their
Development on this version is expected to taper off after its final repackaging in late April, 2015.
- Assorted documentation changes in this file (formatting, typos, etc.), and 3 new/updated screenshots
- A comment about reading and writing calendar files all at once in icsfiletools.py.
- A new example-only file related to the prior item, __chunkio.py__.
- try/finally wrappers around a read and write in icsfiletools.py to ensure closes for non-CPython use (e.g., PyPy).
Search for "finally".
- Minor code changes in icsfiletools.py for a very unlikely save of a calendar whose last event has been removed.
- A one-line code addition in frigcal.py to equate the Escape key ("Esc") with a Today button press,
for keyboard users. See "<Escape>" binding.
- A one-line code addition in frigcal.py to force focus to the month window instead of its image (if any) on window restores,
for keyboard users. Otherwise, a mouse click is required before using bound keys (like Escape). This subsumes the prior 1.4 month
window lift (raise) operation on restores, which isn't enough to set keyboard focus. See "<Map>" event in the code.
Highlights: edit cancel verify, new launcher, file format, image tweaks
This release adds a grab-bag of usability enhancements, and addresses calendar file formatting.
The following lists document this version's minor enhancements.
As none of this release's changes impacted any other GUI displays, only a handful of
new screenshots were taken—the new edit cancel verify dialog,
also showing one of the new month images;
the startup message of the new Windows no console launcher;
and the augmented backup/save error dialogs.
Search on "[1.4]" in the code for more implementation details underlying the following changes applied
in this release.
Verify Cancel if any data changed in event edit dialogs:
Added a close confirmation dialog
to the Cancel button in event edit/create dialogs, issued if any changeable field
has been modified. Previously, Cancel simply closed the dialog, whether data was edited
or not, potentially losing user changes. Closes now happen only if confirmed in a popup
dialog, when any input field has been changed (but are still immediate if no inputs
have been updated). This verification is also run for the edit dialog window's
standard close ("X") button.
Related note: as is, the Delete
button in event edit dialogs does not verify its operation, because, unlike Cancel, this won't
discard user inputs in the form, and Delete does not update calendar files immediately—files
are updated only on quit button clicks which verify their action, allowing
Deletes to be abandoned (albeit at the cost of other session changes). Delete could
easily verify too, but this may be just enough popups to be dismissed by users blindly.
Update does not verify changes for the same reasons (nor does right-click Cut; at some point,
users must be trusted, not annoyed!). Cancel the save on quit to back out any inadvertent or
unwanted deletes or updates.
Order day's events by creation date year before month:
Fixed a minor issue regarding the sort order of the events displayed
within a month window's day tiles (and in their corresponding event
selection lists added in version 1.3).
Event labels in each day are now are ordered and thus grouped by calendar filename,
and then by creation date (year#, month#, day#) within calendar group. Formerly,
these events were instead inadvertently grouped by creation (month#, day#, year#)
within calendar group, such that an event's month was more significant to ordering
than its year (e.g., January, 2015 would appear above December, 2014 on a day's tile).
This was due to the order in the tuple returned by the event class's __lt__
operator method, used by sorts; this method now correctly places year first.
That said, this change is more minor enhancement than repair. Most users probably
didn't rely on—or even notice?—the date ordering of events, for 3 reasons:
a given day typically does not display enough events to make order significant
(though the 1.3 selection list makes days with
many events more manageable);
the intended creation date ordering within a calendar group was only tersely documented
(calendar filename grouping was emphasized);
and it's not clear whether an event's creation or modification date should be used for
this ordering (creation date is preferred when present, but this seems arbitrary).
Events could also sort on creation time, but are not; time is generally ignored by frigcal.
See also the related ordering TBD below.
New no-console launcher for Windows:
Added a new script, frigcal-noconsole.py, which launches the main
frigcal.py script without opening a console messages
window on Windows platforms. Drag this out to a desktop shortcut on Windows
if desired. This launcher spawns the main script, and issues a
startup message window
that closes automatically after a brief timeout. The console is mostly meant
for tracing and debugging, and not generally useful for end users.
See the launcher script's docstring for more on Windows consoles; it's also
possible to suppress the Windows console by renaming the top-level script with
a ".pyw" extension, or marking a shortcut to the ".py" as Run Minimized,
but the new launcher is more automatic and allows the ".py" to still be used
when a console is desired.
Three related notes: First, rather than closing its message window
via a simple timeout, the launcher could be more precise (e.g., delete and poll
for a file to be created, or use an IPC tool like sockets); that seems overkill in
this context. Second, Linux and Mac users may use the new script too (after making
frigcal.py executable), but there's not much reason to do so; scripts in these
platforms are generally launched with shell command lines or platform-specific devices.
Finally, some error messages or context may go only to the console (e.g.,
configs file syntax errors did, until 1.5);
despite its seeming inconvenience, you may still want to run console mode in
some contexts, especially if errors close the program or GUI silently.
To put the preceding point more strongly: system errors happen. They can be triggered by in-use
locks, read-only permissions, device failures, and more. Version 1.5
later added GUI popups for more types of startup and backup/save errors to make handling more user-friendly,
and avoid silent shutdowns in non-console mode. Still, some error context text (e.g. tracebacks) may appear
only on the console, and may be crucial in debugging. Although frigcal is generally very robust, and goes to
great lengths to save multiple backup copies of your calendar data for safety, running without its console
may limit options on some errors.
Drop extra '\n' added by tkinter at end of description field:
The tkinter Text widget always adds an extra '\n' newline at the end of
the full text fetched from it. These are now forcibly stripped—dropping
all '\n' (only) at the text's end, and putting one '\n' back in case some ICS
parsers balk at empty (but legal) field values. Prior to this change, the
system added one new '\n' to an event's description in the calendar file
per event update or paste. This was harmless, and the extra space trivial
(just 2 bytes per '\n'), but these could accumulate needlessly. Stripping
doesn't effect change tests, because it is immediate on text widget fetches.
this could get Text to END+'-1c' instead of stripping, but strips drop any
extras already present from prior releases (albeit dropping any intended but
pointless blank lines at the end).
Help on backup/save errors:
In the very unlikely event that a calendar backup/save operation fails, the
resulting error dialogs for
save are now
augmented to include a pointer to the new help text above for
more details on restoring from automatic backups and other possible resolutions.
Retain literal backslashes in user-input text:
Added a new configs file setting, retainLiteralBackslashes.
When True (its default), data translations ensure that any literal '\' characters in
text entered in the GUI's Summary or Description fields are retained verbatim when data is stored
to and fetched from calendar files.
When False, no data translations are performed, but some '\' characters in user inputs will
be interpreted by the underlying ics library as escape sequence prefixes, and discarded when saved
to a file and loaded in a future session.
Although '\' characters are likely rare in user-input text, the new setting allows them to be retained
or interpreted explicitly. Retaining—the default—makes sense in most use cases, as
user-input is generally meant as entered.
In more detail, to retain backslashes, the system escapes and unescapes them using common
quoted-printable notation—translating any and all '\' to and from '=5C'. You won't see these
replacement characters in the frigcal GUI, as the translation happens automatically on calendar
file save and load. For example, a user input [a\nb\nc\;d\,e"f\"g] becomes [a=5Cnb=5Cnc=5C;d=5C,e"f=5C"g]
in files only, and is restored to its original form when reloaded in a new session. Without this step,
most literal '\' characters would be dropped, and a two-character literal '\n' translated
to a one-character newline.
This translation is switchable because:
(1) you may wish your backslashes to denote escapes (e.g., allowing an entered '\n'
to become a newline);
and (2) the escape mechanism risks loss of file or data portability,
if you use a calendar file containing such escapes in a GUI that doesn't recognize or undo them.
The latter is a non-issue if there are no '\' or frigcal is the sole file user; its likely
worst case just means you may see rare and isolated '=5C' instead of '\' in another GUI's event fields.
See source file icsfiletools.py for more details; while functional,
this may prove to be a temporary workaround, pending a better way of dealing with the underlying
library's data mutations.
Month Image Changes
Explicitly exclude subfolders in the month image files folder:
When loading and displaying a month's image, the program now explicitly
ignores non-files in the month images folder. This makes it
less error prone to store alternative image sets in subfolders nested within this folder,
to be copied up or selected via configs file settings. As it was, the system
simply fetched, sorted, and indexed the image directory listing's names on month number
without regard to entry type. This allowed some subfolders, but only accidentally—as
long as subfolder names were all lexically greater than all month image filenames.
This was true if image filenames all began with "NN_" digits following the pattern of
images shipped, but this is unreliable (e.g., a "02_X" subfolder name that sorted among
the true image names would have caused exceptions for its relative month).
Update: as of version 1.5, non-image files are
also ignored in image folders.
Alternative month images sets and online zip files:
Added additional and alternative sets of month images designed
and contributed by third parties. One is capture in the new
These image sets are shipped in the AlternateMonthImages subfolder of
the program's MonthImages folder,
and enabled by the GUI's Images toggle.
To use these instead of the original set, either copy their files
up to MonthImages, or set the image file path variable in the
frigcal_configs.py file to point to a set's subfolder.
See that file's "imgpath" setting for examples, and the MonthImages
folder's README.txt for more details.
Alternate image sets—possibly including others not shipped
with the program—are also available online as zip files
at this location:
Download a zip file from there, and unzip its image files into your
images folder, either directly, or as a subfolder
whose name you list in the configs file.
The program's main zip file includes the original images set and a handful
of alternates, but larger sets may not be included due to their added size.
Watch for additional image set zip files to appear at the online collection
in the future, as time, interest, or contributions allow, and see the
MonthImages folder's README.txt
file for pointers on creating sets of your own. Item #7 above allows for arbitrary
image set subfolders, whether shipped or custom.
Update: due to lack of time, as of version 1.6's release, the online
alternative image sets have not yet been updated or maintained. Barring new activity
on this front, use the image sets shipped with the frigcal package's zip file,
or create some of your own. See the new support for PNGs without Pillow in
version 1.6 which makes custom sets easier to deploy.
Automatically hide/unhide image windows with their month windows:
When a month window is minimized/restored, automatically minimize/restore
its associated month image window, if its image is enabled.
Image windows are strongly associated with their month windows logically.
This change makes the pairing literal—treating an image as a dependent
extension to its month window, automatically hidden/unhidden with the
month in tandem. Image windows may still be hidden and restored
independently, but month window hide/unhide operations are always propagated
to the associated image. Without this linking, images loitered on the display
disjointedly when months were minimized.
Month image windows: non-resizable, position configuration:
Make image windows non-user-resizable; resizing was pointless,
as the program sizes the window to its image, but never vice-versa.
Also, add a new configs file setting, "initimgposition", giving the
initial position of all image windows (e.g., to always open a horizontal image at the
'+0+0' upper left corner initially). By default, the underlying window system still
chooses starting positions. This mirrors the existing position option for month windows.
Using a fixed starting position means image windows may overlap each other when opened,
but this is irrelevant to single month-window sessions, and all windows may be moved freely.
Making a window non-resizable also makes it non-maximizable on Windows, but logically so;
minimize/restore still work.
Code refactoring—move two dialogs' code to classes:
For readability, moved the code for the cut/copy and select-list dialogs
from their former nested appearance in event handlers, to new top-level
classes. This also allows modal wait-state code to be factored
out to a common Dialog superclass, and shared by all 5 dialog classes
(the 2 new classes, plus the 3 preexisting event edit dialog classes),
instead of being repeated in-line; it's not much code, but redundancy
is almost always a Bad Thing in software—multiple copies imply
multiple future edits.
Code cleanup—rename callbacks "actions":
To avoid overloading the term "event" (used for user-created events on days),
renamed callback handlers to use "actions" universally. For example, the
arguably-confusing method "register_event_events" became "register_event_actions".
TBD—event reordering isn't immediate on adds:
There remains an open but minor event-in-day ordering issue, related to item #2
In short, a day's events are not reordered immediately when a new event is added
(by Create), but only on the next
refresh of the containing month's display. It would be simple to refill the entire month
window, but that may be slow enough to "flash" the display on slower machines.
A more custom coding to refill just one day (in each window displaying the subject month)
would be better, but seems complex enough to warrant a pass for now, as the ordering of
events on a day tile seems likely to be minor or irrelevant to most users.
As is, navigate forward and back (or select Today for the current month) to
refill and reorder if desired.
Highlights: event selection lists, cut/copy context, tear-offs dropped
This release focuses on the new event selection list dialog, but also adds more usability extensions.
The following list chronicles this version's changes;
search on "[1.3]" in the code for more implementation details:
Event selection list dialog:
Specialized left-clicks on the day number area—the area with the numerical day number
at the top of each day frame of month windows, above any events for the day—to open a
new event selection dialog. The dialog displays
all the events in that day in a scrollable list, and supports view, edit, and cut/copy actions on an unlimited number of
This new dialog is designed to look and act nearly the same as a day frame in a month window to make it intuitive.
For instance, clicks on an event's summary text in the list work largely the same as clicks on event
summary fields on day frames, with the following triggers and actions:
|Single left-click (tap) on event list item
||Open a normal
View/Edit dialog for the event clicked.
|Single right-click (tap+hold) on event list item
||Open the normal
Cut/Copy dialog for the event clicked.
In addition, selection lists and their items also mimic the appearance of day frames and their event fields,
by applying the same background color and font configurations used for days and events,
and colorizing list item events by calendar/category just as on day frames. The net effect is a
dialog that is essentially a larger and more flexible version of a month's day frame, opened on left-click request.
This enhancement addresses the physical size limitation of day frames, which by nature constrains the number of
events that can be displayed—a former TBD and futurism. If there are too many events in a
day to display all in the day's frame widget, you may now view and edit any of them by clicking on the day number
area to open the new selection list. This can be useful in general, but may be especially pertinent on small-screen
devices such a tablets.
The new listbox dialog is modal (blocking) to
avoid the need to update potentially many listboxes. It also includes a Create button for adding a new
event on this day via the normal Create
dialog—a fallback option which works just like just like a left-click on the day's background in general,
and allows new events to be added even if no more day frame background is visible below events. The new selection
list dialog is skipped if there are no events on a given day, making a day number click the same as a day frame click,
just as before.
Implementation Notes—this feature works as planned, but comes with a few tradeoffs:
- Both single left- and right-clicks in the listbox always activate the item under (really, nearest to) the
mouse click location. This model is simple, and the new context label on Cut/Copy dialogs helps clarify a subject after
right-clicks (see ahead in this section). Nevertheless, this is a subjective choice. This dialog could
instead use double-left-clicks for activation, reserving single-left-clicks to select an item to be
activated on right-clicks. That would require extra (and per the next note, pointless) user steps, though, and seems
more likely to confuse.
- Although the event selection list looks and acts nearly the same as a day frame in a month window, the mimicry
is not fully complete. The behavior of clicks in the list itself is not specialized by click-mode
(single left- and right-clicks are used for both 'mouse' and 'touch'); and a left- or right-click outside (below) the
list's items does not act as a create or paste as it does on day frames (it instead simply uses the
last item in the list for the click action, the default behavior for listbox widgets). On the other hand,
the forgery is close enough to be intuitive, and a distinct single-left-click for 'mouse' mode seems pointless,
given that summary text is not editable in a list as it is on day frames.
- The series of interactions for right-clicks in the listbox can be fairly long, and potentially confusing:
they may span three actions and dialogs, or more—left-click on day number, right-click in list dialog, click in
Cut/Copy/Open dialog, and possibly an action in the View/Edit dialog for Opens. As this sequence is envisioned as an only
rarely-used failsafe to support cut/copy for days with too many events to display, it probably suffices. If you just
wish to edit/view an off-screen event, though, use a simpler left-click in the list dialog.
- It may not always be obvious that events are off-screen, if events don't appear clipped. Adding an explicit
indicator for days with too many events to display is possible, but may require geometry calculations, and would
clutter the display too much for a use case that is likely atypical, given the program's intent. Instead, to
determine days with too many events, either resize the month window as needed, or simply left-click day numbers for
days that appear full.
Cut/Copy context label:
Added the event's summary text as a label on the Cut/Copy (and Open) right-click dialog, to give the user some context
of the event being operated on. This is usually obvious after right-clicking an event on a day frame, but becomes
more murky when right-clicking an event entry in the new selection listbox. The new right-click dialog and menu with
new context are captured
Drop right-click menu tear-off:
Disabled the default tkinter dashed-line in the Cut/Copy/Open menu, as it's pointless in a modal
dialog that is immediately erased (it's useful for quick access to actions in more persistent menus).
The new right-click menu pull-down is captured here.
Better event dialog titles:
Changed the action names in event dialog titles for clarity. The former "View/Update/Delete Event" is now simply
and the former "Create/Paste Event" is now specialized by usage mode to be either just
"Create Event" or
"Paste Event," better differentiating its role. The dual "Create/Paste" seemed especially dubious, as it was
not always clear that prefilled but editable data was present for a paste; the dialog seems otherwise difficult
to distinguish from an edit or create.
Edited code to use more descriptive names where warranted, especially for callback handler names that
now pair triggers with actions. Also updated some internal documentation for clarity. Considered splitting
off event dialog and view-date classes to separate modules, but punted—this is a tradeoff at best,
as it can make searches more complex (you must search N files and windows instead of 1), and adds module coupling
and conceptualization complexity (especially given the strong component relationships).
Updated this file for this version's changes, and rewrote much of it substantially (an open-ended process).
Took new screenshots for changed content in this release only—the new selection list, the new event dialog
titles, and redesigned right-click dialog and menus (see the main screenshots list).
Prior screenshots are from 1.2, but were not retaken, as their content has not otherwise changed, apart from
the "1.2" in their titles and the redesigned doc page in some backgrounds.
Highlights: icon, help, missing configs, read-only text, day shade
This release adds an assortment of usability features inspired by regular use.
See the new screenshot
for a quick look at some of this version's changes.
The following lists itemizes this version's changes.
Search on "[1.2]" in the code for more implementation details.
Windows icon: Added a program icon for Windows, "frigcalset.ico", generated using the new
Python-coded iconify.py example script.
Attached to all top-level windows automatically; can also be attached to a frigcal.py
desktop shortcut manually, via right-click + Properties + Change Icon.
Help button: Added a "?" help button at upper right, that pops up this HTML file in a
web browser portably, via the Python webbrowser module.
Start position: Added an initwinposition setting in the configurations file
for month windows' initial position, in pixels offset from the screen's upper left corner. This may
help on tablets with smaller screens, but causes each Clone to pop up at the same spot.
Allow missing configs: Wrapped the user configurations module in an attribute
proxy class, such that missing settings issue warnings and use defaults, instead of causing the
program to exit (syntax errors and most invalid settings in the file were already handled). Configuration
settings may be missing in a configs file due to user error or upgrades to newer versions with new settings
(e.g., prior item). Code: sharednames.py (changed),
Read-only footer text:
Made the footer's overview text area non-editable, as any changes made there would be ignored.
Requires the Text widget's state to be set to normal/disabled around deletes and inserts in
onEnter_Event(). This widget has no readonly state, and disabled state prevents even programmatic changes.
On Windows and Linux this works well: it does not alter the text's appearance, and still allows copying and pasting
text from the footer (via Ctrl-C/Ctrl-V on Windows and highlight/middle-click on Linux), but nothing else. This
is contrary to most advice seen on the web—a forum which also made the curious recommendation of an infinite
loop instead of wait_visibility() for Linux dialogs. This change may or may not have unintended consequences on Macs;
please report any issues.
Move day shade on mouse single clicks:
When click-mode is "mouse" in the configurations file, a single left-click on a day
now moves the current-day shading to that day; a double left-click still opens the Create dialog as before. The
"touch" mode has only single-clicks for days (which both moves the shading and opens the dialog),
and other day and event clicks already moved the shading. The shading is only a visual cue, but useful nonetheless.
See also the version 1.6 change that makes the current-day shading's color configurable.
Minor "touch" mode fix: Fixed a number-arguments code error that disabled event single-left clicks when
click-mode was set to the non-default "touch" in the
configurations file—functionality that had not been recently used (user testing matters!).
Regenerated all screenshots for 1.2;
thoroughly overhauled the docs in this file;
and removed old README.txt file, which was just a stub that redirected to this HTML file.
(This file was added back in version 1.5 for indexes when viewing the unzipped version on the web.)
This version was repackaged with only minor changes that did not warrant a new major release number here or in the GUI:
- 1.2.1, Oct-31-14: Minor doc updates—primarily, notes in this file about non-unique
event IDs, and event summary display sort order.
- 1.2.2, Nov-07-14: Minor doc updates—in this file (colored headers), and in code
files (a handful of typo fixes in program comments).
- 1.2.3, Nov-10-14: Three minor changes—
- doc updates in this file (an ongoing task)
- warning messages enhanced for rare missing-config-setting case in attributeproxy.py
- footer text code additions for read-only mode with no significant behavior impact
For the latter of these—(a) set state=disabled initially in onFooterFlip(), else text is
editable from initial footer enable till first fill on hover; and (b) change state=normal/disabled around delete
in clearfooter() called on month navigations, else the delete is ignored (but don't clear the text on
month changes anyhow, unless switched on by a new but optional configs file setting, "clearfooter";
this was a TBD and the clear was being ignored as intended, but only accidentally).
Highlights: smarter saves—changed files only
New in this version:
On window close button presses (a.k.a. "X" and quit), don't backup and
write the file of a calendar if it has not been changed since program startup or its most
recent save. Only calendars newly changed (by updates, deletes, adds, cuts/pastes,
or summary edits) are regenerated on close; this both is an optimization,
and further minimizes data loss risk. If no calendars have been changed,
the backup/save dialog is skipped altogether, and the exit verify appears.
Search on "[1.1]" in the code for changes.
Highlights: initial release
Fully functional. This version
was current when frigcal was first posted on python-announce.
As a wrap-up, this section provides a few words of perspective on this project's goals.
Because this program was written as a personal-use tool, describing its motivation
requires first-person narrative. Much of this section is fully optional reading
if you're not interested in personal experience or opinion, but see its final part
for future enhancement ideas.
In this section:
I wrote this program because:
- Sunbird trashed my data.
The open source
calendar program erroneously lost 10 years of data
in my main iCalendar file—it dropped all data in the file during one of
its very frequent updates leaving it completely empty, and I had to resort to
restoring a weeks-old manually backed-up archive file copy. This should
never happen: something as important as calendar data should be saved only
on request or exit, and should be backed up before being overwritten. As is,
Sunbird updates an iCalendar file on every edit, with no backup of any kind.
Sunbird also provides functionality I never used, and changes the number of weeks
in its month display dynamically, which was visually confusing. Given its fixed
interface choices and looming data loss risk, using Sunbird was no longer an option.
In its defense, Sunbird is no longer maintained and has been subsumed by a much
broader solution, and has other usage modes that may not use iCalendar files
directly; neither of these solutions were relevant to my goals.
- Other free options seemed too simple, too complex, or too connected.
I could find no other open source or free alternatives that suited my tastes and needs.
Some seemed toyish—possibly because Outlook's dominance in the domain limits
developer interest. Others seemed either radically over-engineered
or too web-centric
(e.g., Google Calendar):
I need just a calendar, not a grandiose organizational paradigm, and not a system that
requires a network connection, web browser, and account login just to look up a date or event.
Email integration seems overkill for a calendar, and people really aren't always online,
despite what you may have heard. Moreover, reworking any of the open source options would
be much more work than starting from scratch, especially when coded in other, more tedious
programming languages than Python. And we'll leave here aside the implications of having to
log into a server account managed by a third party whose bread-and-butter is tracking people's
lives for targeted advertising, apart from saying that privacy matters—"free"
online calendar providers are also free to
read your data.
- Outlook isn't an option for me.
Outlook may be pervasive,
but has major downsides—it is no longer included in basic Office bundles
and must be purchased separately; has quirks that drove me to Sunbird years ago;
is far too feature rich for my use case; seems to support the open/portable
iCalendar format only grudgingly; and may move to a subscription model in the
future, and/or be used to force users to Microsoft's cloud service.
The latter point is subjective, but alarming; Outlook's owners have lately taken to
parroting a "mobile first, cloud first" mantra that seems a dark omen for
developers and users alike. Cloud storage especially comes with some very
More pragmatically, Outlook's utility just doesn't justify the cost and vendor
dependence to me. In the same vein,
(a.k.a. iCal) shares many of Outlook's platform-specific downsides, as well as Google
Calendar's cloud-focused negatives (and there are still a few people on the planet who
don't use Apple hardware for such reasons).
Taken together with paper calendars' inherent limitations, a custom coded solution
My use case requires only a simple "refrigerator"-style personal calendar, that uses
a portable data file format, and allows notes to be displayed and edited on days freely.
I don't use or require email integration, task planning, journaling, reminder alarms,
meeting invitations, and the like—specific tools and usage modes which the iCalendar
standard oddly seems designed to formalize and even mandate. I also don't have events
that recur or span multiple days often enough to justify the complexity of their automation
in this release. Some of these may be useful to others (and a few may show up here in future
versions), but they also scale up program size and complexity in ways
that can lead to error states and data loss, and make user interfaces unwieldy and unnatural.
Those goals and constraints make this program limited by comparison to some, but this is by design:
it works, does what I require, and doesn't grow wildly complex and error-prone for
rarely-used features. As open source, it can also be expanded as desired
if you miss some utility here; in fact, its code was written with a primary
focus on readability for this purpose. There are ideas for extensions in the
next major section, if you care to experiment yourself.
In its current form, this program is 4,002 lines of code among 11 source files at latest
count. This includes comments, but not this documentation file, demo files, or any libraries or packages employed;
see and run the included __sloc__.py to count live.
In terms of effort, this system required just 3 weeks of initial development time, during which 3 versions
were coded—first using functions, then using classes for structure, and finally using refactored classes for
edits. Later releases naturally consumed additional occasional time, raising the project's total span to
3.5 months, when that tally was last updated here. (Later stats: the 1.4 release took an additional week
and a half; 1.5, 1.6, and 1.7 required roughly 1 week each; and the project has spanned 2 total years as
of version 1.7.)
Those fairly small sizes and short schedules are due in part to the existence of the complete 3rd-party
iCalendar parser/generator package in the Python open source world—a commendable
library which accelerated development, but on which this program's correctness is also very much dependent.
This is an inherent tradeoff in the "batteries included" paradigm promoted by tools such as Python.
The good news in this case is that icalendar has worked without flaw for two years as of this writing.
Despite such tradeoffs, the results seem compelling: besides serving as a supplemental example
for readers of my books, I use the
frigcal calendar GUI on a regular—and often daily—basis,
as part of a personal tools set that also includes the
mergeall directory synchronization
as well as examples from the book
Programming Python, 4th Edition, including:
For more on this latter set of programs, see this page.
Although there are precoded alternatives to most of the above, custom-coded programs like these serve
real-world purposes; are widely portable; have fully-known behavior; and may evolve entirely at their
author's discretion. As usual, when equipped with a tool like Python and bit of free time and coding skill,
one need not necessarily settle for software that is limited, proprietary, intrusive, or buggy.
This program works as intended today, but is naturally open to evolution. Here are
some ideas for future enhancements, and a few early implementation musings.
- Day's Events List—Resolved in 1.3:
This issue was addressed in version 1.3, with the
new event selection list dialog,
triggered by left-clicks on the day number area, and described in the
1.3 release notes earlier in this document.
The prior description that follows is retained, as it contains other and still valid workarounds;
in the end, this extension proved useful, and perhaps even required for some use cases.
As described earlier, there is by design a practical, physical limit
on the number of events that can be displayed on a given day. This varies by font, window,
and screen sizes, but can be just 3 to 5 in some contexts, and even 3 or 4 events per day is
enough to limit the footer text's display on some smaller screens (though
details can still be viewed in the event dialog with a click). A popup triggered by a button for
days having more events than a configurable maximum is feasible, but would naturally complicate
the GUI and implementation.
The workaround mentioned earlier—when needed, combining multiple events into a single
event, listing them individually in the combined event's description text—seems reasonable for
a personal-use tool. This GUI generally strives to show useful information without requiring extra
interactions, in keeping with its "refrigerator" calendar metaphor; additional clicks and popups
that encourage long event lists might violate this goal.
- Android (and Other) Tablets:
This program has been used on both laptops and Windows 8.1 and 10 tablets. It might also
work well on an Android tablet, though most phone screens may be too small for its display
to be usable. This would require porting Python's tkinter GUI library to Android,
but a port of its underlying Tcl/Tk support is already underway, and a tkinter port
would enable a host of open source Python tools. For more details, see
and watch the web for developments on emerging tkinter platforms.
At present, this idea seems sorely in need of traction.
Apple iPad tablets are another obvious candidate platform (but their proprietary architecture seems
less likely to support this program), and Linux tablets likely run this program as is (though
their market remains to be seen). Naturally, frigcal does run on all Windows tablets today,
as a normal desktop program; see the screenshots section for examples
of it in action.
- Frozen Executable:
This programs seems a prime candidate for distribution as a frozen binary, which does
not require a Python install step to run. See cx_freeze, Py2Exe, and PyInstaller for
packaging options. At first glance, the MS Visual C++ DLL requirement on Windows may
complicate this in some cases (if users must install a DLL, why not just install
Python instead?), and Python 3.X support may still be sketchy as well.
- Recurring Events:
A likely next step is implementing basic recurring events: weekly, monthly,
and annual events. Currently, these can be simulated manually by cutting and pasting events
on recurring days, just as one would do on a paper refrigerator calendar—this
program's target usage model.
That said, automatic recurrence might be implemented with iCalendar RRULE file entries
for events; new tables for these events, indexed by weekday, day number, and month+day; and
additions in the GUI's event View/Edit dialog to specify repeats. In fill_events(), recurrence
days in the displayed month matching the new tables' entries would display a virtual event,
but clicks on these events would all open the original event object for which the RRULE
recurrence was created (unless individual instances are allowed to differ, a usage mode
that seems both subtle and complex). Recurrence dates that do not exist in a viewed
month would also pose dilemmas (e.g., what to do for events that recur on the 30th of every
month, when viewing a February?).
In any case, note that the iCalendar standard supports a very rich range of
recurrence specifications; this program would probably be best served by implementing a small
subset for simplicity, recognizing and generating RRULE file entries with known recurrence
- Spanning (Multiday) Events:
Events spanning days may seem a logical later addition, but may also violate the simplicity
goals for the GUI and its code. This raises subtle issue with events that cross month boundaries,
and others that seem to stretch the calendar metaphor to its breaking point (e.g., should events
that span days also be allowed to recur?). Some of these may be too complex for a basic calendar
program, and event cut/paste seems a far easier option for spans—in terms of both
implementation and usage.
- Images Option Ideas:
As mentioned previously, the Images option's current popup window model
is tentative, and prone to change. This was initially intended to imitate the photos that fill
the top half of a true refrigerator calendar, but may be prove too distracting and gimmicky in
a GUI. A future version could show resized photos on the month display itself, or via a pulldown.
Update: the related enhancements added in 1.4 and 1.5—alternate image sets,
subfolder and non-image file skipping, initial positioning, and pairing of images to their months for window
hides/unhides—make this feature more useful and solid (e.g., it's easier to create new
month image sets of your own, and images are now more strongly associated with months in the GUI);
see 1.4 and 1.5 release notes. Version 1.6
further improves this story by making the third-party Pillow install optional for some image file types
and Pythons (e.g., PNG and Python 3.4+).
Given the changes, images now seem a likely permanent feature, though still subjective enough to be optional.
- Event Search:
It may be useful to provide search functionality that locates events with matching keywords or
content. A pop-up selection list would direct the GUI to the month and day of the located and
selected event, or to the event itself. This might be particularly useful for calendars with many
years of events (like those of this note's author), though use of particular keywords seems
unlikely to be consistent in the dim calendar past.
- Event Drag-and-Drop:
As mentioned earlier, event drag-and-drop move operations were omitted
intentionally, due to the general inaccuracies of drag-and-drop on tablets and other touch
devices, especially for desktop-metaphor interfaces.
Indeed, drag-and-drop can be triggered much too easily, and seems to occur accidentally as
often as deliberately in a Windows file explorer. In a calendar loaded with events,
unintended drag-and-drops could be common, and are more likely to be annoying than useful.
Instead, more deterministic right-click cut-and-paste operations are
provided for moving events between days and months.
Still, ergonomics are always partly subjective,
tkinter does support drag-and-drop operations, and this is a candidate for exploration.
To pursue on your own as an extension,
see Python's tkinter.dnd module in its Lib\tkinter folder for one interface.
This may or may not require GUI redesign.
- Tandem Minimize/Restore:
As described earlier, the Tandem button causes all month view widows (and their images) to navigate
in synch between previous/next months and years. In principle, Tandem could also cause all month
windows (and their images) to minimize whenever any is minimized, and restore whenever any is
restored. That is, all windows would open and close in synch if Tandem were enabled.
This isn't implemented because it's not clear that it's useful or desirable. Specifically:
Need: Because both screen size and GUI complexity pose natural limits on the number of usable windows,
most users seem likely to use a single month window most of the time, and at most two in general.
Need: It's easier to open and close multiple month windows manually than it would be to repeat
navigations in multiple windows manually (images already are opened and closed with their months).
Validity: There are valid arguments both for and against this behavior—while it may save a few clicks in
occasional multi-window usage, some users may very well prefer that individual windows stay open independently.
Hence, this seems too rare a use case and too extreme a policy decision to mandate without user feedback.
If you're a user and have any, please contact the system's author (see the top or bottom of this page).
Or try coding this extension yourself—see the open/close pairing of images to month windows on
"<Unmap>" and "<Map>" events in frigcal.py for hints, as well as
portability issues on Linux (see version 1.6).
- Refactor as Attachable Component:
As shipped, frigcal is oriented towards running as a standalone program, albeit with multiple
copies (clones) of its main window. It may be a minor translation to recode it as a tkinter
widget package, that may be attached to other GUIs as a nested component. Essentially, the
'root' passed in would have to be variable, and not hardcoded as a new Tk() or Toplevel();
the latter would be only precoded use cases for leveraging the code as standalone program.
This wasn't pursued, because the frigcal GUI seems too large to be useful as an attached
component. This is especially so when there are events displayed on days. Moreover, frigcal
is probably overkill if you want just a view-only calendar of eventless days (though its code
may serve as inspiration for such a component). See the book
Programming Python for more on coding
GUIs that serve as both program and component; its PyEdit
text editor and PyCalc
calculator are prime examples of such dual-mode code. PyEdit, for example, serves both as standalone program,
and attached component in PyMailGUI.
- And so on:
This program has not yet been formally verified on Mac OS X; a user reported initial success,
but testing time and budget has not yet allowed more rigorous validation. Consideration was
also given to using a PyEdit component for event description edits in frigcal's
instead of the current simple scrolled text; this would add search and cut/copy/paste buttons,
but was rejected because it would also add GUI clutter for rarely-used utility, and frigcal's
simplicity seems one of its most valued features—which will suffice as a segue to this
document's next and final section.
One of this project's underlying goals has been to produce a useful and intuitively usable
tool with no more functionality than is required for its intended roles. The temptation to
add features like those of the prior section reflects a tension common to all software
projects, and merits a note of perspective here.
In the end, the ease with which functionality can be added with a tool like Python
is both asset and liability. Programs like this must find a balance between features
and usability that stays focused on the original use case. Over-engineering seems an
unfortunately common downfall in software—especially in the open source domain,
where changes often come with little or no analysis of their impact. When unchecked,
this can quickly lead to a state known to developers as feature creep: a
cascade of enhancements that conspires to render a system unusable.
Less technically—but just as importantly—over-automation turns a program's
users into detached operators, instead of active participants, and comes with often
unexpected consequences. Personal anecdote: a company recently failed to make a payment
owed to me, because they scheduled it to occur on a nonexistent day in February
(comical but true: the check didn't get cut because it wasn't a leap year). Had the company's
employees been required to manually post the payment date to a calendar, this probably
would not have happened.
Software works best when it is a tool, not a replacement. This is as true
for a basic calendar GUI as it is for systems of broader scope. Engaging users by avoiding
unwarranted automation can not only make programs more relevant to real goals, it might just
sidestep mistakes that people wouldn't otherwise make. Be it this program or others, the
same rule applies: don't complicate things that don't need to be complicated.