6.6 KiB
Image Resource Management
Overview
The Knoe Database Installer includes multiple image assets that need to work both when running from source and when packaged as a standalone binary. This document explains how image resources are managed.
Image Assets
The following images are included in the installer:
Primary Assets
- knoeIcon.png (1,494,782 bytes) - Application icon
- knoeLogo.png (2,190,703 bytes) - Main Knoe logo
- knoeLogoSepia.png (2,657,956 bytes) - Sepia-toned background logo
- knoeLogoBlueprint.png (3,107,147 bytes) - Blueprint style logo
- knoeIconblueprint.png (1,570,109 bytes) - Blueprint style icon
Supplementary Assets
- knoe-type.gif (599 bytes) - Small typing animation
Resource Path Resolution
The Challenge
When packaging Python applications with PyInstaller, resource files need to be found in two different scenarios:
- Development: Running from source, files are in
PROJECT_ROOT/img/ - Production: Running from bundle, files are in PyInstaller's temporary extraction directory
The Solution
The get_resource_path() function automatically resolves paths correctly in both scenarios:
def get_resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller."""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = Path(sys._MEIPASS)
except AttributeError:
# Running from source
base_path = PROJECT_ROOT
return base_path / relative_path
Usage
All image loading code uses this function:
# Instead of:
bg_path = Path('img/knoeLogoSepia.png')
# Use:
bg_path = get_resource_path('img/knoeLogoSepia.png')
Implementation Details
Modified Code Locations
The following locations in install.py were updated to use get_resource_path():
-
Background logo loading (line ~191)
bg_path = get_resource_path('img/knoeLogoSepia.png') -
Application icon candidates (line ~406)
img_candidates = [ get_resource_path('img/knoeIcon.png'), get_resource_path('img/knoe-type.png'), get_resource_path('img/knoe-type.gif'), get_resource_path('img/Knoe.png'), get_resource_path('img/knoeLogoSepia.png'), ] -
DMG background image (line ~3820)
bg_img = get_resource_path('img/knoeLogoSepia.png')
PyInstaller Configuration
The installer.spec file includes the entire img/ directory in the bundle:
datas = [
('installer', 'installer'),
('conf', 'conf'),
('etc', 'etc'),
('img', 'img'), # All images included
('docs', 'docs'),
]
This ensures all images are:
- Copied into the PyInstaller bundle
- Extracted to the temporary directory at runtime
- Accessible via
sys._MEIPASS / 'img' / filename
Testing
Verify Resource Paths
Run the test script:
python3 test_resource_paths.py
This tests:
- Path resolution from source
- Path resolution simulating PyInstaller environment
- Existence of all image files
Expected output:
✓ App icon
✓ Background logo (sepia)
✓ Main logo
✓ Blueprint logo
✓ Blueprint icon
✓ All tests passed!
Images will be correctly included in PyInstaller build.
Manual Testing
From source:
python3 install.py --gui
# Check that logo appears in background
From built binary:
make package
./dist/Knoe\ Installer.app/Contents/MacOS/knoe-installer --gui
# Check that logo appears in background
Icon Formats
PNG (Source)
- Format: PNG with transparency
- Resolution: 1024x1024 recommended
- Location:
img/knoeIcon.png
ICNS (macOS Bundle)
- Generated by build system from PNG
- Contains multiple resolutions (16x16 through 1024x1024)
- Location:
build/knoe.icns(intermediate), embedded in.appbundle - Generated by:
make build/knoe.icns
The build system automatically converts PNG to ICNS using macOS tools:
sips -z 512 512 img/knoeIcon.png --out build/icon.iconset/icon_512x512.png
iconutil -c icns build/icon.iconset -o build/knoe.icns
Background Image Usage
The GUI installer uses knoeLogoSepia.png as a subtle background:
- Loading: Image loaded via PIL (Pillow)
- Processing:
- Converted to RGBA
- Blended with white background at 15% opacity
- Creates subtle watermark effect
- Rendering:
- Scaled to fill canvas
- Maintains aspect ratio
- Centered on canvas
Code snippet:
bg_path = get_resource_path('img/knoeLogoSepia.png')
if bg_path.exists():
original = Image.open(str(bg_path)).convert('RGBA')
white_bg = Image.new('RGBA', original.size, (255, 255, 255, 255))
self._bg_pil = Image.blend(white_bg, original, 0.15)
Adding New Images
To add new image resources:
- Add image file to
img/directory - Update code to use
get_resource_path():new_img = get_resource_path('img/new_image.png') - No spec changes needed - entire
img/directory is already included - Test with
python3 test_resource_paths.py
Troubleshooting
Image Not Found in Built Binary
Symptom: Image loads from source but not from built .app
Cause: Path hardcoded instead of using get_resource_path()
Solution:
# Wrong:
img_path = PROJECT_ROOT / 'img' / 'logo.png'
# Correct:
img_path = get_resource_path('img/logo.png')
Image Not Showing in GUI
Cause: File doesn't exist or wrong path
Solution: Check with test script:
python3 test_resource_paths.py
Build Size Too Large
Cause: Large images included in bundle
Solution:
- Optimize PNG files:
pngcrush,optipng, etc. - Consider JPEG for photos (PNG for logos/icons)
- Remove unused images from
img/directory
Performance Considerations
Startup Time
PyInstaller extracts bundled resources to a temporary directory on each launch:
- Small images (< 100 KB): Negligible impact
- Large images (> 1 MB): 100-200ms extraction time
- Total time: ~1-2 seconds for all resources
Memory Usage
Images are loaded into memory when displayed:
- knoeLogoSepia.png: ~2.6 MB on disk, ~10 MB in memory (RGBA)
- Scaled versions: Additional memory for display size
Optimization
For production, consider:
- Compress images before including
- Use lazy loading (load only when needed)
- Cache scaled versions instead of re-scaling