Metronom reagiert auf Benutzereingaben
This commit is contained in:
parent
c0d4598191
commit
9573efb391
4 changed files with 250 additions and 4 deletions
26
app.py
26
app.py
|
|
@ -234,6 +234,9 @@ def index():
|
|||
metronome_enabled = session.get('metronome_enabled', False)
|
||||
metronome_bpm = session.get('metronome_bpm', 60)
|
||||
metronome_sound = session.get('metronome_sound', 'beep')
|
||||
metronome_mode = session.get('metronome_mode', 'automatic')
|
||||
metronome_speed = session.get('metronome_speed', 60)
|
||||
target_error_rate = session.get('target_error_rate', 5)
|
||||
|
||||
return render_template('index.html',
|
||||
current_text=current_text,
|
||||
|
|
@ -244,7 +247,10 @@ def index():
|
|||
current_lesson_index=progress.current_text_index,
|
||||
metronome_enabled=metronome_enabled,
|
||||
metronome_bpm=metronome_bpm,
|
||||
metronome_sound=metronome_sound)
|
||||
metronome_sound=metronome_sound,
|
||||
metronome_mode=metronome_mode,
|
||||
metronome_speed=metronome_speed,
|
||||
target_error_rate=target_error_rate)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Fehler in index(): {e}")
|
||||
|
|
@ -582,6 +588,24 @@ def get_lesson_statistics(lesson_index):
|
|||
print(f"Fehler in get_lesson_statistics: {e}")
|
||||
return jsonify({'error': 'Interner Serverfehler'}), 500
|
||||
|
||||
@app.route('/settings')
|
||||
def settings():
|
||||
"""Einstellungsseite"""
|
||||
return render_template('settings.html')
|
||||
|
||||
@app.route('/save_settings', methods=['POST'])
|
||||
def save_settings():
|
||||
"""Speichert die Einstellungen in der Session"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
session['metronome_mode'] = data.get('metronome_mode', 'automatic')
|
||||
session['metronome_speed'] = data.get('metronome_speed', 60)
|
||||
session['target_error_rate'] = data.get('target_error_rate', 5)
|
||||
return jsonify({'status': 'success'})
|
||||
except Exception as e:
|
||||
print(f"Fehler in save_settings: {e}")
|
||||
return jsonify({'error': 'Interner Serverfehler'}), 500
|
||||
|
||||
@app.route('/set_metronome', methods=['POST'])
|
||||
def set_metronome():
|
||||
"""Setzt die Metronom-Einstellungen in der Session"""
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
helpModal.show();
|
||||
});
|
||||
|
||||
// Settings Link initialisieren
|
||||
document.getElementById('settings-toggle').addEventListener('click', function() {
|
||||
window.location.href = '/settings';
|
||||
});
|
||||
|
||||
// Aktuelle Textdaten - mit Fallback falls undefiniert
|
||||
let fullText = window.fullText || "Kein Text verfügbar. Bitte laden Sie die Seite neu.";
|
||||
let currentCharIndex = window.currentCharIndex || 0;
|
||||
|
|
@ -65,7 +70,18 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
let metronomeInterval = null;
|
||||
let audioContext = null;
|
||||
let metronomeSound = window.metronomeSound || 'beep';
|
||||
let metronomeBPM = window.metronomeBPM || 60;
|
||||
let metronomeMode = window.metronomeMode || 'automatic'; // automatic or explicit
|
||||
let targetErrorRate = window.targetErrorRate || 5; // Prozent
|
||||
let currentErrorRate = 0;
|
||||
let currentSpeed = 0; // Aktuelle Tippgeschwindigkeit in ZPM
|
||||
|
||||
// Metronom-Geschwindigkeit basierend auf Modus initialisieren
|
||||
let metronomeBPM;
|
||||
if (metronomeMode === 'explicit') {
|
||||
metronomeBPM = window.metronomeSpeed || 60;
|
||||
} else {
|
||||
metronomeBPM = window.metronomeBPM || 60;
|
||||
}
|
||||
|
||||
// Metronom-Funktionen
|
||||
function createBeepSound(vol, freq, duration) {
|
||||
|
|
@ -156,6 +172,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
}
|
||||
}
|
||||
|
||||
// Funktion zur automatischen Anpassung der Metronom-Geschwindigkeit
|
||||
function adjustMetronomeSpeed() {
|
||||
if (metronomeMode === 'automatic') {
|
||||
if (currentErrorRate < targetErrorRate) {
|
||||
// Fehlerrate ist niedrig -> Geschwindigkeit erhöhen
|
||||
metronomeBPM = Math.min(600, metronomeBPM + 10);
|
||||
} else {
|
||||
// Fehlerrate ist hoch -> Geschwindigkeit verringern
|
||||
metronomeBPM = Math.max(60, metronomeBPM - 10);
|
||||
}
|
||||
|
||||
// Starte Metronom mit neuer Geschwindigkeit neu
|
||||
if (metronomeEnabled && !isPaused) {
|
||||
startMetronome();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Starte Metronom standardmäßig
|
||||
if (!isPaused) {
|
||||
startMetronome();
|
||||
|
|
@ -183,7 +217,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
body: JSON.stringify({
|
||||
enabled: metronomeEnabled,
|
||||
bpm: metronomeBPM,
|
||||
sound: metronomeSound
|
||||
sound: metronomeSound,
|
||||
mode: metronomeMode,
|
||||
speed: metronomeBPM, // Verwende die aktuelle BPM
|
||||
target_error_rate: targetErrorRate
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
@ -624,6 +661,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
totalElement.textContent = totalKeyStrokes;
|
||||
wpmElement.textContent = Math.round(wpm);
|
||||
|
||||
// Speichere aktuelle Werte für Metronom-Anpassung
|
||||
currentSpeed = Math.round(charsPerMinute);
|
||||
currentErrorRate = errorRate;
|
||||
|
||||
// Passe Metronom-Geschwindigkeit automatisch an
|
||||
adjustMetronomeSpeed();
|
||||
|
||||
// Aktualisiere die Textfarben
|
||||
updateTextDisplay();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Typewriter Trainer</h2>
|
||||
<div class="d-flex flex-row-reverse align-items-center gap-2">
|
||||
<i data-lucide="settings" class="icon-placeholder" style="width: 24px; height: 24px; cursor: pointer;"></i>
|
||||
<div id="settings-toggle" style="width: 24px; height: 24px; cursor: pointer;">
|
||||
<i data-lucide="settings" style="width: 24px; height: 24px;"></i>
|
||||
</div>
|
||||
<div id="theme-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<i data-lucide="sun" id="theme-toggle-sun" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="moon" id="theme-toggle-moon" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
|
|
@ -71,6 +73,9 @@
|
|||
window.metronomeEnabled = {{ metronome_enabled|tojson }};
|
||||
window.metronomeBPM = {{ metronome_bpm|tojson }};
|
||||
window.metronomeSound = {{ metronome_sound|tojson }};
|
||||
window.metronomeMode = {{ metronome_mode|tojson }};
|
||||
window.metronomeSpeed = {{ metronome_speed|tojson }};
|
||||
window.targetErrorRate = {{ target_error_rate|tojson }};
|
||||
</script>
|
||||
|
||||
|
||||
|
|
|
|||
173
templates/settings.html
Normal file
173
templates/settings.html
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Einstellungen - Typewriter Trainer</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
||||
</head>
|
||||
<body class="bg-light" data-bs-theme="light">
|
||||
<div class="container py-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Einstellungen</h2>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div id="theme-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<i data-lucide="sun" id="theme-toggle-sun" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="moon" id="theme-toggle-moon" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
</div>
|
||||
<button onclick="window.location.href='/'" class="btn btn-outline-light">
|
||||
Zurück zum Training
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Metronom-Einstellungen -->
|
||||
<div class="mb-4">
|
||||
<h5 class="card-title">Metronom-Einstellungen</h5>
|
||||
|
||||
<!-- Modus-Auswahl -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Metronom-Modus</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="metronomeMode" id="automaticMode" value="automatic" checked>
|
||||
<label class="form-check-label" for="automaticMode">
|
||||
Automatisch
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="metronomeMode" id="explicitMode" value="explicit">
|
||||
<label class="form-check-label" for="explicitMode">
|
||||
Explizit
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Explizite Geschwindigkeitseinstellung -->
|
||||
<div id="explicitSettings" class="mb-3" style="display: none;">
|
||||
<label for="metronomeSpeed" class="form-label">Metronom-Geschwindigkeit (ZPM): <span id="speedValue">60</span></label>
|
||||
<input type="range" class="form-range" id="metronomeSpeed" min="60" max="600" step="10" value="60">
|
||||
<small class="form-text text-muted">Einstellbereich: 60 - 600 Zeichen pro Minute</small>
|
||||
</div>
|
||||
|
||||
<!-- Automatische Einstellungen -->
|
||||
<div id="automaticSettings" class="mb-3">
|
||||
<label for="targetErrorRate" class="form-label">Ziel-Fehlerrate (%): <span id="errorRateValue">5</span></label>
|
||||
<input type="range" class="form-range" id="targetErrorRate" min="1" max="20" step="1" value="5">
|
||||
<small class="form-text text-muted">Die Metronom-Geschwindigkeit wird automatisch angepasst, um diese Fehlerrate nicht zu überschreiten</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button id="saveSettings" class="btn btn-primary">Einstellungen speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
// Theme Management
|
||||
function initializeTheme() {
|
||||
const savedTheme = localStorage.getItem('theme') || 'light';
|
||||
document.body.setAttribute('data-bs-theme', savedTheme);
|
||||
updateThemeButton(savedTheme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const currentTheme = document.body.getAttribute('data-bs-theme');
|
||||
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
document.body.setAttribute('data-bs-theme', newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
updateThemeButton(newTheme);
|
||||
}
|
||||
|
||||
function updateThemeButton(theme) {
|
||||
const sunIcon = document.getElementById('theme-toggle-sun');
|
||||
const moonIcon = document.getElementById('theme-toggle-moon');
|
||||
if (theme === 'dark') {
|
||||
sunIcon.style.display = 'none';
|
||||
moonIcon.style.display = 'block';
|
||||
} else {
|
||||
sunIcon.style.display = 'block';
|
||||
moonIcon.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Metronom-Modus Umschaltung
|
||||
document.querySelectorAll('input[name="metronomeMode"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
const explicitSettings = document.getElementById('explicitSettings');
|
||||
const automaticSettings = document.getElementById('automaticSettings');
|
||||
|
||||
if (this.value === 'explicit') {
|
||||
explicitSettings.style.display = 'block';
|
||||
automaticSettings.style.display = 'none';
|
||||
} else {
|
||||
explicitSettings.style.display = 'none';
|
||||
automaticSettings.style.display = 'block';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Schieberegler für explizite Geschwindigkeit
|
||||
document.getElementById('metronomeSpeed').addEventListener('input', function() {
|
||||
document.getElementById('speedValue').textContent = this.value;
|
||||
});
|
||||
|
||||
// Schieberegler für Fehlerrate
|
||||
document.getElementById('targetErrorRate').addEventListener('input', function() {
|
||||
document.getElementById('errorRateValue').textContent = this.value;
|
||||
});
|
||||
|
||||
// Einstellungen speichern
|
||||
document.getElementById('saveSettings').addEventListener('click', function() {
|
||||
const metronomeMode = document.querySelector('input[name="metronomeMode"]:checked').value;
|
||||
const metronomeSpeed = document.getElementById('metronomeSpeed').value;
|
||||
const targetErrorRate = document.getElementById('targetErrorRate').value;
|
||||
|
||||
fetch('/save_settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
metronome_mode: metronomeMode,
|
||||
metronome_speed: parseInt(metronomeSpeed),
|
||||
target_error_rate: parseInt(targetErrorRate)
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
alert('Einstellungen wurden gespeichert!');
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
alert('Fehler beim Speichern der Einstellungen!');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fehler:', error);
|
||||
alert('Fehler beim Speichern der Einstellungen!');
|
||||
});
|
||||
});
|
||||
|
||||
// Theme initialisieren
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
lucide.createIcons();
|
||||
initializeTheme();
|
||||
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue