Skip to content

Commit 52a4fcc

Browse files
Camera app: improve default setting handling, only save when non-default
1 parent 27d1af9 commit 52a4fcc

File tree

4 files changed

+217
-80
lines changed

4 files changed

+217
-80
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Fri3d Camp 2024 Badge: improve battery monitor calibration to fix 0.1V delta
55
- API: improve and cleanup animations
66
- API: SharedPreferences: add erase_all() function
7+
- API: add defaults handling to SharedPreferences and only save non-defaults
78
- About app: add free, used and total storage space info
89
- AppStore app: remove unnecessary scrollbar over publisher's name
910
- Camera app: massive overhaul!

CLAUDE.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,78 @@ prefs.edit().put_int("brightness", -1).commit()
446446
# brightness is no longer stored in config.json, saves space
447447
```
448448

449+
**Multi-mode apps with merged defaults**:
450+
451+
Apps with multiple operating modes can define separate defaults dictionaries and merge them based on the current mode. The camera app demonstrates this pattern with normal and QR scanning modes:
452+
453+
```python
454+
# Define defaults in your settings class
455+
class CameraSettingsActivity:
456+
# Common defaults shared by all modes
457+
COMMON_DEFAULTS = {
458+
"brightness": 1,
459+
"contrast": 0,
460+
"saturation": 0,
461+
"hmirror": False,
462+
"vflip": True,
463+
# ... 20 more common settings
464+
}
465+
466+
# Normal mode specific defaults
467+
NORMAL_DEFAULTS = {
468+
"resolution_width": 240,
469+
"resolution_height": 240,
470+
"colormode": True,
471+
"ae_level": 0,
472+
"raw_gma": True,
473+
}
474+
475+
# QR scanning mode specific defaults
476+
SCANQR_DEFAULTS = {
477+
"resolution_width": 960,
478+
"resolution_height": 960,
479+
"colormode": False, # Grayscale for better QR detection
480+
"ae_level": 2, # Higher exposure
481+
"raw_gma": False, # Better contrast
482+
}
483+
484+
# Merge defaults based on mode when initializing
485+
def load_settings(self):
486+
if self.scanqr_mode:
487+
# Merge common + scanqr defaults
488+
scanqr_defaults = {}
489+
scanqr_defaults.update(CameraSettingsActivity.COMMON_DEFAULTS)
490+
scanqr_defaults.update(CameraSettingsActivity.SCANQR_DEFAULTS)
491+
self.prefs = SharedPreferences(
492+
self.PACKAGE,
493+
filename="config_scanqr.json",
494+
defaults=scanqr_defaults
495+
)
496+
else:
497+
# Merge common + normal defaults
498+
normal_defaults = {}
499+
normal_defaults.update(CameraSettingsActivity.COMMON_DEFAULTS)
500+
normal_defaults.update(CameraSettingsActivity.NORMAL_DEFAULTS)
501+
self.prefs = SharedPreferences(
502+
self.PACKAGE,
503+
defaults=normal_defaults
504+
)
505+
506+
# Now all get_*() calls can omit default arguments
507+
width = self.prefs.get_int("resolution_width") # Mode-specific default
508+
brightness = self.prefs.get_int("brightness") # Common default
509+
```
510+
511+
**Benefits of this pattern**:
512+
- Single source of truth for all 30 camera settings defaults
513+
- Mode-specific config files (`config.json`, `config_scanqr.json`)
514+
- ~90% reduction in config file size (only non-default values stored)
515+
- Eliminates hardcoded defaults throughout the codebase
516+
- No need to pass defaults to every `get_int()`/`get_bool()` call
517+
- Self-documenting code with clear defaults dictionaries
518+
519+
**Note**: Use `dict.update()` instead of `{**dict1, **dict2}` for MicroPython compatibility (dictionary unpacking syntax not supported).
520+
449521
**Intent system**: Launch activities and pass data
450522
```python
451523
from mpos.content.intent import Intent

internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,30 @@ def load_settings_cached(self):
188188
if self.scanqr_mode:
189189
print("loading scanqr settings...")
190190
if not self.scanqr_prefs:
191-
self.scanqr_prefs = SharedPreferences(self.PACKAGE, filename=self.SCANQR_CONFIG)
192-
self.width = self.scanqr_prefs.get_int("resolution_width", CameraSettingsActivity.DEFAULT_SCANQR_WIDTH)
193-
self.height = self.scanqr_prefs.get_int("resolution_height", CameraSettingsActivity.DEFAULT_SCANQR_HEIGHT)
194-
self.colormode = self.scanqr_prefs.get_bool("colormode", CameraSettingsActivity.DEFAULT_SCANQR_COLORMODE)
191+
# Merge common and scanqr-specific defaults
192+
scanqr_defaults = {}
193+
scanqr_defaults.update(CameraSettingsActivity.COMMON_DEFAULTS)
194+
scanqr_defaults.update(CameraSettingsActivity.SCANQR_DEFAULTS)
195+
self.scanqr_prefs = SharedPreferences(
196+
self.PACKAGE,
197+
filename=self.SCANQR_CONFIG,
198+
defaults=scanqr_defaults
199+
)
200+
# Defaults come from constructor, no need to pass them here
201+
self.width = self.scanqr_prefs.get_int("resolution_width")
202+
self.height = self.scanqr_prefs.get_int("resolution_height")
203+
self.colormode = self.scanqr_prefs.get_bool("colormode")
195204
else:
196205
if not self.prefs:
197-
self.prefs = SharedPreferences(self.PACKAGE)
198-
self.width = self.prefs.get_int("resolution_width", CameraSettingsActivity.DEFAULT_WIDTH)
199-
self.height = self.prefs.get_int("resolution_height", CameraSettingsActivity.DEFAULT_HEIGHT)
200-
self.colormode = self.prefs.get_bool("colormode", CameraSettingsActivity.DEFAULT_COLORMODE)
206+
# Merge common and normal-specific defaults
207+
normal_defaults = {}
208+
normal_defaults.update(CameraSettingsActivity.COMMON_DEFAULTS)
209+
normal_defaults.update(CameraSettingsActivity.NORMAL_DEFAULTS)
210+
self.prefs = SharedPreferences(self.PACKAGE, defaults=normal_defaults)
211+
# Defaults come from constructor, no need to pass them here
212+
self.width = self.prefs.get_int("resolution_width")
213+
self.height = self.prefs.get_int("resolution_height")
214+
self.colormode = self.prefs.get_bool("colormode")
201215

202216
def update_preview_image(self):
203217
self.image_dsc = lv.image_dsc_t({
@@ -467,93 +481,95 @@ def apply_camera_settings(self, prefs, cam, use_webcam):
467481

468482
try:
469483
# Basic image adjustments
470-
brightness = prefs.get_int("brightness", CameraSettingsActivity.DEFAULTS.get("brightness"))
484+
brightness = prefs.get_int("brightness")
471485
cam.set_brightness(brightness)
472486

473-
contrast = prefs.get_int("contrast", 0)
487+
contrast = prefs.get_int("contrast")
474488
cam.set_contrast(contrast)
475489

476-
saturation = prefs.get_int("saturation", 0)
490+
saturation = prefs.get_int("saturation")
477491
cam.set_saturation(saturation)
478-
492+
479493
# Orientation
480-
hmirror = prefs.get_bool("hmirror", False)
494+
hmirror = prefs.get_bool("hmirror")
481495
cam.set_hmirror(hmirror)
482-
483-
vflip = prefs.get_bool("vflip", True)
496+
497+
vflip = prefs.get_bool("vflip")
484498
cam.set_vflip(vflip)
485-
499+
486500
# Special effect
487-
special_effect = prefs.get_int("special_effect", 0)
501+
special_effect = prefs.get_int("special_effect")
488502
cam.set_special_effect(special_effect)
489-
503+
490504
# Exposure control (apply master switch first, then manual value)
491-
exposure_ctrl = prefs.get_bool("exposure_ctrl", True)
505+
exposure_ctrl = prefs.get_bool("exposure_ctrl")
492506
cam.set_exposure_ctrl(exposure_ctrl)
493-
507+
494508
if not exposure_ctrl:
495-
aec_value = prefs.get_int("aec_value", 300)
509+
aec_value = prefs.get_int("aec_value")
496510
cam.set_aec_value(aec_value)
497-
498-
ae_level = prefs.get_int("ae_level", 2 if self.scanqr_mode else 0)
511+
512+
# Mode-specific default comes from constructor
513+
ae_level = prefs.get_int("ae_level")
499514
cam.set_ae_level(ae_level)
500-
501-
aec2 = prefs.get_bool("aec2", False)
515+
516+
aec2 = prefs.get_bool("aec2")
502517
cam.set_aec2(aec2)
503518

504519
# Gain control (apply master switch first, then manual value)
505-
gain_ctrl = prefs.get_bool("gain_ctrl", True)
520+
gain_ctrl = prefs.get_bool("gain_ctrl")
506521
cam.set_gain_ctrl(gain_ctrl)
507-
522+
508523
if not gain_ctrl:
509-
agc_gain = prefs.get_int("agc_gain", 0)
524+
agc_gain = prefs.get_int("agc_gain")
510525
cam.set_agc_gain(agc_gain)
511-
512-
gainceiling = prefs.get_int("gainceiling", 0)
526+
527+
gainceiling = prefs.get_int("gainceiling")
513528
cam.set_gainceiling(gainceiling)
514-
529+
515530
# White balance (apply master switch first, then mode)
516-
whitebal = prefs.get_bool("whitebal", True)
531+
whitebal = prefs.get_bool("whitebal")
517532
cam.set_whitebal(whitebal)
518-
533+
519534
if not whitebal:
520-
wb_mode = prefs.get_int("wb_mode", 0)
535+
wb_mode = prefs.get_int("wb_mode")
521536
cam.set_wb_mode(wb_mode)
522-
523-
awb_gain = prefs.get_bool("awb_gain", True)
537+
538+
awb_gain = prefs.get_bool("awb_gain")
524539
cam.set_awb_gain(awb_gain)
525540

526541
# Sensor-specific settings (try/except for unsupported sensors)
527542
try:
528-
sharpness = prefs.get_int("sharpness", 0)
543+
sharpness = prefs.get_int("sharpness")
529544
cam.set_sharpness(sharpness)
530545
except:
531546
pass # Not supported on OV2640?
532-
547+
533548
try:
534-
denoise = prefs.get_int("denoise", 0)
549+
denoise = prefs.get_int("denoise")
535550
cam.set_denoise(denoise)
536551
except:
537552
pass # Not supported on OV2640?
538-
553+
539554
# Advanced corrections
540-
colorbar = prefs.get_bool("colorbar", False)
555+
colorbar = prefs.get_bool("colorbar")
541556
cam.set_colorbar(colorbar)
542-
543-
dcw = prefs.get_bool("dcw", True)
557+
558+
dcw = prefs.get_bool("dcw")
544559
cam.set_dcw(dcw)
545-
546-
bpc = prefs.get_bool("bpc", False)
560+
561+
bpc = prefs.get_bool("bpc")
547562
cam.set_bpc(bpc)
548-
549-
wpc = prefs.get_bool("wpc", True)
563+
564+
wpc = prefs.get_bool("wpc")
550565
cam.set_wpc(wpc)
551-
552-
raw_gma = prefs.get_bool("raw_gma", False if self.scanqr_mode else True)
566+
567+
# Mode-specific default comes from constructor
568+
raw_gma = prefs.get_bool("raw_gma")
553569
print(f"applying raw_gma: {raw_gma}")
554570
cam.set_raw_gma(raw_gma)
555-
556-
lenc = prefs.get_bool("lenc", True)
571+
572+
lenc = prefs.get_bool("lenc")
557573
cam.set_lenc(lenc)
558574

559575
# JPEG quality (only relevant for JPEG format)

0 commit comments

Comments
 (0)