PyQt License Starter
Clone-and-run PyQt6 desktop app with Keymint license activation pre-configured. Activation dialog, machine binding, and a deactivation helper included.
Quick Start
bash
git clone https://github.com/keymint-dev/keymint-pyqt-starter.git
cd keymint-pyqt-starter
pip install -r requirements.txt
export KEYMINT_CLIENT_API_KEY=km_live_your_client_key
export KEYMINT_PRODUCT_ID=YOUR_PRODUCT_ID
python main.pyOpens a PyQt6 window with an activation dialog. Enter a valid Keymint license key, and the main window appears.
What's Included
- License activation — calls
POST /key/activatewith cross-platform machine fingerprint - Activation dialog — PyQt6 dialog with key entry, status feedback, pre-fill from stored key
- Persistent storage — license key and activation state saved via
QSettings - Startup check — on launch, a stored key is activated again; if that fails for any reason (including no network), the activation dialog opens with the key pre-filled
- Deactivation helper —
deactivate_license()inlicense_manager.pyfrees the seat and clears the stored key. The starter has no menu or button that calls it; wire it into your own UI - Cross-platform host ID — works on Windows, macOS, and Linux
- Offline licenses (optional) — set
KEYMINT_PUBLIC_KEY(the PEM from Developer → Public Keys) to accept signed.licfiles, verified locally inoffline_license.py
Configuration
Copy .env.example to .env and fill in your values, or export them in your shell:
bash
export KEYMINT_CLIENT_API_KEY=km_live_your_client_key
export KEYMINT_PRODUCT_ID=YOUR_PRODUCT_ID| Variable | Where to Find It |
|---|---|
KEYMINT_CLIENT_API_KEY | Keymint Dashboard → Developer → API Keys (client scope) |
KEYMINT_PRODUCT_ID | Keymint Dashboard → Products |
Project Structure
text
├── main.py # Entry point and placeholder main window, gates app on license check
├── license_manager.py # Keymint SDK calls, machine ID, license storage
├── activation_dialog.py # PyQt6 activation UI
├── offline_license.py # Offline .lic verification (optional)
├── requirements.txt # Python dependencies
└── .env.example # Environment variable templateSDK Methods Used
| Method | Where | Purpose |
|---|---|---|
client.activate_key() | license_manager.py | Validate and bind license to machine |
client.deactivate_key() | license_manager.py | Free activation seat (in deactivate_license(), not called by the starter's UI) |
Host ID Strategy
python
def get_host_id() -> str:
system = platform.system()
try:
if system == "Windows":
# WMIC UUID
output = subprocess.check_output("wmic csproduct get uuid", shell=True)
raw = output.decode().split("\n")[1].strip()
elif system == "Darwin":
# IOPlatformUUID
output = subprocess.check_output(
"ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID", shell=True
)
raw = output.decode().split('"')[-2]
else:
# /etc/machine-id
with open("/etc/machine-id") as f:
raw = f.read().strip()
except Exception:
# Falls back to the MAC address
raw = str(uuid.getnode())
return hashlib.sha256(raw.encode()).hexdigest()[:16]wmic isn't installed by default on recent Windows 11 builds, so there the starter falls back to the MAC address, which can change with network adapters. For a steadier ID, consider get_or_create_installation_id() from the SDK's keymint.identity module.
Build & Package
bash
# Single-file executable
pyinstaller --onefile --hidden-import=keymint main.py
# macOS .app bundle
pyinstaller --windowed --hidden-import=keymint main.pyThe client API key is bundled into the executable. It has client scope — safe for distribution.
Next Steps
- Add optional offline license support for air-gapped deployments
- Integrate with Stripe for subscription licensing
- Customize the activation dialog styling with your brand
- Add a trial mode using
expiryDateon trial license keys