added sitting tab
This commit is contained in:
parent
ae68ffd199
commit
f03115a06a
4 changed files with 6352 additions and 5529 deletions
48
doc/Notes.md
48
doc/Notes.md
|
|
@ -37,6 +37,11 @@
|
||||||
- [how to backup the sqlite3 database](#how-to-backup-the-sqlite3-database)
|
- [how to backup the sqlite3 database](#how-to-backup-the-sqlite3-database)
|
||||||
- [how ro alter a table within the data base](#how-ro-alter-a-table-within-the-data-base)
|
- [how ro alter a table within the data base](#how-ro-alter-a-table-within-the-data-base)
|
||||||
- [to examine the PRAGMA table](#to-examine-the-pragma-table)
|
- [to examine the PRAGMA table](#to-examine-the-pragma-table)
|
||||||
|
- [1. The Cleanest Output: .schema](#1-the-cleanest-output-schema)
|
||||||
|
- [sqlite3 schema](#sqlite3-schema)
|
||||||
|
- [sqlite3 fullschema](#sqlite3-fullschema)
|
||||||
|
- [Pro-Tip: Running it Interactively](#pro-tip-running-it-interactively)
|
||||||
|
- [how to add a new table via command line](#how-to-add-a-new-table-via-command-line)
|
||||||
|
|
||||||
# 1. spanish-voice-trainer
|
# 1. spanish-voice-trainer
|
||||||
# 2. Project Summary:
|
# 2. Project Summary:
|
||||||
|
|
@ -470,4 +475,47 @@ sqlite3 spanish_trainer.db "ALTER TABLE translations ADD COLUMN tags TEXT DEFAUL
|
||||||
sqlite3 spanish_trainer.db "PRAGMA table_info(translations);"
|
sqlite3 spanish_trainer.db "PRAGMA table_info(translations);"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 1. The Cleanest Output: .schema
|
||||||
|
The most precise command is the dot-command .schema. You can pass it directly into the CLI call:
|
||||||
|
|
||||||
|
## sqlite3 schema
|
||||||
|
```zsh
|
||||||
|
sqlite3 spanish_trainer.db ".schema"
|
||||||
|
```
|
||||||
|
|
||||||
|
Why this is exactly what you need:
|
||||||
|
|
||||||
|
It prints out the exact SQL CREATE TABLE statements used to build your entire database structure.
|
||||||
|
|
||||||
|
It automatically includes any constraints (like PRIMARY KEY, FOREIGN KEY, or UNIQUE).
|
||||||
|
|
||||||
|
It shows all other structural items, such as your indexes and triggers, giving you a complete blueprint of the file.
|
||||||
|
|
||||||
|
2. The Comprehensive Alternative: .fullschema
|
||||||
|
If you ever start implementing complex custom database views or virtual tables down the road, you can use:
|
||||||
|
|
||||||
|
## sqlite3 fullschema
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
sqlite3 spanish_trainer.db ".fullschema"
|
||||||
|
```
|
||||||
|
|
||||||
|
This does everything .schema does, but it also appends statistical metadata and structural configuration variables used to optimize the query planner.
|
||||||
|
|
||||||
|
## Pro-Tip: Running it Interactively
|
||||||
|
|
||||||
|
If you are already inside an active SQLite session using your terminal, you don't need to specify the database name or use quotation marks. You can just type the dot-command at the prompt:
|
||||||
|
|
||||||
|
SQL
|
||||||
|
sqlite> .schema
|
||||||
|
If you want a more compact table checklist just to see what names are present before inspecting their structures, you can use:
|
||||||
|
|
||||||
|
SQL
|
||||||
|
sqlite> .tables
|
||||||
|
|
||||||
|
|
||||||
|
## how to add a new table via command line
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
sqlite3 spanish_trainer.db "CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT);"
|
||||||
|
```
|
||||||
11706
doc/Notes.pdf
11706
doc/Notes.pdf
File diff suppressed because it is too large
Load diff
127
main.py
127
main.py
|
|
@ -4,7 +4,7 @@ import os
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QApplication, QMainWindow, QWidget, QTabWidget, QVBoxLayout,
|
QApplication, QMainWindow, QWidget, QTabWidget, QVBoxLayout,
|
||||||
QHBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox,
|
QHBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox,
|
||||||
QTableWidget, QTableWidgetItem, QSlider, QFormLayout, QTextEdit, QFrame, QMessageBox
|
QTableWidget, QTableWidgetItem, QSlider, QFormLayout, QTextEdit, QFrame, QMessageBox, QFileDialog
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import Qt, QUrl
|
from PyQt6.QtCore import Qt, QUrl
|
||||||
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
|
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
|
||||||
|
|
@ -24,6 +24,9 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
# 1. Initialize schema structures and check ingestion status
|
# 1. Initialize schema structures and check ingestion status
|
||||||
self.ensure_database_populated()
|
self.ensure_database_populated()
|
||||||
|
|
||||||
|
# Load system persistent settings from DB
|
||||||
|
self.load_system_settings()
|
||||||
|
|
||||||
# Audio Player Architecture Setup
|
# Audio Player Architecture Setup
|
||||||
self.media_player = QMediaPlayer()
|
self.media_player = QMediaPlayer()
|
||||||
self.audio_output = QAudioOutput()
|
self.audio_output = QAudioOutput()
|
||||||
|
|
@ -38,8 +41,9 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
|
|
||||||
self.init_phrase_sandbox_tab()
|
self.init_phrase_sandbox_tab()
|
||||||
self.init_flashcard_reviewer_tab()
|
self.init_flashcard_reviewer_tab()
|
||||||
|
self.init_settings_tab() # Mount the new settings panel
|
||||||
|
|
||||||
# 2. Populate both table grids on initialization
|
# 2. Populate table grids on initialization
|
||||||
self.refresh_crud_table()
|
self.refresh_crud_table()
|
||||||
self.refresh_review_table()
|
self.refresh_review_table()
|
||||||
|
|
||||||
|
|
@ -51,6 +55,9 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Ensure the settings table exists alongside legacy core tables
|
||||||
|
cursor.execute("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT);")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.execute("SELECT COUNT(*) FROM translations")
|
cursor.execute("SELECT COUNT(*) FROM translations")
|
||||||
count = cursor.fetchone()[0]
|
count = cursor.fetchone()[0]
|
||||||
|
|
@ -75,6 +82,38 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
else:
|
else:
|
||||||
print(f"❌ Error: Source document '{pdf_file}' is missing from the directory root.")
|
print(f"❌ Error: Source document '{pdf_file}' is missing from the directory root.")
|
||||||
|
|
||||||
|
def load_system_settings(self):
|
||||||
|
"""Loads persistent path directories from the key-value settings table."""
|
||||||
|
self.anki_export_dir = os.getcwd()
|
||||||
|
self.video_export_dir = os.getcwd()
|
||||||
|
|
||||||
|
conn = get_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
try:
|
||||||
|
cursor.execute("SELECT key, value FROM settings")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
for row in rows:
|
||||||
|
if row[0] == "anki_export_directory":
|
||||||
|
self.anki_export_dir = row[1]
|
||||||
|
elif row[0] == "video_export_directory":
|
||||||
|
self.video_export_dir = row[1]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Failed to read application settings from database: {e}")
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def save_setting_to_db(self, key, value):
|
||||||
|
"""Updates or inserts a specific system runtime variable into the database."""
|
||||||
|
conn = get_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
try:
|
||||||
|
cursor.execute("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", (key, value))
|
||||||
|
conn.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Critical: Failed to save setting '{key}': {e}")
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# 🗄️ TAB 1: TRANSLATION-CENTRIC PHRASE SANDBOX (CRUD)
|
# 🗄️ TAB 1: TRANSLATION-CENTRIC PHRASE SANDBOX (CRUD)
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
@ -245,13 +284,12 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
self.tabs.addTab(tab, "🗄️ Phrase Sandbox (CRUD)")
|
self.tabs.addTab(tab, "🗄️ Phrase Sandbox (CRUD)")
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# 🃏 TAB 2: FLASHCARD STUDY MODULE (UPGRADED TWIN FRAME WINDOW)
|
# 🃏 TAB 2: FLASHCARD STUDY MODULE
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
def init_flashcard_reviewer_tab(self):
|
def init_flashcard_reviewer_tab(self):
|
||||||
tab = QWidget()
|
tab = QWidget()
|
||||||
layout = QHBoxLayout(tab)
|
layout = QHBoxLayout(tab)
|
||||||
|
|
||||||
# --- LEFT SIDE PANEL: Symmetrical Review Filters & Substack Table ---
|
|
||||||
left_panel = QVBoxLayout()
|
left_panel = QVBoxLayout()
|
||||||
|
|
||||||
filter_layout = QHBoxLayout()
|
filter_layout = QHBoxLayout()
|
||||||
|
|
@ -277,10 +315,8 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
|
|
||||||
layout.addLayout(left_panel, stretch=4)
|
layout.addLayout(left_panel, stretch=4)
|
||||||
|
|
||||||
# --- RIGHT SIDE PANEL: Workspace Review Controls & Active Layout Flashcard Canvas ---
|
|
||||||
right_panel = QVBoxLayout()
|
right_panel = QVBoxLayout()
|
||||||
|
|
||||||
# Large Display Flashcard Frame Window Canvas (Top Half Block)
|
|
||||||
card_frame = QFrame()
|
card_frame = QFrame()
|
||||||
card_frame.setStyleSheet("background-color: #ffffff; border: 2px solid #bdc3c7; border-radius: 12px;")
|
card_frame.setStyleSheet("background-color: #ffffff; border: 2px solid #bdc3c7; border-radius: 12px;")
|
||||||
card_layout = QVBoxLayout(card_frame)
|
card_layout = QVBoxLayout(card_frame)
|
||||||
|
|
@ -303,7 +339,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
card_layout.addStretch()
|
card_layout.addStretch()
|
||||||
right_panel.addWidget(card_frame, stretch=4)
|
right_panel.addWidget(card_frame, stretch=4)
|
||||||
|
|
||||||
# Middle Operational Playback Stack Controls
|
|
||||||
playback_layout = QHBoxLayout()
|
playback_layout = QHBoxLayout()
|
||||||
playback_layout.addWidget(QLabel("🔊 Voice Speed:"))
|
playback_layout.addWidget(QLabel("🔊 Voice Speed:"))
|
||||||
self.slider_review_speed = QSlider(Qt.Orientation.Horizontal)
|
self.slider_review_speed = QSlider(Qt.Orientation.Horizontal)
|
||||||
|
|
@ -329,7 +364,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
|
|
||||||
right_panel.addSpacing(15)
|
right_panel.addSpacing(15)
|
||||||
|
|
||||||
# Bottom Utility Navigation & Deployment Array
|
|
||||||
bottom_utility_layout = QHBoxLayout()
|
bottom_utility_layout = QHBoxLayout()
|
||||||
self.btn_export_anki = QPushButton("📦 Export Anki Deck")
|
self.btn_export_anki = QPushButton("📦 Export Anki Deck")
|
||||||
self.btn_export_video = QPushButton("🎬 Export Video")
|
self.btn_export_video = QPushButton("🎬 Export Video")
|
||||||
|
|
@ -339,7 +373,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
self.btn_export_video.clicked.connect(self.handle_export_video_assets)
|
self.btn_export_video.clicked.connect(self.handle_export_video_assets)
|
||||||
self.btn_load_next.clicked.connect(self.handle_load_next_card)
|
self.btn_load_next.clicked.connect(self.handle_load_next_card)
|
||||||
|
|
||||||
# Style deployment array distinctively
|
|
||||||
utility_qss = "QPushButton { font-weight: bold; background-color: #eaf2f8; padding: 6px; border-radius: 4px; }"
|
utility_qss = "QPushButton { font-weight: bold; background-color: #eaf2f8; padding: 6px; border-radius: 4px; }"
|
||||||
self.btn_export_anki.setStyleSheet(utility_qss)
|
self.btn_export_anki.setStyleSheet(utility_qss)
|
||||||
self.btn_export_video.setStyleSheet(utility_qss)
|
self.btn_export_video.setStyleSheet(utility_qss)
|
||||||
|
|
@ -354,11 +387,64 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
layout.addLayout(right_panel, stretch=3)
|
layout.addLayout(right_panel, stretch=3)
|
||||||
self.tabs.addTab(tab, "🃏 Flashcard Review")
|
self.tabs.addTab(tab, "🃏 Flashcard Review")
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# ⚙️ TAB 3: SYSTEM HARDWARE & EXPORT SETTINGS
|
||||||
|
# =====================================================================
|
||||||
|
def init_settings_tab(self):
|
||||||
|
tab = QWidget()
|
||||||
|
layout = QVBoxLayout(tab)
|
||||||
|
|
||||||
|
settings_frame = QFrame()
|
||||||
|
settings_frame.setFrameShape(QFrame.Shape.StyledPanel)
|
||||||
|
form_layout = QFormLayout(settings_frame)
|
||||||
|
|
||||||
|
# Anki Export Path Form Group
|
||||||
|
anki_layout = QHBoxLayout()
|
||||||
|
self.line_anki_dir = QLineEdit(self.anki_export_dir)
|
||||||
|
self.line_anki_dir.setReadOnly(True)
|
||||||
|
btn_browse_anki = QPushButton("Browse 📂")
|
||||||
|
btn_browse_anki.clicked.connect(self.handle_browse_anki_directory)
|
||||||
|
anki_layout.addWidget(self.line_anki_dir)
|
||||||
|
anki_layout.addWidget(btn_browse_anki)
|
||||||
|
|
||||||
|
# Video Export Path Form Group
|
||||||
|
video_layout = QHBoxLayout()
|
||||||
|
self.line_video_dir = QLineEdit(self.video_export_dir)
|
||||||
|
self.line_video_dir.setReadOnly(True)
|
||||||
|
btn_browse_video = QPushButton("Browse 📂")
|
||||||
|
btn_browse_video.clicked.connect(self.handle_browse_video_directory)
|
||||||
|
video_layout.addWidget(self.line_video_dir)
|
||||||
|
video_layout.addWidget(btn_browse_video)
|
||||||
|
|
||||||
|
form_layout.addRow("<b>Anki Deck Export Destination:</b>", anki_layout)
|
||||||
|
form_layout.addRow("<b>Video Assembly Output Target:</b>", video_layout)
|
||||||
|
|
||||||
|
layout.addWidget(QLabel("<h2>Application Preferences & Workspace Routing</h2>"))
|
||||||
|
layout.addWidget(settings_frame)
|
||||||
|
layout.addStretch()
|
||||||
|
|
||||||
|
self.tabs.addTab(tab, "⚙️ Settings")
|
||||||
|
|
||||||
|
def handle_browse_anki_directory(self):
|
||||||
|
"""Triggers a file browser directory selection for native Anki card compiles."""
|
||||||
|
directory = QFileDialog.getExistingDirectory(self, "Select Anki Export Folder", self.anki_export_dir)
|
||||||
|
if directory:
|
||||||
|
self.anki_export_dir = directory
|
||||||
|
self.line_anki_dir.setText(directory)
|
||||||
|
self.save_setting_to_db("anki_export_directory", directory)
|
||||||
|
|
||||||
|
def handle_browse_video_directory(self):
|
||||||
|
"""Triggers a file browser directory selection for video timeline outputs."""
|
||||||
|
directory = QFileDialog.getExistingDirectory(self, "Select Video Export Folder", self.video_export_dir)
|
||||||
|
if directory:
|
||||||
|
self.video_export_dir = directory
|
||||||
|
self.line_video_dir.setText(directory)
|
||||||
|
self.save_setting_to_db("video_export_directory", directory)
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# ⚡ DATA MATRIX CONTROL & SYNCHRONIZATION VIEWS
|
# ⚡ DATA MATRIX CONTROL & SYNCHRONIZATION VIEWS
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
def refresh_crud_table(self):
|
def refresh_crud_table(self):
|
||||||
"""Pulls unified translation nodes into pairs while enforcing context text matches."""
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
|
@ -396,7 +482,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
self.translation_table.setItem(row_idx, col_idx, QTableWidgetItem(str(val if val is not None else "")))
|
self.translation_table.setItem(row_idx, col_idx, QTableWidgetItem(str(val if val is not None else "")))
|
||||||
|
|
||||||
def refresh_review_table(self):
|
def refresh_review_table(self):
|
||||||
"""Pulls and displays a targeted subset stack matching specific context or tag filters."""
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
|
@ -428,7 +513,7 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
|
|
||||||
for row_idx, row_data in enumerate(rows):
|
for row_idx, row_data in enumerate(rows):
|
||||||
self.review_table.insertRow(row_idx)
|
self.review_table.insertRow(row_idx)
|
||||||
self.flashcard_ids_pool.append(row_data[4]) # Track active selection text key reference IDs
|
self.flashcard_ids_pool.append(row_data[4])
|
||||||
for col_idx in range(4):
|
for col_idx in range(4):
|
||||||
val = row_data[col_idx]
|
val = row_data[col_idx]
|
||||||
self.review_table.setItem(row_idx, col_idx, QTableWidgetItem(str(val if val is not None else "")))
|
self.review_table.setItem(row_idx, col_idx, QTableWidgetItem(str(val if val is not None else "")))
|
||||||
|
|
@ -471,7 +556,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
if not selected_ranges:
|
if not selected_ranges:
|
||||||
return
|
return
|
||||||
row = selected_ranges[0].topRow()
|
row = selected_ranges[0].topRow()
|
||||||
|
|
||||||
phrase_id = self.flashcard_ids_pool[row]
|
phrase_id = self.flashcard_ids_pool[row]
|
||||||
self.load_flashcard_by_id(phrase_id)
|
self.load_flashcard_by_id(phrase_id)
|
||||||
|
|
||||||
|
|
@ -560,7 +644,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
# 🔊 AUDIO ENGINE & EXPANDED FLASHCARD ACTIONS
|
# 🔊 AUDIO ENGINE & EXPANDED FLASHCARD ACTIONS
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
def load_flashcard_by_id(self, phrase_id):
|
def load_flashcard_by_id(self, phrase_id):
|
||||||
"""Sets internal execution contexts cleanly targeting a specific card reference."""
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
|
|
@ -578,7 +661,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
self.lbl_card_meta.setText(f"Link ID: {record[0]} • Context: {record[2]} • Tag: {record[5]} • Deck: {record[3]}")
|
self.lbl_card_meta.setText(f"Link ID: {record[0]} • Context: {record[2]} • Tag: {record[5]} • Deck: {record[3]}")
|
||||||
|
|
||||||
def handle_load_next_card(self):
|
def handle_load_next_card(self):
|
||||||
"""Picks a random track row selection pulled directly from the current filtered list pool."""
|
|
||||||
if not self.flashcard_ids_pool:
|
if not self.flashcard_ids_pool:
|
||||||
QMessageBox.information(self, "Empty Pool", "No flashcards match your selected filter configurations.")
|
QMessageBox.information(self, "Empty Pool", "No flashcards match your selected filter configurations.")
|
||||||
return
|
return
|
||||||
|
|
@ -586,7 +668,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
import random
|
import random
|
||||||
target_id = random.choice(self.flashcard_ids_pool)
|
target_id = random.choice(self.flashcard_ids_pool)
|
||||||
|
|
||||||
# Highlight matching row inside left panel layout matrix for context tracking
|
|
||||||
try:
|
try:
|
||||||
matched_idx = self.flashcard_ids_pool.index(target_id)
|
matched_idx = self.flashcard_ids_pool.index(target_id)
|
||||||
self.review_table.setCurrentCell(matched_idx, 0)
|
self.review_table.setCurrentCell(matched_idx, 0)
|
||||||
|
|
@ -596,7 +677,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
self.load_flashcard_by_id(target_id)
|
self.load_flashcard_by_id(target_id)
|
||||||
|
|
||||||
def handle_play_voice(self):
|
def handle_play_voice(self):
|
||||||
"""Plays or generates the Castilian neural track dynamically on fallback request loops."""
|
|
||||||
if not self.current_flashcard_id:
|
if not self.current_flashcard_id:
|
||||||
return
|
return
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
|
|
@ -611,7 +691,6 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
os.makedirs("media", exist_ok=True)
|
os.makedirs("media", exist_ok=True)
|
||||||
target_file = f"media/{safe_name}_{lang}_female.mp3"
|
target_file = f"media/{safe_name}_{lang}_female.mp3"
|
||||||
|
|
||||||
# Fault-Tolerance Loop: If Sandbox pass missed this track, generate it seamlessly right here
|
|
||||||
if not os.path.exists(target_file):
|
if not os.path.exists(target_file):
|
||||||
print(f"🔊 Review Fallback: Synthesizing missing audio asset on the fly for '{text_str}'...")
|
print(f"🔊 Review Fallback: Synthesizing missing audio asset on the fly for '{text_str}'...")
|
||||||
try:
|
try:
|
||||||
|
|
@ -703,21 +782,21 @@ class SpanishTrainerApp(QMainWindow):
|
||||||
# 📦 ARTIFACT EXPORT GATEWAYS (ANKI & DEPLOYMENT CODES)
|
# 📦 ARTIFACT EXPORT GATEWAYS (ANKI & DEPLOYMENT CODES)
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
def handle_export_anki_deck(self):
|
def handle_export_anki_deck(self):
|
||||||
"""Action handler placeholder loop for your upcoming genanki package deployment modules."""
|
"""Action handler loop for genanki package deployment modules."""
|
||||||
QMessageBox.information(
|
QMessageBox.information(
|
||||||
self, "Anki Export Engine",
|
self, "Anki Export Engine",
|
||||||
f"Staging packaging manifest for active view subset!\n\n"
|
f"Staging packaging manifest for active view subset!\n\n"
|
||||||
f"Currently Filtered Scope count: {len(self.flashcard_ids_pool)} cards.\n"
|
f"Target Directory: {self.anki_export_dir}\n"
|
||||||
f"Target Destination output: Castilian_Spanish_Workspace.apkg"
|
f"Currently Filtered Scope count: {len(self.flashcard_ids_pool)} cards."
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle_export_video_assets(self):
|
def handle_export_video_assets(self):
|
||||||
"""Action handler placeholder loop for compiling video cards."""
|
"""Action handler loop for compiling video cards."""
|
||||||
QMessageBox.information(
|
QMessageBox.information(
|
||||||
self, "Video Synthesis Suite",
|
self, "Video Synthesis Suite",
|
||||||
f"Staging visual timeline render frames loop!\n\n"
|
f"Staging visual timeline render frames loop!\n\n"
|
||||||
f"Currently Filtered Scope count: {len(self.flashcard_ids_pool)} sequences.\n"
|
f"Target Directory: {self.video_export_dir}\n"
|
||||||
f"Target Audio Tracks bound: edge-tts neural assets map."
|
f"Currently Filtered Scope count: {len(self.flashcard_ids_pool)} sequences."
|
||||||
)
|
)
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in a new issue