fix: Last-State Bug, Musik-Button und Cookie-Größe

- Label direkt verwendet (nicht in Item genestet)
- Icon-Größe auf xx-large erhöht
- Musik-Button erst nach Initialisierung sichtbar
- Geöffneter Keks größer (36x28 vs 32x24 GU)
- Last-State Funktionen bereinigt

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
darklithium
2026-06-02 03:31:56 +02:00
parent ace4d9c43c
commit da719d7670
2 changed files with 131 additions and 95 deletions
+69 -45
View File
@@ -12,10 +12,13 @@ MainView {
height: units.gu(75) height: units.gu(75)
theme.name: "Lomiri.Components.Themes.SuruDark" theme.name: "Lomiri.Components.Themes.SuruDark"
// ====================================================================
// PROPERTIES (am Anfang definieren!)
// ====================================================================
property bool fortuneOpened: false property bool fortuneOpened: false
property string currentFortune: "" property string currentFortune: ""
property bool musicPlaying: false property bool musicPlaying: false
property bool musicButtonVisible: false property bool appInitialized: false
Python { Python {
id: py id: py
@@ -40,6 +43,40 @@ MainView {
volume: 1.0 volume: 1.0
} }
// ====================================================================
// INITIALISIERUNGS-TIMER
// ====================================================================
// WICHTIG: PyOtherSide braucht Zeit zum Laden!
// 1 Sekunde Verzögerung verhindert Race Conditions
Timer {
id: initTimer
interval: 1000
running: true
repeat: false
onTriggered: {
try {
currentFortune = py.call_sync("fortunecookie.get_initial_fortune", []);
currentFortuneLabel.text = currentFortune;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_closed2.png");
// Musik-Status laden (Last-State)
musicPlaying = py.call_sync("fortunecookie.get_music_enabled", []);
console.log("DEBUG QML: musicPlaying geladen: " + musicPlaying);
// MediaPlayer Zustand synchronisieren
if (musicPlaying) {
mediaPlayer.play();
}
// UI erst nach Initialisierung anzeigen
appInitialized = true;
} catch (e) {
console.log("ERROR QML: Initialisierung fehlgeschlagen: " + e);
}
}
}
Page { Page {
id: mainPage id: mainPage
anchors.fill: parent anchors.fill: parent
@@ -48,30 +85,10 @@ MainView {
title: "Fortune Cookie" title: "Fortune Cookie"
} }
Timer {
id: initTimer
interval: 1000 // Warte 1 Sekunde auf Python-Ladung
running: true
repeat: false
onTriggered: {
try {
currentFortuneLabel.text = py.call_sync("fortunecookie.get_initial_fortune", []);
cookieImage.source = Qt.resolvedUrl("../assets/cookie_closed2.png");
musicPlaying = py.call_sync("fortunecookie.get_music_enabled", []);
console.log("DEBUG QML: musicPlaying loaded from Python: " + musicPlaying);
if (musicPlaying) {
mediaPlayer.play();
}
musicButtonVisible = true;
} catch (e) {
console.log("ERROR QML: Failed to initialize: " + e);
}
}
}
Image { Image {
id: cookieImage id: cookieImage
anchors.centerIn: parent anchors.centerIn: parent
// Geöffneter Keks ist größer als geschlossener
width: fortuneOpened ? units.gu(36) : units.gu(32) width: fortuneOpened ? units.gu(36) : units.gu(32)
height: fortuneOpened ? units.gu(28) : units.gu(24) height: fortuneOpened ? units.gu(28) : units.gu(24)
source: fortuneOpened ? Qt.resolvedUrl("../assets/cookie_open2.png") : Qt.resolvedUrl("../assets/cookie_closed2.png") source: fortuneOpened ? Qt.resolvedUrl("../assets/cookie_open2.png") : Qt.resolvedUrl("../assets/cookie_closed2.png")
@@ -90,6 +107,7 @@ MainView {
fortuneOpened = true; fortuneOpened = true;
currentFortune = py.call_sync("fortunecookie.get_current_fortune", []); currentFortune = py.call_sync("fortunecookie.get_current_fortune", []);
currentFortuneLabel.text = currentFortune; currentFortuneLabel.text = currentFortune;
currentFortuneLabel.visible = true;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_open2.png"); cookieImage.source = Qt.resolvedUrl("../assets/cookie_open2.png");
}); });
} }
@@ -133,7 +151,7 @@ MainView {
text: "" text: ""
fontSize: "large" fontSize: "large"
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
visible: fortuneOpened visible: false
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
MouseArea { MouseArea {
@@ -150,8 +168,15 @@ MainView {
} }
} }
Item { // ================================================================
id: musicButtonContainer // MUSIK-BUTTON
// ================================================================
// FIX: Label DIREKT verwenden (nicht in Item nesten!) - sonst "Element is not creatable"
// Icon-Größe: xx-large für bessere Sichtbarkeit
// Hintergrund: transparent (wie Page-Hintergrund)
// Erst nach Initialisierung anzeigen, um Flackern zu vermeiden
Label {
id: musicButton
anchors { anchors {
right: parent.right right: parent.right
bottom: parent.bottom bottom: parent.bottom
@@ -159,29 +184,28 @@ MainView {
} }
width: units.gu(10) width: units.gu(10)
height: units.gu(10) height: units.gu(10)
visible: musicButtonVisible text: musicPlaying ? "\uD83D\uDD0A" : "\uD83D\uDD07" // 🎵 oder 🔇
fontSize: "xx-large" // Großes Icon für gute Sichtbarkeit
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: theme.palette.normalText
// FIX: Erst nach Initialisierung anzeigen
visible: appInitialized
Label { MouseArea {
id: musicButton anchors.fill: parent
text: musicPlaying ? "\uD83D\uDD0A" : "\uD83D\uDD07" hoverEnabled: true
fontSize: "x-large"
anchors.centerIn: parent
MouseArea { onClicked: {
anchors.fill: parent console.log("DEBUG QML: Music button clicked, current musicPlaying: " + musicPlaying);
hoverEnabled: true if (musicPlaying) {
mediaPlayer.stop();
onClicked: { } else {
console.log("DEBUG QML: Music button clicked, current musicPlaying: " + musicPlaying); mediaPlayer.play();
if (musicPlaying) {
mediaPlayer.stop();
} else {
mediaPlayer.play();
}
musicPlaying = !musicPlaying;
console.log("DEBUG QML: Setting music enabled to: " + musicPlaying);
py.call("fortunecookie.set_music_enabled", [musicPlaying]);
} }
musicPlaying = !musicPlaying;
console.log("DEBUG QML: Setting music enabled to: " + musicPlaying);
py.call("fortunecookie.set_music_enabled", [musicPlaying]);
} }
} }
} }
+62 -50
View File
@@ -2,6 +2,7 @@
Fortune Cookie v1.0 - Python Backend Module Fortune Cookie v1.0 - Python Backend Module
Framework 1.7 Standard Framework 1.7 Standard
Audio-Steuerung in QML (keine Qt-Python-Bindings benoetigt) Audio-Steuerung in QML (keine Qt-Python-Bindings benoetigt)
Last-State Speicherung mit Datei-basiertem Speicher
""" """
import os import os
@@ -17,6 +18,7 @@ APP_NAME = "fortunecookie"
APP_VERSION = "1.0.0" APP_VERSION = "1.0.0"
MAINTAINER = "darklithium <dev@darklithium.de>" MAINTAINER = "darklithium <dev@darklithium.de>"
# ============================================================================ # ============================================================================
# PLATTFORM-ERKENNUNG (Framework 1.7 Standard) # PLATTFORM-ERKENNUNG (Framework 1.7 Standard)
# ============================================================================ # ============================================================================
@@ -38,9 +40,6 @@ _current_fortune = ""
_fortunes = [] _fortunes = []
_initialized = False _initialized = False
# Musik-Status (wird dynamisch von Datei geladen)
# _music_enabled wird nicht als globale Variable gespeichert, sondern immer frisch geladen
def _init(): def _init():
"""Initialisiert das Modul (wird beim ersten Aufruf ausgefuehrt).""" """Initialisiert das Modul (wird beim ersten Aufruf ausgefuehrt)."""
@@ -138,80 +137,93 @@ def get_new_fortune():
# ============================================================================ # ============================================================================
# LAST-STATE SPEICHERUNG (Musik an/aus) # LAST-STATE SPEICHERUNG (Musik an/aus)
# ============================================================================ # ============================================================================
#
# 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(): def _get_config_dir():
"""Gibt das Konfigurationsverzeichnis der App zurueck. """Gibt das Konfigurationsverzeichnis zurück (Click-App kompatibel)."""
Click-Apps auf UBPorts haben eingeschraenkte Schreibrechte.
Verwendete Pfade:
- ~/.cache/<appname>/ (funktioniert in Click-Apps)
"""
try: try:
home = os.path.expanduser("~") home = os.path.expanduser("~")
# Click-App-Pfad (funktioniert in der Sandbox) app_name = APP_NAME + ".darklithium"
app_name = "fortunecookie.darklithium"
cache_dir = os.path.join(home, ".cache", app_name) cache_dir = os.path.join(home, ".cache", app_name)
os.makedirs(cache_dir, exist_ok=True) os.makedirs(cache_dir, exist_ok=True)
return cache_dir return cache_dir
except Exception: except Exception:
# Fallback # Fallback für Tests
return os.path.join("/tmp", "fortunecookie") return os.path.join("/tmp", APP_NAME + "_config")
def _get_music_state_file(): def _get_state_file(state_name):
"""Gibt den Pfad zur Musik-Status-Datei zurueck.""" """Gibt den Pfad zu einer Zustandsdatei zurück."""
config_dir = _get_config_dir() return os.path.join(_get_config_dir(), state_name)
return os.path.join(config_dir, "music_enabled")
def _load_music_state(): def load_state(state_name, default_value=True):
"""Laedt den Musik-Status aus Datei (true/false).""" """Lädt einen Zustand aus einer Datei.
Args:
state_name (str): Name des Zustands
default_value (bool): Standardwert, wenn Datei nicht existiert
Rückgabe:
bool: Der geladene Zustand (True/False)
"""
try: try:
state_file = _get_music_state_file() state_file = _get_state_file(state_name)
print(f"DEBUG: Loading music state from: {state_file}")
print(f"DEBUG: File exists: {os.path.exists(state_file)}")
if os.path.exists(state_file): if os.path.exists(state_file):
with open(state_file, "r") as f: with open(state_file, "r") as f:
content = f.read().strip().lower() content = f.read().strip().lower()
print(f"DEBUG: File content: '{content}'") return content == "true"
result = content == "true"
print(f"DEBUG: Music enabled: {result}")
return result
else:
print("DEBUG: Music state file does not exist, using default: True")
except Exception as e: except Exception as e:
print(f"WARN: Musik-Status nicht geladen: {e}") print(f"WARN: Zustand nicht geladen ({state_name}): {e}")
import traceback return default_value
traceback.print_exc()
# Default: Musik an
return True
def _save_music_state(enabled): def save_state(state_name, value):
"""Speichert den Musik-Status in Datei (true/false).""" """Speichert einen Zustand in einer Datei.
Args:
state_name (str): Name des Zustands
value (bool): Der zu speichernde Zustand (True/False)
Rückgabe:
bool: True bei Erfolg
"""
try: try:
config_dir = _get_config_dir() config_dir = _get_config_dir()
os.makedirs(config_dir, exist_ok=True) os.makedirs(config_dir, exist_ok=True)
state_file = _get_music_state_file() state_file = _get_state_file(state_name)
print(f"DEBUG: Saving music state {enabled} to: {state_file}")
with open(state_file, "w") as f: with open(state_file, "w") as f:
f.write("true" if enabled else "false") f.write("true" if value else "false")
print(f"DEBUG: Successfully saved music state") return True
except Exception as e: except Exception as e:
print(f"WARN: Musik-Status nicht gespeichert: {e}") print(f"WARN: Zustand nicht gespeichert ({state_name}): {e}")
import traceback return False
traceback.print_exc()
def set_music_enabled(enabled):
"""Aktiviert/Deaktiviert die Musik und speichert den Status."""
_save_music_state(enabled)
return True
def get_music_enabled(): def get_music_enabled():
"""Gibt den Musik-Status zurueck (frisch von Datei geladen).""" """Gibt zurück, ob Musik aktiviert ist (Last-State).
return _load_music_state()
Lädt den Zustand FRISCH aus der Datei bei jedem Aufruf.
"""
return load_state("music_enabled", default_value=True)
def set_music_enabled(enabled):
"""Setzt den Musik-Status und speichert ihn persistent.
Args:
enabled (bool): True = Musik an, False = Musik aus
Rückgabe:
bool: True bei Erfolg
"""
return save_state("music_enabled", enabled)
# ============================================================================ # ============================================================================