# 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("English Text:"), self.txt_english) form_layout.addRow(QLabel("Spanish Text:"), self.txt_spanish) form_layout.addRow(QLabel("Source Context:"), self.txt_context) form_layout.addRow(QLabel("Historical Notes:"), 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)