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
+57 -33
View File
@@ -12,10 +12,13 @@ MainView {
height: units.gu(75)
theme.name: "Lomiri.Components.Themes.SuruDark"
// ====================================================================
// PROPERTIES (am Anfang definieren!)
// ====================================================================
property bool fortuneOpened: false
property string currentFortune: ""
property bool musicPlaying: false
property bool musicButtonVisible: false
property bool appInitialized: false
Python {
id: py
@@ -40,6 +43,40 @@ MainView {
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 {
id: mainPage
anchors.fill: parent
@@ -48,30 +85,10 @@ MainView {
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 {
id: cookieImage
anchors.centerIn: parent
// Geöffneter Keks ist größer als geschlossener
width: fortuneOpened ? units.gu(36) : units.gu(32)
height: fortuneOpened ? units.gu(28) : units.gu(24)
source: fortuneOpened ? Qt.resolvedUrl("../assets/cookie_open2.png") : Qt.resolvedUrl("../assets/cookie_closed2.png")
@@ -90,6 +107,7 @@ MainView {
fortuneOpened = true;
currentFortune = py.call_sync("fortunecookie.get_current_fortune", []);
currentFortuneLabel.text = currentFortune;
currentFortuneLabel.visible = true;
cookieImage.source = Qt.resolvedUrl("../assets/cookie_open2.png");
});
}
@@ -133,7 +151,7 @@ MainView {
text: ""
fontSize: "large"
horizontalAlignment: Text.AlignHCenter
visible: fortuneOpened
visible: false
wrapMode: Text.WordWrap
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 {
right: parent.right
bottom: parent.bottom
@@ -159,13 +184,13 @@ MainView {
}
width: units.gu(10)
height: units.gu(10)
visible: musicButtonVisible
Label {
id: musicButton
text: musicPlaying ? "\uD83D\uDD0A" : "\uD83D\uDD07"
fontSize: "x-large"
anchors.centerIn: parent
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
MouseArea {
anchors.fill: parent
@@ -185,5 +210,4 @@ MainView {
}
}
}
}
}
+62 -50
View File
@@ -2,6 +2,7 @@
Fortune Cookie v1.0 - Python Backend Module
Framework 1.7 Standard
Audio-Steuerung in QML (keine Qt-Python-Bindings benoetigt)
Last-State Speicherung mit Datei-basiertem Speicher
"""
import os
@@ -17,6 +18,7 @@ APP_NAME = "fortunecookie"
APP_VERSION = "1.0.0"
MAINTAINER = "darklithium <dev@darklithium.de>"
# ============================================================================
# PLATTFORM-ERKENNUNG (Framework 1.7 Standard)
# ============================================================================
@@ -38,9 +40,6 @@ _current_fortune = ""
_fortunes = []
_initialized = False
# Musik-Status (wird dynamisch von Datei geladen)
# _music_enabled wird nicht als globale Variable gespeichert, sondern immer frisch geladen
def _init():
"""Initialisiert das Modul (wird beim ersten Aufruf ausgefuehrt)."""
@@ -138,80 +137,93 @@ def get_new_fortune():
# ============================================================================
# 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():
"""Gibt das Konfigurationsverzeichnis der App zurueck.
Click-Apps auf UBPorts haben eingeschraenkte Schreibrechte.
Verwendete Pfade:
- ~/.cache/<appname>/ (funktioniert in Click-Apps)
"""
"""Gibt das Konfigurationsverzeichnis zurück (Click-App kompatibel)."""
try:
home = os.path.expanduser("~")
# Click-App-Pfad (funktioniert in der Sandbox)
app_name = "fortunecookie.darklithium"
app_name = APP_NAME + ".darklithium"
cache_dir = os.path.join(home, ".cache", app_name)
os.makedirs(cache_dir, exist_ok=True)
return cache_dir
except Exception:
# Fallback
return os.path.join("/tmp", "fortunecookie")
# Fallback für Tests
return os.path.join("/tmp", APP_NAME + "_config")
def _get_music_state_file():
"""Gibt den Pfad zur Musik-Status-Datei zurueck."""
config_dir = _get_config_dir()
return os.path.join(config_dir, "music_enabled")
def _get_state_file(state_name):
"""Gibt den Pfad zu einer Zustandsdatei zurück."""
return os.path.join(_get_config_dir(), state_name)
def _load_music_state():
"""Laedt den Musik-Status aus Datei (true/false)."""
def load_state(state_name, default_value=True):
"""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:
state_file = _get_music_state_file()
print(f"DEBUG: Loading music state from: {state_file}")
print(f"DEBUG: File exists: {os.path.exists(state_file)}")
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()
print(f"DEBUG: File content: '{content}'")
result = content == "true"
print(f"DEBUG: Music enabled: {result}")
return result
else:
print("DEBUG: Music state file does not exist, using default: True")
return content == "true"
except Exception as e:
print(f"WARN: Musik-Status nicht geladen: {e}")
import traceback
traceback.print_exc()
# Default: Musik an
return True
print(f"WARN: Zustand nicht geladen ({state_name}): {e}")
return default_value
def _save_music_state(enabled):
"""Speichert den Musik-Status in Datei (true/false)."""
def save_state(state_name, value):
"""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:
config_dir = _get_config_dir()
os.makedirs(config_dir, exist_ok=True)
state_file = _get_music_state_file()
print(f"DEBUG: Saving music state {enabled} to: {state_file}")
state_file = _get_state_file(state_name)
with open(state_file, "w") as f:
f.write("true" if enabled else "false")
print(f"DEBUG: Successfully saved music state")
except Exception as e:
print(f"WARN: Musik-Status nicht gespeichert: {e}")
import traceback
traceback.print_exc()
def set_music_enabled(enabled):
"""Aktiviert/Deaktiviert die Musik und speichert den Status."""
_save_music_state(enabled)
f.write("true" if value else "false")
return True
except Exception as e:
print(f"WARN: Zustand nicht gespeichert ({state_name}): {e}")
return False
def get_music_enabled():
"""Gibt den Musik-Status zurueck (frisch von Datei geladen)."""
return _load_music_state()
"""Gibt zurück, ob Musik aktiviert ist (Last-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)
# ============================================================================