Skip to main content

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+C adds 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 independent StatsSnapshot containing total, letters, and active.
  • 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 returns True if 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_path exposes 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 state
  • keyboard_event_calc/listener.py: pynput adapter and key-repeat filtering
  • keyboard_event_calc/storage.py: platform paths and atomic JSON persistence
  • keyboard_event_calc/gui.py: always-on-top Qt Widgets interface
  • tests/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)

Source distribution for keyboard-event-calc 0.1.0
File Size Uploaded
keyboard_event_calc-0.1.0.tar.gz 15.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for keyboard-event-calc 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page