feat: phase 1 - einstellungen mit lautstärke und spruchlisten
- Settings.qml mit Lautstärke-Slidern für Musik und Knack-Geräusch - Spruchlisten-Auswahl (classic, farmer_wisdom, unfortune) - StackLayout für Navigation zwischen Hauptseite und Einstellungen - Python: load_setting/save_setting für generische Einstellungen - Python: Volume-Einstellungen und Spruchlisten-Verwaltung - ASSET_REQUIREMENTS.md für Grafik-Spezifikationen - JSON-Dateien: classic.json, farmer_wisdom.json, unfortune.json Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+156
-88
@@ -1,21 +1,21 @@
|
||||
"""
|
||||
Fortune Cookie v1.0 - Python Backend Module
|
||||
Framework 1.7 Standard
|
||||
Fortune Cookie v1.1 - Python Backend Module
|
||||
Framework 1.8 Standard
|
||||
Audio-Steuerung in QML (keine Qt-Python-Bindings benoetigt)
|
||||
Last-State Speicherung mit Datei-basiertem Speicher
|
||||
Einstellungen: Lautstaerke, Spruchlisten
|
||||
"""
|
||||
|
||||
import os
|
||||
import random
|
||||
import json
|
||||
import platform
|
||||
from pathlib import Path
|
||||
|
||||
# ============================================================================
|
||||
# APP-METADATEN
|
||||
# ============================================================================
|
||||
APP_NAME = "fortunecookie"
|
||||
APP_VERSION = "1.0.0"
|
||||
APP_VERSION = "1.1.0"
|
||||
MAINTAINER = "darklithium <dev@darklithium.de>"
|
||||
|
||||
|
||||
@@ -32,90 +32,107 @@ def get_platform():
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# FORTUNE-LOGIK
|
||||
# FORTUNE-LISTEN VERWALTUNG
|
||||
# ============================================================================
|
||||
|
||||
# Globale Variablen
|
||||
# Verfuegbare Spruchlisten
|
||||
AVAILABLE_FORTUNE_LISTS = [
|
||||
"classic", # Standard Glückskeks-Sprueche
|
||||
"farmer_wisdom", # Bauernweisheiten
|
||||
"unfortune", # Gothic/UNfortune-Sprueche
|
||||
]
|
||||
|
||||
# Aktuelle Spruchliste (Standard: classic)
|
||||
_current_fortune_list = "classic"
|
||||
_current_fortune = ""
|
||||
_fortunes = []
|
||||
_fortunes = {}
|
||||
_initialized = False
|
||||
|
||||
|
||||
def _init():
|
||||
"""Initialisiert das Modul (wird beim ersten Aufruf ausgefuehrt)."""
|
||||
global _fortunes, _initialized
|
||||
global _fortunes, _initialized, _current_fortune_list
|
||||
|
||||
if _initialized:
|
||||
return True
|
||||
|
||||
# Lade Fortunes
|
||||
_load_fortunes()
|
||||
# Lade alle Spruchlisten
|
||||
_load_all_fortune_lists()
|
||||
|
||||
# Lade aktuelle Liste aus Einstellungen
|
||||
_current_fortune_list = load_setting("fortune_list", default_value="classic")
|
||||
if _current_fortune_list not in _fortunes:
|
||||
_current_fortune_list = "classic"
|
||||
|
||||
_initialized = True
|
||||
return True
|
||||
|
||||
|
||||
def _load_fortunes():
|
||||
"""Laedt alle Sprueche aus assets/fortunes.json."""
|
||||
def _load_all_fortune_lists():
|
||||
"""Laedt alle verfuegbaren Spruchlisten."""
|
||||
global _fortunes
|
||||
|
||||
if _fortunes:
|
||||
return
|
||||
|
||||
try:
|
||||
# Versuche verschiedene Pfade
|
||||
possible_paths = [
|
||||
os.path.join(os.path.dirname(__file__), "..", "assets", "fortunes.json"),
|
||||
os.path.join("assets", "fortunes.json"),
|
||||
]
|
||||
base_path = os.path.join(os.path.dirname(__file__), "..", "assets", "fortunes")
|
||||
|
||||
for fortunes_path in possible_paths:
|
||||
if os.path.exists(fortunes_path):
|
||||
with open(fortunes_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data, list):
|
||||
_fortunes = data
|
||||
elif isinstance(data, dict):
|
||||
# Lade deutsche Sprüche, falls vorhanden, sonst englische
|
||||
_fortunes = data.get("de", data.get("en", []))
|
||||
elif isinstance(data, dict) and "fortunes" in data:
|
||||
_fortunes = data.get("fortunes", [])
|
||||
return
|
||||
for list_name in AVAILABLE_FORTUNE_LISTS:
|
||||
try:
|
||||
json_path = os.path.join(base_path, f"{list_name}.json")
|
||||
if os.path.exists(json_path):
|
||||
with open(json_path, "r", encoding="utf-8") as f:
|
||||
_fortunes[list_name] = json.load(f)
|
||||
else:
|
||||
# Fallback: Leere Liste
|
||||
_fortunes[list_name] = []
|
||||
except Exception as e:
|
||||
print(f"WARN: Spruchliste {list_name} nicht geladen: {e}")
|
||||
_fortunes[list_name] = []
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: Spruchlisten nicht geladen: {e}")
|
||||
# Fallback: Standard-Sprueche
|
||||
_fortunes = [
|
||||
"Ein guter Tag beginnt mit einem Laecheln.",
|
||||
"Das Glueck liegt in den kleinen Dingen.",
|
||||
"Geduld ist eine Tugend.",
|
||||
]
|
||||
|
||||
except Exception:
|
||||
_fortunes = []
|
||||
_fortunes = {
|
||||
"classic": [
|
||||
"Ein guter Tag beginnt mit einem Laecheln.",
|
||||
"Das Glueck liegt in den kleinen Dingen.",
|
||||
"Geduld ist eine Tugend.",
|
||||
],
|
||||
"farmer_wisdom": [],
|
||||
"unfortune": []
|
||||
}
|
||||
|
||||
|
||||
def _get_random_fortune():
|
||||
"""Gibt einen zufaelligen Spruch zurueck."""
|
||||
if not _fortunes:
|
||||
_load_fortunes()
|
||||
return random.choice(_fortunes) if _fortunes else "Keine Sprueche verfguebar."
|
||||
"""Gibt einen zufaelligen Spruch aus der aktuellen Liste zurueck."""
|
||||
global _current_fortune_list, _fortunes
|
||||
|
||||
_init()
|
||||
|
||||
if _current_fortune_list not in _fortunes or not _fortunes[_current_fortune_list]:
|
||||
# Fallback auf classic
|
||||
_current_fortune_list = "classic"
|
||||
if not _fortunes.get("classic"):
|
||||
return "Keine Sprueche verfguebar."
|
||||
|
||||
return random.choice(_fortunes[_current_fortune_list])
|
||||
|
||||
|
||||
def get_initial_fortune():
|
||||
"""Gibt einen zufaelligen Spruch fuer den Start zurueck."""
|
||||
_init()
|
||||
global _current_fortune
|
||||
_init()
|
||||
_current_fortune = _get_random_fortune()
|
||||
return _current_fortune
|
||||
|
||||
|
||||
def open_fortune():
|
||||
"""Oeffnet den Fortune Cookie (neuer Spruch)."""
|
||||
_init()
|
||||
"""Oeffnet den Fortune Cookie (neuer Spruch aus aktueller Liste)."""
|
||||
global _current_fortune
|
||||
|
||||
# Neuer Spruch
|
||||
_init()
|
||||
_current_fortune = _get_random_fortune()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -128,23 +145,16 @@ def get_current_fortune():
|
||||
|
||||
def get_new_fortune():
|
||||
"""Gibt einen neuen Spruch zurueck (Cookie schliesst sich)."""
|
||||
_init()
|
||||
global _current_fortune
|
||||
_init()
|
||||
_current_fortune = _get_random_fortune()
|
||||
return _current_fortune
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# LAST-STATE SPEICHERUNG (Musik an/aus)
|
||||
# EINSTELLUNGEN (Settings)
|
||||
# ============================================================================
|
||||
#
|
||||
# Verwende diese Funktionen, um App-Zustände zwischen App-Starts zu speichern.
|
||||
# WICHTIG:
|
||||
# - In Click-Apps: ~/.cache/<appname>/ ist beschreibbar
|
||||
# - Datei-Inhalt: Einfach "true" oder "false" als String speichern
|
||||
# - Immer FRISCH aus Datei laden (kein Caching!)
|
||||
|
||||
# Cache-Verzeichnis für Konfigurationen
|
||||
def _get_config_dir():
|
||||
"""Gibt das Konfigurationsverzeichnis zurück (Click-App kompatibel)."""
|
||||
try:
|
||||
@@ -154,42 +164,49 @@ def _get_config_dir():
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
return cache_dir
|
||||
except Exception:
|
||||
# Fallback für Tests
|
||||
return os.path.join("/tmp", APP_NAME + "_config")
|
||||
|
||||
|
||||
def _get_state_file(state_name):
|
||||
"""Gibt den Pfad zu einer Zustandsdatei zurück."""
|
||||
return os.path.join(_get_config_dir(), state_name)
|
||||
def _get_setting_file(setting_name):
|
||||
"""Gibt den Pfad zu einer Einstellungsdatei zurück."""
|
||||
return os.path.join(_get_config_dir(), f"{setting_name}.txt")
|
||||
|
||||
|
||||
def load_state(state_name, default_value=True):
|
||||
"""Lädt einen Zustand aus einer Datei.
|
||||
def load_setting(setting_name, default_value=None):
|
||||
"""Laedt eine Einstellung aus einer Datei.
|
||||
|
||||
Args:
|
||||
state_name (str): Name des Zustands
|
||||
default_value (bool): Standardwert, wenn Datei nicht existiert
|
||||
setting_name (str): Name der Einstellung
|
||||
default_value: Standardwert, wenn Datei nicht existiert
|
||||
|
||||
Rückgabe:
|
||||
bool: Der geladene Zustand (True/False)
|
||||
Wert der Einstellung (Typ hängt von default_value ab)
|
||||
"""
|
||||
try:
|
||||
state_file = _get_state_file(state_name)
|
||||
if os.path.exists(state_file):
|
||||
with open(state_file, "r") as f:
|
||||
content = f.read().strip().lower()
|
||||
return content == "true"
|
||||
setting_file = _get_setting_file(setting_name)
|
||||
if os.path.exists(setting_file):
|
||||
with open(setting_file, "r") as f:
|
||||
content = f.read().strip()
|
||||
# Try to convert to appropriate type
|
||||
if default_value is not None:
|
||||
if isinstance(default_value, bool):
|
||||
return content.lower() == "true"
|
||||
elif isinstance(default_value, int):
|
||||
return int(content)
|
||||
elif isinstance(default_value, float):
|
||||
return float(content)
|
||||
return content
|
||||
except Exception as e:
|
||||
print(f"WARN: Zustand nicht geladen ({state_name}): {e}")
|
||||
print(f"WARN: Einstellung nicht geladen ({setting_name}): {e}")
|
||||
return default_value
|
||||
|
||||
|
||||
def save_state(state_name, value):
|
||||
"""Speichert einen Zustand in einer Datei.
|
||||
def save_setting(setting_name, value):
|
||||
"""Speichert eine Einstellung in einer Datei.
|
||||
|
||||
Args:
|
||||
state_name (str): Name des Zustands
|
||||
value (bool): Der zu speichernde Zustand (True/False)
|
||||
setting_name (str): Name der Einstellung
|
||||
value: Zu speichernder Wert (wird zu String konvertiert)
|
||||
|
||||
Rückgabe:
|
||||
bool: True bei Erfolg
|
||||
@@ -197,33 +214,84 @@ def save_state(state_name, value):
|
||||
try:
|
||||
config_dir = _get_config_dir()
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
state_file = _get_state_file(state_name)
|
||||
with open(state_file, "w") as f:
|
||||
f.write("true" if value else "false")
|
||||
setting_file = _get_setting_file(setting_name)
|
||||
with open(setting_file, "w") as f:
|
||||
f.write(str(value))
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"WARN: Zustand nicht gespeichert ({state_name}): {e}")
|
||||
print(f"WARN: Einstellung nicht gespeichert ({setting_name}): {e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_music_enabled():
|
||||
"""Gibt zurück, ob Musik aktiviert ist (Last-State).
|
||||
# ============================================================================
|
||||
# LAUTSTÄRKE-EINSTELLUNGEN
|
||||
# ============================================================================
|
||||
|
||||
Lädt den Zustand FRISCH aus der Datei bei jedem Aufruf.
|
||||
"""
|
||||
return load_state("music_enabled", default_value=True)
|
||||
def get_music_volume():
|
||||
"""Gibt die Musik-Lautstärke zurück (0.0 - 1.0)."""
|
||||
return float(load_setting("music_volume", default_value=0.5))
|
||||
|
||||
|
||||
def set_music_enabled(enabled):
|
||||
"""Setzt den Musik-Status und speichert ihn persistent.
|
||||
def set_music_volume(volume):
|
||||
"""Setzt die Musik-Lautstärke (0.0 - 1.0)."""
|
||||
# Clamp value between 0.0 and 1.0
|
||||
volume = max(0.0, min(1.0, float(volume)))
|
||||
return save_setting("music_volume", volume)
|
||||
|
||||
|
||||
def get_crack_volume():
|
||||
"""Gibt die Knack-Lautstärke zurück (0.0 - 1.0)."""
|
||||
return float(load_setting("crack_volume", default_value=1.0))
|
||||
|
||||
|
||||
def set_crack_volume(volume):
|
||||
"""Setzt die Knack-Lautstärke (0.0 - 1.0)."""
|
||||
volume = max(0.0, min(1.0, float(volume)))
|
||||
return save_setting("crack_volume", volume)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SPRUCHLISTEN-EINSTELLUNGEN
|
||||
# ============================================================================
|
||||
|
||||
def get_fortune_lists():
|
||||
"""Gibt die Liste der verfuegbaren Spruchlisten zurück."""
|
||||
_init()
|
||||
return AVAILABLE_FORTUNE_LISTS
|
||||
|
||||
|
||||
def get_current_fortune_list():
|
||||
"""Gibt den Namen der aktuellen Spruchliste zurück."""
|
||||
_init()
|
||||
return load_setting("fortune_list", default_value="classic")
|
||||
|
||||
|
||||
def set_fortune_list(list_name):
|
||||
"""Setzt die aktuelle Spruchliste.
|
||||
|
||||
Args:
|
||||
enabled (bool): True = Musik an, False = Musik aus
|
||||
list_name (str): Name der Spruchliste (classic, farmer_wisdom, unfortune)
|
||||
|
||||
Rückgabe:
|
||||
bool: True bei Erfolg
|
||||
"""
|
||||
return save_state("music_enabled", enabled)
|
||||
if list_name not in AVAILABLE_FORTUNE_LISTS:
|
||||
return False
|
||||
return save_setting("fortune_list", list_name)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MUSIK EIN/AUS (Last-State)
|
||||
# ============================================================================
|
||||
|
||||
def get_music_enabled():
|
||||
"""Gibt zurück, ob Musik aktiviert ist (Last-State)."""
|
||||
return load_setting("music_enabled", default_value=True)
|
||||
|
||||
|
||||
def set_music_enabled(enabled):
|
||||
"""Setzt den Musik-Status und speichert ihn persistent."""
|
||||
return save_setting("music_enabled", bool(enabled))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user