YourApp Starter is a CustomTkinter-based reference implementation that shows how to ship a polished Python desktop app with multiple frontends, theming hooks, and production-ready installers. The UI still resembles the original Tic Tac Toe sample, but the domain layer now ships as a neutral stub (dispatch_action raises NotImplementedError) so you can wire in your own business rules immediately.
This project was created as a reference template for building professional Python GUI applications. It demonstrates:
- ✅ Modern GUI Development using CustomTkinter
- ✅ Proper Project Structure with separation of concerns
- ✅ Python Packaging with setuptools and wheel distribution
- ✅ Isolated Virtual Environment installation
- ✅ Professional Windows Distribution with automated installer
- ✅ Desktop Integration with shortcuts and icons
- ✅ Clean Architecture following best practices
- ✅ Complete Documentation for users and developers
Use this as a starting point for your own Python GUI projects!
- Multi-frontend dispatcher: GUI, headless, CLI, and service entry points live behind
python -m tictactoeso you can register more launchers without forking scripts. - Typed theme/config layer:
tictactoe.config.guiexposes dataclasses for fonts, colors, copy, and layout; presets can be loaded from JSON or environment variables. - Domain placeholder scaffolding:
tictactoe.domain.logicemitsExampleStatesnapshots and documents override points so adopters can replace the sample logic without guessing how the controllers interact. - Installer + launcher duo:
wheel-builder.batoutputs a version-stampedinstallation.bat, VBScript launcher, manifest, and helper docs with one command. - CI rehearsal scripts:
scripts/run-ci.ps1and.shrun formatting, linting, typing, pytest (GUI + non-GUI), and installer smoke tests locally or in automation. - Headless GUI adapter:
HeadlessGameViewmirrors widget telemetry so GUI smoke tests run in CI without a Tk display.
- Isolated installations prevent global Python conflicts and mimic end-user environments.
- Desktop integration (shortcut, icon, VBScript launcher) demonstrates how to feel native on Windows.
- Environment-driven configuration keeps installers/CI in sync with runtime features like themes or automation scripts.
- Docs-first approach means README + deep dives already describe packaging, configuration, and adoption steps for your future contributors.
yourapp-starter/
├── src/
│ └── tictactoe/
│ ├── __init__.py
│ ├── __main__.py # Entry point
│ ├── assets/
│ │ └── favicon.ico # Application icon
│ ├── config/ # Configuration (future use)
│ ├── domain/
│ │ └── logic.py # Game logic (model)
│ └── ui/
│ └── gui/
│ └── main.py # GUI implementation (view)
├── tests/ # Unit tests
├── docs/
│ ├── INSTALLATION-GUIDE.md # User installation guide
│ └── INSTALLATION-TECHNICAL-DETAILS.md # Technical deep-dive
├── dist/ # Built distribution files
├── pyproject.toml # Project metadata & dependencies
├── requirements.txt # Development dependencies
├── wheel-builder.bat # Build script
└── README.md
Architecture: MVC-inspired separation
- domain/: Business logic (Model)
- ui/: User interface (View)
- Future: Add controller layer if needed
- Download the latest sample release (
yourapp-starter.zip) - Extract the ZIP file
- Run
installation.bat - Launch from desktop shortcut
See INSTALLATION-GUIDE.md for detailed instructions.
- Python 3.8 or higher
- Git (optional)
# Clone the repository
git clone <repository-url>
cd yourapp-starter
# Create virtual environment
python -m venv .venv
# Activate virtual environment
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux/Mac
# Install dependencies
pip install -e .
# Install development dependencies
pip install -r requirements.txt# Activate virtual environment first
.venv\Scripts\activate
# Run the bundled sample app
python -m tictactoe
# or
tictactoepython -m tictactoe now acts as a thin dispatcher so template users can plug in
different interfaces without forking the entry point. Pick a frontend at runtime:
# Launch the CustomTkinter GUI (default)
python -m tictactoe --ui gui
# Launch the GUI with the headless shim (CI or servers without Tk)
python -m tictactoe --ui headless
# Launch the terminal client
python -m tictactoe --ui cli
# Launch the headless automation/service runner
python -m tictactoe --ui service
# Launch the GUI with the bundled "dark" theme
python -m tictactoe --ui gui --theme dark
# Load a JSON theme config from disk
python -m tictactoe --ui gui --theme-file themes/enterprise.json
# List every registered frontend
python -m tictactoe --list-frontendsEnvironment variables offer zero-touch overrides for installers or CI:
| Variable | Accepted values | Notes |
|---|---|---|
TICTACTOE_UI |
gui, cli, headless, service |
Forces a frontend when no flag is provided. |
TICTACTOE_HEADLESS |
0 / 1 |
Still respected by the GUI to load the shim widgets in tests. |
TICTACTOE_THEME |
default, light, dark, enterprise, ... |
Selects a named preset from tictactoe.config.gui.NAMED_THEMES. |
TICTACTOE_THEME_FILE |
filesystem path | Points to a JSON file containing GameViewConfig data. |
TICTACTOE_THEME_PAYLOAD |
JSON string (internal) | Automatically managed when using --theme / --theme-file; can be set manually for advanced flows. |
TICTACTOE_SCRIPT |
comma-separated moves | Consumed by the service frontend to drive automation runs. |
TICTACTOE_SCRIPT_FILE |
filesystem path | Alternative to TICTACTOE_SCRIPT if you store scripts in files. |
TICTACTOE_AUTOMATION_OUTPUT |
filesystem path | Where automation results are written as JSON. |
TICTACTOE_AUTOMATION_LABEL |
any string | Stored in the automation summary for traceability. |
TICTACTOE_AUTOMATION_QUIET |
0 / 1 |
Controls whether the service frontend prints to stdout. |
TICTACTOE_LOGGING |
0 / 1 / true / false |
Turns on telemetry/logging hooks for all frontends at once. Legacy TICTACTOE_GUI_LOGGING, TICTACTOE_CLI_LOGGING, and TICTACTOE_SERVICE_LOGGING still work for per-frontend overrides. |
Setting TICTACTOE_UI=headless automatically flips TICTACTOE_HEADLESS=1, which is
useful for CI smoke tests that still exercise the GUI bootstrap path without a Tk
runtime.
Need targeted diagnostics without editing code? Export TICTACTOE_LOGGING=1 (or one
of the legacy *_LOGGING vars) before launching. Every frontend will automatically
use the built-in ControllerHooks.logging_hooks() helper so you can capture
controller telemetry in the terminal or installer logs.
Need scripted or fully automated CLI sessions? Invoke the CLI module directly so
you can access its own flags (such as --script and --quiet):
# Replay a deterministic move list and capture JSON output for CI
python -m tictactoe.ui.cli.main --script 0,4,8 --output-json artifacts/automation.json
# Load moves from a file and tag the run for dashboards
python -m tictactoe.ui.cli.main --script-file scripts/demo.moves --label nightly-seed
# Serialize a theme preset to disk for editing
python - <<"PY"
from pathlib import Path
from tictactoe.config.gui import serialize_game_view_config, get_theme
Path("themes/dark.json").write_text(
__import__("json").dumps(serialize_game_view_config(get_theme("dark")), indent=2),
encoding="utf-8",
)
PYWant to flip JSON experiments back into typed dataclasses? The sample payloads live in src/tictactoe/assets/themes/, and you can promote any of them with:
python -m tictactoe.tools.theme_codegen src/tictactoe/assets/themes/light.json --variable-prefix marketingThe script prints an import block plus a marketing_light_theme variable so you can commit polished presets once you're happy with the JSON prototype.
When you don't want to pass CLI flags, switch to the service frontend (--ui service)
and configure the environment variables above. This is handy for installer smoke
tests or GitHub Actions jobs that already manage state through env vars.
| Step | Files to touch | What to change |
|---|---|---|
| 1. Rename the package | pyproject.toml, src/tictactoe/, README.md |
Update [project] metadata, rename the package directory, adjust imports/entry points, and rewrite the hero copy for your product. |
| 2. Refresh configs & assets | tictactoe.config.gui, src/tictactoe/assets/ |
Replace fonts/colors/texts via GameViewConfig, swap favicon.ico, and add any extra assets you plan to copy into the installer bundle. |
| 3. Align UIs & controllers | tictactoe.ui.gui.*, tictactoe.ui.cli.main, tictactoe.domain.logic |
Rework copy, widgets, or domain actions to match your app while keeping HeadlessGameView + CLI automation helpers intact. |
| 4. Update installers | wheel-builder.bat, generated installation.bat, tic-tac-toe-starter.vbs |
Change shortcut names, manifest metadata, and smoke-test scripts; rerun wheel-builder.bat --ci --no-pause to verify. |
| 5. Lock quality gates | scripts/run-ci.ps1, scripts/run-ci.sh, tests/ |
Keep the formatter/linter/type checker suites, expand pytest coverage for your modules, and ensure GUI markers still pass headless. |
Each step links back into the deeper docs in docs/TEMPLATE-USAGE-GUIDE.md and
docs/TEMPLATE-CHECKLIST.md, so contributors can follow the same adoption path.
The GUI layer now ships with adapter-friendly contracts so you can choose how the widgets are rendered without touching the controller:
from tictactoe.ui.gui import HeadlessGameView
from tictactoe.ui.gui.main import TicTacToeGUI
# Exercise the controller and domain logic without CustomTkinter widgets
app = TicTacToeGUI(view_factory=HeadlessGameView)
app.run()HeadlessGameView implements the shared GameViewPort protocol alongside the
default GameView (CustomTkinter) so template users can bundle their own adapters
without rewriting TicTacToeGUI. The same telemetry helpers (cell_text,
status_text, etc.) are available for tests regardless of the adapter in use.
# Build the interactive bundle (prompts if anything fails)
.\wheel-builder.bat
# Build + smoke-test into %%TEMP%% (useful for CI)
.\wheel-builder.bat --ci --no-pauseThe script stamps the current version, copies src/tictactoe/assets/, and
emits a complete installer payload in dist/:
tictactoe-<version>-py3-none-any.whlinstallation.bat(pre-configured install root + smoke tests)tic-tac-toe-starter.vbslauncher with relative pathshow-to-install-me.txtandlicense.txtassets/directory (icons, future resources)
This project includes an automated build system:
- Run
wheel-builder.bat(orwheel-builder.bat --ci --no-pauseinside pipelines) - Outputs the wheel, installer script, launcher, and docs/asset bundle under
dist/
The installer (installation.bat) now mirrors production-ready workflows:
- ✅ Version-stamped install directory (
%LOCALAPPDATA%\Programs\yourapp-starter-<version>%by default,%TEMP%when built with--ci) - ✅ Full bundle copy including
assets/so icons/config ship with the app - ✅ Private virtual environment creation + wheel installation
- ✅ Post-install smoke test (
python -m tictactoe --ui service --script 0,4,8 --quiet) - ✅ Desktop shortcut + VBScript launcher with custom icon name (
YourApp Starter.lnk) - ✅
install-info.jsonmanifest for inventory/telemetry scripts - ✅
how-to-install-me.txtaid packaged beside the installer for hand-off documentation
To uninstall, delete the stamped install directory and remove the shortcut.
For technical details, see INSTALLATION-TECHNICAL-DETAILS.md.
- Python 3.8+: Programming language
- CustomTkinter: Modern UI framework (enhanced Tkinter)
- setuptools: Python packaging
- wheel: Binary distribution format
customtkinter>=5.2.2- Modern UI componentspillow>=12.0.0- Image handlingdarkdetect>=0.8.0- OS theme detectionpackaging>=25.0- Version management
pytest- Unit testingblack- Code formattingruff- Lintingmypy- Type checking
- Clone/Download this repository
- Rename the project folder and package
- Update
pyproject.toml:- Change
name,version,description - Update author information
- Modify dependencies as needed
- Change
- Replace game logic in
domain/with your business logic - Redesign UI in
ui/gui/for your application - Update
favicon.icowith your application icon - Modify
wheel-builder.batto match your project name/version - Rebuild and test installation
- Virtual Environment Installation: Keeps user's Python clean
- Wheel Distribution: Professional packaging method
- Desktop Integration: Icons and shortcuts
- VBScript Launcher: Silent execution without console
- AppUserModelID: Proper Windows taskbar integration
- Automated Installer: One-click installation experience
All fonts, strings, dimensions, and color hooks for the CustomTkinter frontend live in tictactoe.config.gui. Inject a GameViewConfig (and optionally a WindowConfig) into TicTacToeGUI to reskin or resize the app without touching the widget code:
from tictactoe.config import (
ColorConfig,
FontConfig,
FontSpec,
GameViewConfig,
LayoutConfig,
TextConfig,
WindowConfig,
)
from tictactoe.ui.gui.main import TicTacToeGUI
view_config = GameViewConfig(
fonts=FontConfig(
title=FontSpec(size=40, weight="bold"),
cell=FontSpec(size=48, weight="bold"),
),
layout=LayoutConfig(
board_padding=(30, 30),
cell_size=(120, 120),
cell_spacing=8,
),
text=TextConfig(
title="Ultimate YourApp",
reset_button="Play Again",
win_message_template="Congrats {winner}!",
),
colors=ColorConfig(
board_background="#101828",
cell_fg="#1D2939",
cell_hover="#344054",
status_text="#F2F4F7",
),
)
window_config = WindowConfig(title="Ultimate YourApp", geometry="500x720")
app = TicTacToeGUI(view_config=view_config, window_config=window_config)
app.run()Every field is optional; omit keys you do not want to override. Because the config layer uses frozen dataclasses, you can share presets throughout your project or load them from JSON/YAML before instantiating the GUI.
- INSTALLATION-GUIDE.md: User-friendly installation instructions
- INSTALLATION-TECHNICAL-DETAILS.md: Technical deep-dive for developers
- TEMPLATE-USAGE-GUIDE.md: Step-by-step instructions plus rationale for customizing the template
- Keep coverage-focused pytest suites (headless + GUI markers) so domain and UI regressions are caught before shipping.
- Always run the formatter (
black), linter (ruff), and type checker (mypy) before pushing. - Mirror the local CI scripts (
scripts/run-ci.ps1/.sh) inside your automation so installer smoke tests stay representative.
# Run the default non-GUI suite with coverage
python -m pytest -m "not gui"
# Run only the GUI smoke tests (requires Tk runtime unless headless)
python -m pytest -m gui
# Formatting and linting
python -m black --check src tests
python -m ruff check src tests
# Static type checking
python -m mypy src
# Full tox automation (lint + type + tests)
python -m tox -e lint,type,py313
# One-command local CI rehearsal (PowerShell)
# -> runs black/ruff/mypy, pytest markers, wheel-builder, installation.bat
pwsh scripts/run-ci.ps1
# Bash/zsh equivalent (uses a temporary virtualenv sandbox for installer verification)
bash scripts/run-ci.shscripts/run-ci.ps1/scripts/run-ci.shmirror the CI pipeline (dev requirements + editable install + tox lint/type/py313). Run them before every push to catch formatting, typing, or packaging issues locally. Pass-SkipRequirementsInstall(PowerShell) orSKIP_REQUIREMENTS_INSTALL=1(bash/zsh) if you have already synced dependencies in the current shell..pre-commit-config.yamlwires up Black, Ruff, mypy, file-format checks, and now runspython -m pytest -m "not gui"both before each commit and push. Install once and forget:
python -m pip install pre-commit
pre-commit install # run on every commit
pre-commit install --hook-type pre-pushThe pre-push hook replays the fast pytest suite and tox -e lint,type, matching the workflow GitHub Actions enforces.
This is a template project, but improvements are welcome!
- Fork the repository
- Create a feature branch
- Make your changes
- Add/update tests
- Submit a pull request
This project is licensed under the Zero-Clause BSD License (0BSD) - see the LICENSE file for details.
Python Packaging:
CustomTkinter:
Virtual Environments:
Windows Scripting:
Potential improvements for this template:
- AI opponent (single-player mode)
- Settings/preferences system
- Sound effects
- Statistics tracking
- Linux/Mac installer support
- PyInstaller build for standalone .exe
- Internationalization (i18n)
- Modern, professional look
- Easy to use (similar to Tkinter)
- Built-in theming support
- Active development and community
- Isolation: No conflicts with user's Python packages
- Clean: Easy uninstall (just delete folder)
- Professional: Industry standard for Python applications
- Versioning: Each version can have different dependencies
- Standard: Python-recommended distribution format
- Fast: Binary format, no compilation needed
- Compatible: Works with pip and all package managers
- Professional: Used by major Python projects
- Silent: Runs without console window (pythonw.exe)
- Integration: Allows custom icon in taskbar
- Simple: No additional dependencies needed
- Windows-native: Uses built-in Windows Script Host
Q: Can I use this for commercial projects? A: Yes. The 0BSD License places almost no restrictions on redistribution or commercial use.
Q: Does this work on Linux/Mac? A: The installer is Windows-specific, but the code runs on any platform. You'd need to create platform-specific installers.
Q: Can I package this as a standalone .exe? A: Yes! Use PyInstaller or similar tools. This template focuses on wheel distribution, but .exe packaging is compatible.
Q: Why not use Qt/wxPython instead of Tkinter? A: CustomTkinter provides a modern look with Tkinter's simplicity. Qt/wxPython are great alternatives but have larger dependencies.
Q: How do I update users to a new version? A: Users just run the new installer - it automatically removes the old version first.
- CustomTkinter by Tom Schimansky - Modern Tkinter components
- Python Packaging Authority - Packaging tools and guidance
- Microsoft - Windows integration APIs
For questions about using this template:
- Open an issue on GitHub
- Check existing documentation
- Review the technical details document
Built with ❤️ as a learning resource and project template
Last Updated: November 15, 2025