feat: fortune cookie v1.0 - qml ui und python backend implementiert

This commit is contained in:
darklithium
2026-06-01 21:16:26 +02:00
parent 9123c7465f
commit 114ccc6c4f
6 changed files with 397 additions and 307 deletions
+278 -154
View File
@@ -1,194 +1,318 @@
"""
UNIVERSELLES PYTHON-MODUL TEMPLATE für Ubuntu Touch Apps
Basierend auf metime und Referenz-App (Version 1.7)
VERWENDUNG:
1. Kopiere diese Datei nach src/<app-name>.py
2. Ersetze '<app-name>' mit deinem App-Namen
3. Füge deine Funktionen hinzu
WICHTIG (1.7):
- KEINE dbus-Importe in diesem Modul! (PyOtherSide-Kompatibilität)
- Keine Top-Level print() Statements
- Keine Top-Level Code-Ausführung
- Ein Modul reicht für 90% der Apps (modulare Trennung ist OPTIONAL)
Fortune Cookie v1.0 - Python Backend Module
Framework 1.7 Standard
"""
import os
import random
import json
import platform
from pathlib import Path
# ============================================================================
# APP-METADATEN (Anpassen!)
# APP-METADATEN
# ============================================================================
APP_NAME = "<app-name>" # App-Name (z. B. "meine-app")
APP_VERSION = "0.1.0" # Version (Semantic Versioning)
MAINTAINER = "Christian Franz <dev@darklithium.de>" # Maintainer
APP_NAME = "fortunecookie"
APP_VERSION = "1.0.0"
MAINTAINER = "darklithium <dev@darklithium.de>"
# ============================================================================
# PLATTFORM-ERKENNUNG (1.7 Standard)
# PLATTFORM-ERKENNUNG (Framework 1.7 Standard)
# ============================================================================
def get_platform():
"""
Gibt die aktuelle Plattform zurück (arm64/amd64).
Rückgabe:
str: "arm64" oder "amd64"
"""
"""Gibt die aktuelle Plattform zurueck (arm64/amd64)."""
machine = platform.machine().lower()
if "arm" in machine or "aarch" in machine:
return "arm64"
return "amd64"
# ============================================================================
# STATUS-FUNKTIONEN
# ============================================================================
def get_status_text():
"""
Gibt einen Status-Text für die UI zurück.
Rückgabe:
str: Status-Text mit App-Name, Version und Plattform
"""
plat = get_platform()
return f"{APP_NAME} v{APP_VERSION} | Plattform: {plat}"
def get_platform():
"""Alias für get_platform (für QML-Aufrufe)."""
return get_platform()
# ============================================================================
# UI-FUNKTIONEN (Beispiele - anpassen!)
# FORTUNE-LOGIK
# ============================================================================
def get_content_text():
"""
Gibt den aktuellen Inhaltstext zurück.
# Globale Variablen
_current_fortune = ""
_fortunes = []
_initialized = False
Rückgabe:
str: Text für die UI
"""
return "Button wurde geklickt! (Python → QML)"
# Musik-Status
_music_enabled = True
_music_playing = False
def on_button_click():
"""
Wird aufgerufen, wenn der Button in QML geklickt wird.
# Medien-Player (wird lazy initialisiert)
_media_player = None
_cookie_crack_sound = None
Rückgabe:
bool: True bei Erfolg
"""
print("Button clicked") # Log für Debugging
def _init():
"""Initialisiert das Modul (wird beim ersten Aufruf ausgefuehrt)."""
global _fortunes, _initialized, _music_enabled
if _initialized:
return True
# Lade Fortunes
_load_fortunes()
# Lade Last-State
_music_enabled = _load_music_state()
_initialized = True
return True
def _load_fortunes():
"""Laedt alle Sprueche aus assets/fortunes.json."""
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"),
]
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):
_fortunes = data.get("fortunes", [])
return
# Fallback: Standard-Sprueche
_fortunes = [
"Ein guter Tag beginnt mit einem Laecheln.",
"Das Glueck liegt in den kleinen Dingen.",
"Geduld ist eine Tugend.",
]
except Exception:
# Fallback: Einige Standard-Sprueche
_fortunes = [
"Ein guter Tag beginnt mit einem Laecheln.",
"Das Glueck liegt in den kleinen Dingen.",
"Geduld ist eine Tugend.",
]
def get_initial_fortune():
"""Gibt einen zufaelligen Spruch fuer den Start zurueck."""
_init()
global _current_fortune
_current_fortune = _get_random_fortune()
return _current_fortune
def open_fortune():
"""Oeffnet den Fortune Cookie (neuer Spruch + Knack-Geraeusch)."""
_init()
global _current_fortune
# Neuer Spruch
_current_fortune = _get_random_fortune()
# Knack-Geraeusch abspielen (wenn verfguebar)
_play_crack_sound()
return True
def get_current_fortune():
"""Gibt den aktuellen Spruch zurueck."""
_init()
global _current_fortune
return _current_fortune
def get_new_fortune():
"""Gibt einen neuen Spruch zurueck (Cookie schliesst sich)."""
_init()
global _current_fortune
_current_fortune = _get_random_fortune()
return _current_fortune
def _get_random_fortune():
"""Gibt einen zufaelligen Spruch zurueck."""
if not _fortunes:
_load_fortunes()
return random.choice(_fortunes) if _fortunes else "Keine Sprueche verfguebar."
# ============================================================================
# DATENVERZEICHNIS (Optional - für persistente Daten)
# MUSIK-LOGIK
# ============================================================================
def start_music():
"""Startet die Hintergrundmusik."""
_init()
global _music_playing, _media_player, _music_enabled
if not _music_enabled:
return False
try:
# Medien-Player initialisieren (wenn nicht vorhanden)
if _media_player is None:
from PySide2 import QtMultimedia, QtCore
_media_player = QtMultimedia.QMediaPlayer()
audio_output = QtMultimedia.QAudioOutput()
_media_player.setAudioOutput(audio_output)
# Musik-Datei laden
music_path = get_asset_path("chinese_music.mp3")
_media_player.setSource(QtCore.QUrl.fromLocalFile(music_path))
_media_player.setLoops(QtMultimedia.QMediaPlayer.Infinite)
_media_player.setVolume(50)
_media_player.play()
_music_playing = True
return True
except Exception:
return False
def stop_music():
"""Stoppt die Hintergrundmusik."""
global _music_playing, _media_player
if _media_player is not None:
try:
_media_player.stop()
except Exception:
pass
_music_playing = False
return True
def toggle_music():
"""Wechselt Musik-Status (an/aus)."""
if _music_playing:
return stop_music()
else:
return start_music()
def set_music_enabled(enabled):
"""Aktiviert/Deaktiviert Musik generell."""
global _music_enabled
_music_enabled = enabled
_save_music_state(enabled)
return enabled
def get_music_enabled():
"""Gibt zurueck, ob Musik aktiviert ist."""
global _music_enabled
return _music_enabled
def get_music_playing():
"""Gibt zurueck, ob Musik gerade spielt."""
global _music_playing
return _music_playing
# ============================================================================
# LAST-STATE SPEICHERUNG
# ============================================================================
def _get_data_dir():
"""Gibt das Datenverzeichnis zurueck."""
if "CLICK" in os.environ:
return os.path.join(
os.path.expanduser("~"), ".local", "share", f"{APP_NAME}.darklithium"
)
else:
return os.path.join(
os.path.expanduser("~"), ".local", "share", f"{APP_NAME}"
)
def _load_music_state():
"""Laedt den Musik-Status aus Datei."""
try:
state_file = os.path.join(_get_data_dir(), "music_state.json")
if os.path.exists(state_file):
with open(state_file, "r") as f:
data = json.load(f)
return data.get("enabled", True)
except Exception:
pass
return True
def _save_music_state(enabled):
"""Speichert den Musik-Status in Datei."""
try:
data_dir = _get_data_dir()
os.makedirs(data_dir, exist_ok=True)
state_file = os.path.join(data_dir, "music_state.json")
with open(state_file, "w") as f:
json.dump({"enabled": enabled}, f)
except Exception:
pass
def _play_crack_sound():
"""Spielt das Knack-Geraeusch ab."""
try:
global _cookie_crack_sound
if _cookie_crack_sound is None:
from PySide2 import QtMultimedia, QtCore
_cookie_crack_sound = QtMultimedia.QMediaPlayer()
audio_output = QtMultimedia.QAudioOutput()
_cookie_crack_sound.setAudioOutput(audio_output)
crack_path = get_asset_path("cookie_crack.mp3")
if os.path.exists(crack_path):
_cookie_crack_sound.setSource(QtCore.QUrl.fromLocalFile(crack_path))
_cookie_crack_sound.setVolume(100)
_cookie_crack_sound.play()
except Exception:
pass
# ============================================================================
# DATENVERZEICHNIS (fuer zukuenftige Erweiterungen)
# ============================================================================
def get_data_dir():
"""
Gibt das Datenverzeichnis der App zurück.
"""Gibt das Datenverzeichnis der App zurueck."""
return _get_data_dir()
Rückgabe:
str: Pfad zum Datenverzeichnis
"""
app_dir = os.path.join(
os.path.expanduser("~"),
".local",
"share",
f"{APP_NAME}.darklithium"
)
os.makedirs(app_dir, exist_ok=True)
return app_dir
def get_data_file_path(filename):
"""
Gibt den Pfad zu einer Daten-Datei zurück.
def get_fortunes_file_path():
"""Gibt den Pfad zur Fortunes-Datei zurueck."""
return os.path.join(_get_data_dir(), "fortunes.json")
Args:
filename (str): Dateiname
Rückgabe:
str: Vollständiger Pfad zur Datei
"""
return os.path.join(get_data_dir(), filename)
# ============================================================================
# BEISPIEL: DATEN LADEN/SPEICHERN
# PLATTFORM-SPEZIFISCHE PFADERMITTLUNG
# ============================================================================
def load_data(filename="data.json"):
"""
Lädt JSON-Daten aus einer Datei.
def get_asset_path(filename):
"""Gibt den Pfad zu einer Asset-Datei zurueck (funktioniert in Click & Desktop)."""
possible_paths = [
os.path.join(os.path.dirname(__file__), "..", "assets", filename),
os.path.join("assets", filename),
]
Args:
filename (str): Dateiname
for path in possible_paths:
if os.path.exists(path):
return path
Rückgabe:
dict: Geladene Daten (oder {} bei Fehler)
"""
import json
file_path = get_data_file_path(filename)
try:
with open(file_path, "r") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {}
def save_data(data, filename="data.json"):
"""
Speichert Daten in eine JSON-Datei.
Args:
data (dict): Zu speichernde Daten
filename (str): Dateiname
Rückgabe:
bool: True bei Erfolg
"""
import json
file_path = get_data_file_path(filename)
try:
with open(file_path, "w") as f:
json.dump(data, f, indent=2)
return True
except Exception as e:
print(f"Fehler beim Speichern: {e}")
return False
# ============================================================================
# BEISPIEL: EINFACHE LOGIK
# ============================================================================
# Zähler für Button-Klicks (Beispiel)
_click_counter = 0
def increment_counter():
"""Inkrementiert den Klick-Zähler."""
global _click_counter
_click_counter += 1
return _click_counter
def get_counter():
"""Gibt den aktuellen Zählerstand zurück."""
global _click_counter
return _click_counter
# ============================================================================
# MAIN (wird nicht automatisch ausgeführt - PyOtherSide lädt nur Funktionen)
# ============================================================================
# Hinweis: In PyOtherSide wird nur importiert, was in QML aufgerufen wird.
# Top-Level Code wird NICHT ausgeführt!
# Beispiel für Initialisierung (wird erst beim ersten Aufruf ausgeführt):
_initialized = False
def init():
"""Initialisiert das Modul (wird beim ersten Aufruf aus QML ausgeführt)."""
global _initialized
if not _initialized:
print(f"{APP_NAME} Modul initialisiert")
_initialized = True
return _initialized
return os.path.join("assets", filename)