keyboard-event-calc
keyboard-event-calc is a compact, always-on-top desktop application that
counts global keyboard presses. It provides both a Qt-based graphical interface
and a thread-safe Python API for embedding the counter in another program.
The application stores aggregate statistics only. It never stores typed text, the order of key presses, keyboard shortcuts as sequences, window titles, or the name of the active application.
Features
- Counts global keyboard press events, including letters, numbers, function keys, navigation keys, modifiers, and keys used in shortcuts.
- Counts each key in a shortcut separately. For example,
Ctrl+Cadds two to the total. - Filters operating-system key-repeat events while a key remains held down. A new count is recorded after that key is released and pressed again.
- Starts in a paused state so global counting begins only after Start is clicked.
- Uses a green status light while counting and a red status light while paused.
- Provides Start, Pause, and Clear controls in a small fixed-size window that remains above other windows.
- Requires confirmation before clearing data. A successful clear resets every total, saves the empty state immediately, and leaves counting paused until Start is clicked again.
- Includes a collapsed-by-default letter ranking. When expanded, it displays up to ten letters ordered by descending press count.
- Treats uppercase and lowercase forms as the same letter. Non-letter keys are included in the total but not in the letter ranking.
- Restores the previous total and letter counts each time the application is launched.
- Saves changes periodically while counting and whenever the counter is paused or closed.
- Writes the statistics file atomically to reduce the chance of partial data after an interrupted write.
Requirements
- Python 3.10 or newer
- uv
- Windows or Linux
- An X11 session on Linux for complete global keyboard monitoring
The interface uses PySide6-Essentials. Global keyboard events are provided by
pynput.
Installation
Create the project virtual environment and install the locked dependencies:
uv venv
uv sync
uv sync installs the application and its dependencies into .venv. The same
commands work in PowerShell, Command Prompt, and most Linux shells.
Running the application
Launch the installed GUI entry point:
uv run keyboard-event-calc
The package can also be launched as a Python module:
uv run python -m keyboard_event_calc
The application loads previously saved statistics but initially remains paused. Click Start to begin counting. Pause stops new events from being added without discarding the current totals.
Letter ranking
Select Letter details (top 10) to expand the ranking. The table shows only letters that have been recorded and displays no more than ten rows. Letters are sorted by count from highest to lowest; equal counts are sorted alphabetically.
The ranking stores counts by normalized letter, not by physical keyboard position. Consequently, the active keyboard layout determines which letter a key represents.
Data and privacy
The persisted JSON document contains only a format version, the total press count, and a mapping of letters to counts. It does not contain enough information to reconstruct text or shortcuts.
The default data file is stored at:
- Windows:
%LOCALAPPDATA%\keyboard-event-calc\stats.json - Linux with
XDG_STATE_HOME:$XDG_STATE_HOME/keyboard-event-calc/stats.json - Linux without
XDG_STATE_HOME:~/.local/state/keyboard-event-calc/stats.json
Missing, unreadable, malformed, or incompatible data files are treated as an empty statistics set. If saving fails during a normal close operation, the window remains open so that saving can be retried.
Python API
KeyboardCounter exposes the same start, pause, clear, snapshot, persistence,
and shutdown lifecycle used by the GUI:
from keyboard_event_calc import KeyboardCounter
with KeyboardCounter() as counter:
counter.start()
# The application can do other work while pynput listens in the background.
snapshot = counter.snapshot()
print(snapshot.total)
print(snapshot.active)
print(snapshot.letters)
print(snapshot.top_letters(10))
counter.pause()
A custom statistics file can be supplied when the counter is created:
from pathlib import Path
from keyboard_event_calc import KeyboardCounter
counter = KeyboardCounter(storage_path=Path("my-keyboard-stats.json"))
try:
counter.start()
print(counter.storage_path)
finally:
counter.close()
API behavior
start()creates the global listener on first use and begins or resumes counting.pause()stops counting and immediately flushes pending statistics.clear()resets all statistics, persists the reset, and pauses the counter. The confirmation dialog is a GUI responsibility, so API callers should ask for confirmation themselves when appropriate.snapshot()returns an independentStatsSnapshotcontainingtotal,letters, andactive.StatsSnapshot.top_letters(limit=10)returns(letter, count)pairs sorted by descending count and then alphabetically.record_key(key)records one event when active and can be used by custom integrations. It returnsTrueif the event was counted.flush()writes pending changes without pausing the counter.close()pauses, saves, stops the listener, and permanently closes that counter instance. It is also called by the context manager.storage_pathexposes the path used by the current instance.
All counter state operations are protected by a reentrant lock. Snapshots can therefore be read safely from a different thread while the listener records events.
KeyboardListenerError is raised when the operating-system listener cannot be
started. File-system failures raised while saving are left as OSError so API
callers can choose an appropriate retry policy.
Linux notes
pynput uses X11 for global input monitoring on Linux. Under a native Wayland
session, it is generally limited to applications running through Xwayland and
may not observe all key events. This limitation affects the listener rather
than the Qt interface.
The desktop environment or distribution may also apply its own security rules to global input monitoring.
Development and tests
The test suite uses the Python standard library and does not activate a real global keyboard hook:
uv run python -m unittest discover -v
The main package is organized as follows:
keyboard_event_calc/counter.py: public API and synchronized counter statekeyboard_event_calc/listener.py:pynputadapter and key-repeat filteringkeyboard_event_calc/storage.py: platform paths and atomic JSON persistencekeyboard_event_calc/gui.py: always-on-top Qt Widgets interfacetests/test_counter.py: core behavior and persistence tests
License
This project is distributed under the MIT License. See LICENSE for details.
Metadata
Release files for keyboard-event-calc 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| keyboard_event_calc-0.1.0.tar.gz | 15.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| keyboard_event_calc-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.0 kB
Release files / keyboard_event_calc-0.1.0.tar.gz
| Download URL | keyboard_event_calc-0.1.0.tar.gz |
|---|---|
| Size | 15.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
97fada3b7d3eda774b35a17bad6d78b52973a5500d17ef1278ec11ea58296e78
|
|
BLAKE2b-256 checksum How to use checksums |
1fea09f775018212dff45829658cfc6fd3de46688a26dbb2319520ca16a3fdbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.6 Windows/11
|
Release files / keyboard_event_calc-0.1.0-py3-none-any.whl
| Download URL | keyboard_event_calc-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e870ad55bf4d6e248958287045e032ba0fadaa6dac92eedb3f94350611b7863e
|
|
BLAKE2b-256 checksum How to use checksums |
a88b6769b1e3e4685a807e549003e2857695094b001f4d5d406bb28494b5bb3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.6 Windows/11
|