Næste version
This commit is contained in:
226
linedance-app/ui/dance_info_dialog.py
Normal file
226
linedance-app/ui/dance_info_dialog.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""
|
||||
dance_info_dialog.py — Rediger info om en dans: koreograf, video, step sheet, noter.
|
||||
"""
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
||||
QPushButton, QTextEdit, QComboBox, QFrame, QMessageBox,
|
||||
QTabWidget, QWidget,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QUrl
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
|
||||
|
||||
class DanceInfoDialog(QDialog):
|
||||
"""Vis og rediger info om danse tilknyttet en sang."""
|
||||
|
||||
def __init__(self, song: dict, parent=None):
|
||||
super().__init__(parent)
|
||||
self._song = song
|
||||
self._dances = [] # [{dance_id, name, level_name, ...}]
|
||||
self._current_idx = 0
|
||||
|
||||
self.setWindowTitle(f"Dans-info — {song.get('title', '')}")
|
||||
self.setMinimumSize(560, 420)
|
||||
self.resize(620, 460)
|
||||
|
||||
self._load_dances()
|
||||
self._build_ui()
|
||||
if self._dances:
|
||||
self._show_dance(0)
|
||||
|
||||
def _load_dances(self):
|
||||
try:
|
||||
from local.local_db import get_dances_for_song, get_alt_dances_for_song, new_conn
|
||||
conn = new_conn()
|
||||
|
||||
rows = conn.execute("""
|
||||
SELECT d.id, d.name, d.level_id, d.choreographer,
|
||||
d.video_url, d.stepsheet_url, d.notes,
|
||||
dl.name as level_name
|
||||
FROM song_dances sd
|
||||
JOIN dances d ON d.id = sd.dance_id
|
||||
LEFT JOIN dance_levels dl ON dl.id = d.level_id
|
||||
WHERE sd.song_id=? ORDER BY sd.dance_order
|
||||
""", (self._song.get("id"),)).fetchall()
|
||||
|
||||
for row in rows:
|
||||
self._dances.append({
|
||||
"dance_id": row["id"],
|
||||
"name": row["name"],
|
||||
"level_name": row["level_name"] or "",
|
||||
"choreographer": row["choreographer"] or "",
|
||||
"video_url": row["video_url"] or "",
|
||||
"stepsheet_url": row["stepsheet_url"] or "",
|
||||
"notes": row["notes"] or "",
|
||||
"is_alt": False,
|
||||
})
|
||||
|
||||
# Alternativ-danse
|
||||
alt_rows = conn.execute("""
|
||||
SELECT d.id, d.name, d.level_id, d.choreographer,
|
||||
d.video_url, d.stepsheet_url, d.notes,
|
||||
dl.name as level_name
|
||||
FROM song_alt_dances sad
|
||||
JOIN dances d ON d.id = sad.dance_id
|
||||
LEFT JOIN dance_levels dl ON dl.id = d.level_id
|
||||
WHERE sad.song_id=? ORDER BY d.name
|
||||
""", (self._song.get("id"),)).fetchall()
|
||||
|
||||
for row in alt_rows:
|
||||
self._dances.append({
|
||||
"dance_id": row["id"],
|
||||
"name": row["name"],
|
||||
"level_name": row["level_name"] or "",
|
||||
"choreographer": row["choreographer"] or "",
|
||||
"video_url": row["video_url"] or "",
|
||||
"stepsheet_url": row["stepsheet_url"] or "",
|
||||
"notes": row["notes"] or "",
|
||||
"is_alt": True,
|
||||
})
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"DanceInfoDialog load fejl: {e}")
|
||||
|
||||
def _build_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(12, 12, 12, 12)
|
||||
layout.setSpacing(8)
|
||||
|
||||
# Sang-info
|
||||
info = QFrame()
|
||||
info.setObjectName("track_display")
|
||||
il = QHBoxLayout(info)
|
||||
il.setContentsMargins(10, 8, 10, 8)
|
||||
lbl = QLabel(self._song.get("title", "—"))
|
||||
lbl.setObjectName("track_title")
|
||||
il.addWidget(lbl, stretch=1)
|
||||
layout.addWidget(info)
|
||||
|
||||
if not self._dances:
|
||||
layout.addWidget(QLabel("Ingen danse tagget på denne sang."))
|
||||
btn_close = QPushButton("Luk")
|
||||
btn_close.clicked.connect(self.reject)
|
||||
layout.addWidget(btn_close)
|
||||
return
|
||||
|
||||
# Dans-vælger
|
||||
top = QHBoxLayout()
|
||||
top.addWidget(QLabel("Dans:"))
|
||||
self._dance_combo = QComboBox()
|
||||
for d in self._dances:
|
||||
prefix = "↪ " if d["is_alt"] else ""
|
||||
lvl = f" / {d['level_name']}" if d["level_name"] else ""
|
||||
self._dance_combo.addItem(f"{prefix}{d['name']}{lvl}")
|
||||
self._dance_combo.currentIndexChanged.connect(self._on_dance_changed)
|
||||
top.addWidget(self._dance_combo, stretch=1)
|
||||
layout.addLayout(top)
|
||||
|
||||
# Formular
|
||||
form_frame = QFrame()
|
||||
form_frame.setObjectName("track_display")
|
||||
form = QVBoxLayout(form_frame)
|
||||
form.setContentsMargins(12, 10, 12, 10)
|
||||
form.setSpacing(8)
|
||||
|
||||
# Koreograf
|
||||
row1 = QHBoxLayout()
|
||||
row1.addWidget(QLabel("Koreograf:"))
|
||||
self._choreo = QLineEdit()
|
||||
self._choreo.setPlaceholderText("Koreografens navn...")
|
||||
row1.addWidget(self._choreo)
|
||||
form.addLayout(row1)
|
||||
|
||||
# Step sheet URL
|
||||
row2 = QHBoxLayout()
|
||||
row2.addWidget(QLabel("Step sheet:"))
|
||||
self._stepsheet = QLineEdit()
|
||||
self._stepsheet.setPlaceholderText("https://www.copperknob.co.uk/...")
|
||||
row2.addWidget(self._stepsheet)
|
||||
btn_ss = QPushButton("↗")
|
||||
btn_ss.setFixedWidth(28)
|
||||
btn_ss.setToolTip("Åbn i browser")
|
||||
btn_ss.clicked.connect(lambda: self._open_url(self._stepsheet.text()))
|
||||
row2.addWidget(btn_ss)
|
||||
form.addLayout(row2)
|
||||
|
||||
# Video URL
|
||||
row3 = QHBoxLayout()
|
||||
row3.addWidget(QLabel("Video:"))
|
||||
self._video = QLineEdit()
|
||||
self._video.setPlaceholderText("https://www.youtube.com/...")
|
||||
row3.addWidget(self._video)
|
||||
btn_v = QPushButton("↗")
|
||||
btn_v.setFixedWidth(28)
|
||||
btn_v.setToolTip("Åbn i browser")
|
||||
btn_v.clicked.connect(lambda: self._open_url(self._video.text()))
|
||||
row3.addWidget(btn_v)
|
||||
form.addLayout(row3)
|
||||
|
||||
# Noter
|
||||
form.addWidget(QLabel("Noter:"))
|
||||
self._notes = QTextEdit()
|
||||
self._notes.setPlaceholderText("Egne noter om dansen...")
|
||||
self._notes.setMaximumHeight(80)
|
||||
form.addWidget(self._notes)
|
||||
|
||||
layout.addWidget(form_frame, stretch=1)
|
||||
|
||||
# Knapper
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.addStretch()
|
||||
btn_cancel = QPushButton("Luk")
|
||||
btn_cancel.clicked.connect(self.reject)
|
||||
btn_row.addWidget(btn_cancel)
|
||||
btn_save = QPushButton("💾 Gem")
|
||||
btn_save.setObjectName("btn_play")
|
||||
btn_save.clicked.connect(self._save)
|
||||
btn_row.addWidget(btn_save)
|
||||
layout.addLayout(btn_row)
|
||||
|
||||
def _on_dance_changed(self, idx: int):
|
||||
self._save_to_cache(self._current_idx)
|
||||
self._current_idx = idx
|
||||
self._show_dance(idx)
|
||||
|
||||
def _show_dance(self, idx: int):
|
||||
if not 0 <= idx < len(self._dances):
|
||||
return
|
||||
d = self._dances[idx]
|
||||
self._choreo.setText(d["choreographer"])
|
||||
self._stepsheet.setText(d["stepsheet_url"])
|
||||
self._video.setText(d["video_url"])
|
||||
self._notes.setPlainText(d["notes"])
|
||||
|
||||
def _save_to_cache(self, idx: int):
|
||||
"""Gem UI-værdier til cache så de ikke mistes ved dans-skift."""
|
||||
if not 0 <= idx < len(self._dances):
|
||||
return
|
||||
self._dances[idx]["choreographer"] = self._choreo.text().strip()
|
||||
self._dances[idx]["stepsheet_url"] = self._stepsheet.text().strip()
|
||||
self._dances[idx]["video_url"] = self._video.text().strip()
|
||||
self._dances[idx]["notes"] = self._notes.toPlainText().strip()
|
||||
|
||||
def _save(self):
|
||||
self._save_to_cache(self._current_idx)
|
||||
try:
|
||||
from local.local_db import update_dance_info
|
||||
for d in self._dances:
|
||||
update_dance_info(
|
||||
d["dance_id"],
|
||||
choreographer = d["choreographer"],
|
||||
video_url = d["video_url"],
|
||||
stepsheet_url = d["stepsheet_url"],
|
||||
notes = d["notes"],
|
||||
)
|
||||
self.accept()
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Fejl", f"Kunne ikke gemme: {e}")
|
||||
|
||||
def _open_url(self, url: str):
|
||||
url = url.strip()
|
||||
if not url:
|
||||
return
|
||||
if not url.startswith("http"):
|
||||
url = "https://" + url
|
||||
QDesktopServices.openUrl(QUrl(url))
|
||||
Reference in New Issue
Block a user