139_spanish-voice-trainer/tabs/sandbox_tab.py

219 lines
No EOL
8.7 KiB
Python

# tabs/sandbox_tab.py
import subprocess
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QLineEdit, QPushButton, QTableWidget, QTableWidgetItem,
QHeaderView, QMessageBox, QFormLayout
)
from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt
import database
class SandboxTab(QWidget):
# Signal emitted whenever data is added, modified, or deleted
data_mutated = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
# Main layout structure
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(30, 20, 30, 20)
main_layout.setSpacing(15)
# --- SECTION 1: FORM INPUT CRADLE ---
form_container = QWidget()
form_layout = QFormLayout(form_container)
form_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight)
form_layout.setSpacing(10)
# Input Form Fields
self.txt_english = QLineEdit()
self.txt_english.setPlaceholderText("Enter English phrase or word...")
self.txt_english.setStyleSheet("padding: 6px; font-size: 14px;")
self.txt_spanish = QLineEdit()
self.txt_spanish.setPlaceholderText("Introduce la frase en español...")
self.txt_spanish.setStyleSheet("padding: 6px; font-size: 14px;")
self.txt_context = QLineEdit()
self.txt_context.setPlaceholderText("e.g., Camino 2027, Café, Market conversation...")
self.txt_context.setStyleSheet("padding: 6px; font-size: 14px;")
self.txt_notes = QLineEdit()
self.txt_notes.setPlaceholderText("Grammar rules, formal vs informal nuances...")
self.txt_notes.setStyleSheet("padding: 6px; font-size: 14px;")
# Mount fields onto Form Layout
form_layout.addRow(QLabel("<b>English Text:</b>"), self.txt_english)
form_layout.addRow(QLabel("<b>Spanish Text:</b>"), self.txt_spanish)
form_layout.addRow(QLabel("<b>Source Context:</b>"), self.txt_context)
form_layout.addRow(QLabel("<b>Historical Notes:</b>"), self.txt_notes)
main_layout.addWidget(form_container)
# --- SECTION 2: AUDIO PREVIEW ACTION ROW ---
audio_layout = QHBoxLayout()
audio_layout.setSpacing(15)
self.btn_play_en = QPushButton("🔊 Test English Voice")
self.btn_play_en.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_play_en.setStyleSheet("""
QPushButton {
background-color: #E67E22;
color: white;
font-weight: bold;
font-size: 13px;
padding: 8px 16px;
border-radius: 4px;
}
QPushButton:hover { background-color: #D35400; }
""")
self.btn_play_en.clicked.connect(self.preview_english_audio)
self.btn_play_es = QPushButton("🔊 Test Mónica (Spanish)")
self.btn_play_es.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_play_es.setStyleSheet("""
QPushButton {
background-color: #9B59B6;
color: white;
font-weight: bold;
font-size: 13px;
padding: 8px 16px;
border-radius: 4px;
}
QPushButton:hover { background-color: #8E44AD; }
""")
self.btn_play_es.clicked.connect(self.preview_spanish_audio)
audio_layout.addWidget(self.btn_play_en)
audio_layout.addWidget(self.btn_play_es)
audio_layout.addStretch()
main_layout.addLayout(audio_layout)
# --- SECTION 3: DATA COMMIT CONTROL BAR ---
control_layout = QHBoxLayout()
self.btn_save = QPushButton("Save Translation Record")
self.btn_save.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_save.setStyleSheet("""
QPushButton {
background-color: #2980B9;
color: white;
font-weight: bold;
font-size: 14px;
padding: 10px 20px;
border-radius: 5px;
}
QPushButton:hover { background-color: #1F618D; }
""")
self.btn_save.clicked.connect(self.commit_translation_record)
self.btn_clear = QPushButton("Clear Fields")
self.btn_clear.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_clear.setStyleSheet("""
QPushButton {
background-color: #BDC3C7;
color: #34495E;
font-weight: bold;
font-size: 14px;
padding: 10px 20px;
border-radius: 5px;
}
QPushButton:hover { background-color: #95A5A6; }
""")
self.btn_clear.clicked.connect(self.clear_input_fields)
control_layout.addWidget(self.btn_save)
control_layout.addWidget(self.btn_clear)
control_layout.addStretch()
main_layout.addLayout(control_layout)
# --- SECTION 4: DATALIST DISPLAY REGION ---
self.table = QTableWidget()
self.table.setColumnCount(5)
self.table.setHorizontalHeaderLabels(["ID", "English Phrase", "Spanish Translation", "Context", "Notes"])
self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
# Tweak display headers to scale nicely
header = self.table.horizontalHeader()
header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(3, QHeaderView.ResizeMode.Interactive)
header.setSectionResizeMode(4, QHeaderView.ResizeMode.Interactive)
main_layout.addWidget(self.table)
# Populate live view from storage layout tracking engines
self.reload_table_display()
@pyqtSlot()
def preview_english_audio(self):
"""Auditions current text state inside the English text box field."""
text = self.txt_english.text().strip()
if text:
subprocess.Popen(["say", text])
@pyqtSlot()
def preview_spanish_audio(self):
"""Auditions current text state inside the Spanish text box field using Mónica."""
text = self.txt_spanish.text().strip()
if text:
subprocess.Popen(["say", "-v", "Monica", text])
@pyqtSlot()
def commit_translation_record(self):
"""Extracts text metrics out of input wrappers and saves down to database engine storage."""
en_text = self.txt_english.text().strip()
es_text = self.txt_spanish.text().strip()
context = self.txt_context.text().strip()
notes = self.txt_notes.text().strip()
if not en_text or not es_text:
QMessageBox.warning(self, "Validation Alert", "Both English and Spanish base text blocks are required.")
return
# Call explicit writing handlers down to SQLite database layer
database.insert_translation_explicit(en_text, es_text, context, notes)
# Wipe structural field items cleanly on completions loop
self.clear_input_fields()
# Sync state out across the rest of the app window elements
self.reload_table_display()
self.data_mutated.emit()
@pyqtSlot()
def clear_input_fields(self):
"""Flushes transient cache items out of form line elements."""
self.txt_english.clear()
self.txt_spanish.clear()
self.txt_context.clear()
self.txt_notes.clear()
def reload_table_display(self):
"""Refetches database rows and populates the master dashboard grid view."""
self.table.setRowCount(0)
records = database.get_all_translations_explicit()
for idx, row in enumerate(records):
self.table.insertRow(idx)
# Form clean mapping cell entities
item_id = QTableWidgetItem(str(row["translation_id"]))
item_en = QTableWidgetItem(row["en_text"])
item_es = QTableWidgetItem(row["es_text"])
item_ctx = QTableWidgetItem(row["source_context"] or "")
item_nts = QTableWidgetItem(row["notes"] or "")
# Align center the ID key indices
item_id.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
self.table.setItem(idx, 0, item_id)
self.table.setItem(idx, 1, item_en)
self.table.setItem(idx, 2, item_es)
self.table.setItem(idx, 3, item_ctx)
self.table.setItem(idx, 4, item_nts)