gui.panels.slide_navigator

  1from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem
  2from PySide6.QtCore import Signal, Qt
  3from PySide6.QtGui import QColor, QBrush, QFont
  4
  5from gui.theme import c, signal as theme_signal
  6
  7
  8_TIPO_COLOR: dict[str, str] = {
  9    **{t: '#60a5fa' for t in (
 10        'java', 'python', 'javascript', 'html', 'xml',
 11        'jsp', 'css', 'json', 'yaml', 'codigo',
 12    )},
 13    'texto':        '#fb7185',
 14    'bullets':      '#818cf8',
 15    'dos_columnas': '#2dd4bf',
 16    'cita':         '#f9a8d4',
 17    **{t: '#4ade80' for t in (
 18        'comparar', 'comparar_python', 'comparar_js',
 19        'comparar_xml', 'comparar_html', 'comparar_json', 'comparar_css', 'comparar_jsp',
 20    )},
 21    'tabla':        '#fb923c',
 22    'aviso':        '#fbbf24',
 23    **{t: '#c084fc' for t in ('terminal', 'salida', 'code')},
 24    'diagrama':     '#22d3ee',
 25    **{t: '#f472b6' for t in ('imagen', 'imagen_texto')},
 26}
 27
 28# Valores especiales de section_idx para los nodos fijos
 29NODE_PORTADA    = -10
 30NODE_OBJETIVOS  = -9
 31NODE_CONCLUSION = -8
 32NODE_REFERENCIAS = -7
 33
 34
 35def _slide_label(slide: dict, n: int) -> str:
 36    tipo = slide.get('tipo', '?')
 37    if tipo == 'aviso':
 38        nivel = slide.get('nivel', '')
 39        return f'aviso: {nivel}  ({n})'
 40    return f'{tipo}  ({n})'
 41
 42
 43class SlideNavigator(QTreeWidget):
 44    """Árbol semántico del YAML activo.
 45
 46    Emite slide_selected(section_idx, slide_idx) al hacer click:
 47      - section_idx >= 0, slide_idx >= 0   → slide concreto (0 = primer slide de la sección)
 48      - section_idx == NODE_*              → nodo fijo (portada, objetivos…)
 49    """
 50    slide_selected = Signal(int, int)
 51
 52    def __init__(self, parent=None):
 53        super().__init__(parent)
 54        self.setHeaderHidden(True)
 55        self.setAnimated(True)
 56        self.setIndentation(16)
 57        self.setMinimumWidth(160)
 58        self.itemClicked.connect(self._on_item_clicked)
 59        theme_signal().changed.connect(self._on_theme_changed)
 60        self._last_data: dict = {}
 61
 62    # ── API pública ───────────────────────────────────────────────────────────
 63
 64    def _on_theme_changed(self) -> None:
 65        if self._last_data:
 66            self.rebuild(self._last_data)
 67
 68    def rebuild(self, data: dict) -> None:
 69        self._last_data = data
 70        """Reconstruye el árbol desde el dict YAML parseado."""
 71        self.clear()
 72
 73        self._add_fixed('Portada',   NODE_PORTADA,    0)
 74        self._add_fixed('Objetivos', NODE_OBJETIVOS,  0)
 75
 76        indice_list = data.get('indice', [])
 77
 78        for sec_idx, sec in enumerate(data.get('secciones', [])):
 79            if 'titulo' in sec:
 80                titulo = sec['titulo']
 81            elif 'indice' in sec and indice_list:
 82                idx = sec['indice'] - 1
 83                titulo = indice_list[idx] if 0 <= idx < len(indice_list) else f'Sección {sec_idx + 1}'
 84            else:
 85                titulo = f'Sección {sec_idx + 1}'
 86
 87            sec_item = QTreeWidgetItem(self)
 88            sec_item.setText(0, f'Sec {sec_idx + 1}: {titulo}')
 89            sec_item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, 0))
 90            sec_item.setForeground(0, QBrush(QColor(c('text'))))
 91            bold = QFont()
 92            bold.setBold(True)
 93            sec_item.setFont(0, bold)
 94
 95            for slide_idx, slide in enumerate(sec.get('slides', [])):
 96                tipo = slide.get('tipo', '?')
 97                s_item = QTreeWidgetItem(sec_item)
 98                s_item.setText(0, f'  {_slide_label(slide, slide_idx + 1)}')
 99                s_item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, slide_idx))
100                s_item.setForeground(0, QBrush(QColor(_TIPO_COLOR.get(tipo, '#94a3b8'))))
101
102            sec_item.setExpanded(True)
103
104        self._add_fixed('Conclusión',  NODE_CONCLUSION,  0)
105        self._add_fixed('Referencias', NODE_REFERENCIAS, 0)
106
107    # ── Slots privados ────────────────────────────────────────────────────────
108
109    def _add_fixed(self, label: str, sec_idx: int, slide_idx: int) -> QTreeWidgetItem:
110        item = QTreeWidgetItem(self)
111        item.setText(0, label)
112        item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, slide_idx))
113        item.setForeground(0, QBrush(QColor(c('text_dim'))))
114        return item
115
116    def _on_item_clicked(self, item: QTreeWidgetItem, _column: int) -> None:
117        data = item.data(0, Qt.ItemDataRole.UserRole)
118        if data is not None:
119            self.slide_selected.emit(data[0], data[1])
NODE_PORTADA = -10
NODE_OBJETIVOS = -9
NODE_CONCLUSION = -8
NODE_REFERENCIAS = -7
class SlideNavigator(PySide6.QtWidgets.QTreeWidget):
 44class SlideNavigator(QTreeWidget):
 45    """Árbol semántico del YAML activo.
 46
 47    Emite slide_selected(section_idx, slide_idx) al hacer click:
 48      - section_idx >= 0, slide_idx >= 0   → slide concreto (0 = primer slide de la sección)
 49      - section_idx == NODE_*              → nodo fijo (portada, objetivos…)
 50    """
 51    slide_selected = Signal(int, int)
 52
 53    def __init__(self, parent=None):
 54        super().__init__(parent)
 55        self.setHeaderHidden(True)
 56        self.setAnimated(True)
 57        self.setIndentation(16)
 58        self.setMinimumWidth(160)
 59        self.itemClicked.connect(self._on_item_clicked)
 60        theme_signal().changed.connect(self._on_theme_changed)
 61        self._last_data: dict = {}
 62
 63    # ── API pública ───────────────────────────────────────────────────────────
 64
 65    def _on_theme_changed(self) -> None:
 66        if self._last_data:
 67            self.rebuild(self._last_data)
 68
 69    def rebuild(self, data: dict) -> None:
 70        self._last_data = data
 71        """Reconstruye el árbol desde el dict YAML parseado."""
 72        self.clear()
 73
 74        self._add_fixed('Portada',   NODE_PORTADA,    0)
 75        self._add_fixed('Objetivos', NODE_OBJETIVOS,  0)
 76
 77        indice_list = data.get('indice', [])
 78
 79        for sec_idx, sec in enumerate(data.get('secciones', [])):
 80            if 'titulo' in sec:
 81                titulo = sec['titulo']
 82            elif 'indice' in sec and indice_list:
 83                idx = sec['indice'] - 1
 84                titulo = indice_list[idx] if 0 <= idx < len(indice_list) else f'Sección {sec_idx + 1}'
 85            else:
 86                titulo = f'Sección {sec_idx + 1}'
 87
 88            sec_item = QTreeWidgetItem(self)
 89            sec_item.setText(0, f'Sec {sec_idx + 1}: {titulo}')
 90            sec_item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, 0))
 91            sec_item.setForeground(0, QBrush(QColor(c('text'))))
 92            bold = QFont()
 93            bold.setBold(True)
 94            sec_item.setFont(0, bold)
 95
 96            for slide_idx, slide in enumerate(sec.get('slides', [])):
 97                tipo = slide.get('tipo', '?')
 98                s_item = QTreeWidgetItem(sec_item)
 99                s_item.setText(0, f'  {_slide_label(slide, slide_idx + 1)}')
100                s_item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, slide_idx))
101                s_item.setForeground(0, QBrush(QColor(_TIPO_COLOR.get(tipo, '#94a3b8'))))
102
103            sec_item.setExpanded(True)
104
105        self._add_fixed('Conclusión',  NODE_CONCLUSION,  0)
106        self._add_fixed('Referencias', NODE_REFERENCIAS, 0)
107
108    # ── Slots privados ────────────────────────────────────────────────────────
109
110    def _add_fixed(self, label: str, sec_idx: int, slide_idx: int) -> QTreeWidgetItem:
111        item = QTreeWidgetItem(self)
112        item.setText(0, label)
113        item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, slide_idx))
114        item.setForeground(0, QBrush(QColor(c('text_dim'))))
115        return item
116
117    def _on_item_clicked(self, item: QTreeWidgetItem, _column: int) -> None:
118        data = item.data(0, Qt.ItemDataRole.UserRole)
119        if data is not None:
120            self.slide_selected.emit(data[0], data[1])

Árbol semántico del YAML activo.

Emite slide_selected(section_idx, slide_idx) al hacer click:

  • section_idx >= 0, slide_idx >= 0 → slide concreto (0 = primer slide de la sección)
  • section_idx == NODE_* → nodo fijo (portada, objetivos…)
SlideNavigator(parent=None)
53    def __init__(self, parent=None):
54        super().__init__(parent)
55        self.setHeaderHidden(True)
56        self.setAnimated(True)
57        self.setIndentation(16)
58        self.setMinimumWidth(160)
59        self.itemClicked.connect(self._on_item_clicked)
60        theme_signal().changed.connect(self._on_theme_changed)
61        self._last_data: dict = {}
def slide_selected(unknown):
def rebuild(self, data: dict) -> None:
 69    def rebuild(self, data: dict) -> None:
 70        self._last_data = data
 71        """Reconstruye el árbol desde el dict YAML parseado."""
 72        self.clear()
 73
 74        self._add_fixed('Portada',   NODE_PORTADA,    0)
 75        self._add_fixed('Objetivos', NODE_OBJETIVOS,  0)
 76
 77        indice_list = data.get('indice', [])
 78
 79        for sec_idx, sec in enumerate(data.get('secciones', [])):
 80            if 'titulo' in sec:
 81                titulo = sec['titulo']
 82            elif 'indice' in sec and indice_list:
 83                idx = sec['indice'] - 1
 84                titulo = indice_list[idx] if 0 <= idx < len(indice_list) else f'Sección {sec_idx + 1}'
 85            else:
 86                titulo = f'Sección {sec_idx + 1}'
 87
 88            sec_item = QTreeWidgetItem(self)
 89            sec_item.setText(0, f'Sec {sec_idx + 1}: {titulo}')
 90            sec_item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, 0))
 91            sec_item.setForeground(0, QBrush(QColor(c('text'))))
 92            bold = QFont()
 93            bold.setBold(True)
 94            sec_item.setFont(0, bold)
 95
 96            for slide_idx, slide in enumerate(sec.get('slides', [])):
 97                tipo = slide.get('tipo', '?')
 98                s_item = QTreeWidgetItem(sec_item)
 99                s_item.setText(0, f'  {_slide_label(slide, slide_idx + 1)}')
100                s_item.setData(0, Qt.ItemDataRole.UserRole, (sec_idx, slide_idx))
101                s_item.setForeground(0, QBrush(QColor(_TIPO_COLOR.get(tipo, '#94a3b8'))))
102
103            sec_item.setExpanded(True)
104
105        self._add_fixed('Conclusión',  NODE_CONCLUSION,  0)
106        self._add_fixed('Referencias', NODE_REFERENCIAS, 0)
staticMetaObject = PySide6.QtCore.QMetaObject("SlideNavigator" inherits "QTreeWidget": Methods: #103 type=Signal, signature=slide_selected(int,int), parameters=int, int )