gui.panels.course_bar
1from pathlib import Path 2 3from PySide6.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton, QTabBar, QMenu 4from PySide6.QtCore import Signal 5 6from gui.utils.path_setup import get_courses, get_yamls 7from gui.theme import signal as theme_signal 8from lib.i18n import _ 9 10 11class CourseBar(QWidget): 12 course_changed = Signal(Path) 13 unit_changed = Signal(Path) 14 new_course_requested = Signal() 15 new_presentation_requested = Signal() 16 17 def __init__(self, parent=None): 18 super().__init__(parent) 19 self.setObjectName('course_bar') 20 self.setFixedHeight(36) 21 self._yamls: list[Path] = [] 22 self._courses: list[Path] = [] 23 self._current_course: Path | None = None 24 self._build_ui() 25 self._load_courses() 26 theme_signal().changed.connect(self._on_theme_changed) 27 28 def _build_ui(self): 29 lay = QHBoxLayout(self) 30 lay.setContentsMargins(8, 4, 8, 4) 31 lay.setSpacing(4) 32 33 lay.addWidget(self._lbl(_('Curso:'), 'course_label')) 34 35 self._course_btn = QPushButton('—') 36 self._course_btn.setObjectName('course_btn') 37 self._course_btn.clicked.connect(self._show_course_menu) 38 lay.addWidget(self._course_btn) 39 40 lay.addWidget(self._icon_btn('+', _('Nuevo curso'), self.new_course_requested.emit)) 41 lay.addWidget(self._icon_btn('⟳', _('Recargar cursos'), self._refresh_courses)) 42 43 lay.addWidget(self._lbl('│', 'sep_label')) 44 45 lay.addWidget(self._icon_btn('⟳', _('Recargar presentaciones'), self._refresh_units)) 46 47 self.unit_tabs = QTabBar() 48 self.unit_tabs.setObjectName('unit_tabs') 49 self.unit_tabs.setExpanding(False) 50 self.unit_tabs.currentChanged.connect(self._on_tab_changed) 51 lay.addWidget(self.unit_tabs) 52 53 lay.addWidget(self._icon_btn('+', _('Nueva presentación'), self.new_presentation_requested.emit)) 54 55 lay.addStretch() 56 57 # ── Helpers de layout ───────────────────────────────────────────────────── 58 59 @staticmethod 60 def _lbl(text: str, name: str) -> QLabel: 61 w = QLabel(text) 62 w.setObjectName(name) 63 return w 64 65 @staticmethod 66 def _icon_btn(text: str, tooltip: str, slot) -> QPushButton: 67 btn = QPushButton(text) 68 btn.setObjectName('bar_icon_btn') 69 btn.setToolTip(tooltip) 70 btn.setFixedSize(22, 22) 71 btn.clicked.connect(slot) 72 return btn 73 74 # ── Acciones internas ───────────────────────────────────────────────────── 75 76 def _on_theme_changed(self) -> None: 77 self.update() 78 79 def _show_course_menu(self): 80 menu = QMenu(self) 81 for course in self._courses: 82 act = menu.addAction(course.name) 83 act.setData(course) 84 if course == self._current_course: 85 act.setCheckable(True) 86 act.setChecked(True) 87 chosen = menu.exec(self._course_btn.mapToGlobal( 88 self._course_btn.rect().bottomLeft() 89 )) 90 if chosen and chosen.data(): 91 self._select_course(self._courses.index(chosen.data())) 92 93 def _refresh_courses(self): 94 old = self._current_course 95 self._courses = get_courses() 96 if not self._courses: 97 self._course_btn.setText('— ▾') 98 self._current_course = None 99 return 100 idx = self._courses.index(old) if old in self._courses else 0 101 self._select_course(idx) 102 103 def _refresh_units(self): 104 if self._current_course: 105 self._reload_tabs(self._current_course) 106 107 # ── Lógica de carga ─────────────────────────────────────────────────────── 108 109 def _load_courses(self): 110 self._courses = get_courses() 111 if self._courses: 112 self._select_course(0) 113 else: 114 self._course_btn.setText('— ▾') 115 116 def _select_course(self, idx: int): 117 course = self._courses[idx] 118 self._current_course = course 119 self._course_btn.setText(f'{course.name} ▾') 120 self.course_changed.emit(course) 121 self._reload_tabs(course) 122 123 def _reload_tabs(self, course_dir: Path): 124 self.unit_tabs.blockSignals(True) 125 while self.unit_tabs.count(): 126 self.unit_tabs.removeTab(0) 127 self._yamls = get_yamls(course_dir) 128 for y in self._yamls: 129 self.unit_tabs.addTab(y.name) 130 self.unit_tabs.blockSignals(False) 131 if self._yamls: 132 self.unit_tabs.setCurrentIndex(0) 133 self.unit_changed.emit(self._yamls[0]) 134 135 def _on_tab_changed(self, idx: int): 136 if 0 <= idx < len(self._yamls): 137 self.unit_changed.emit(self._yamls[idx]) 138 139 # ── API pública ─────────────────────────────────────────────────────────── 140 141 def current_course(self) -> Path | None: 142 return self._current_course 143 144 def current_yaml(self) -> Path | None: 145 idx = self.unit_tabs.currentIndex() 146 if 0 <= idx < len(self._yamls): 147 return self._yamls[idx] 148 return None
12class CourseBar(QWidget): 13 course_changed = Signal(Path) 14 unit_changed = Signal(Path) 15 new_course_requested = Signal() 16 new_presentation_requested = Signal() 17 18 def __init__(self, parent=None): 19 super().__init__(parent) 20 self.setObjectName('course_bar') 21 self.setFixedHeight(36) 22 self._yamls: list[Path] = [] 23 self._courses: list[Path] = [] 24 self._current_course: Path | None = None 25 self._build_ui() 26 self._load_courses() 27 theme_signal().changed.connect(self._on_theme_changed) 28 29 def _build_ui(self): 30 lay = QHBoxLayout(self) 31 lay.setContentsMargins(8, 4, 8, 4) 32 lay.setSpacing(4) 33 34 lay.addWidget(self._lbl(_('Curso:'), 'course_label')) 35 36 self._course_btn = QPushButton('—') 37 self._course_btn.setObjectName('course_btn') 38 self._course_btn.clicked.connect(self._show_course_menu) 39 lay.addWidget(self._course_btn) 40 41 lay.addWidget(self._icon_btn('+', _('Nuevo curso'), self.new_course_requested.emit)) 42 lay.addWidget(self._icon_btn('⟳', _('Recargar cursos'), self._refresh_courses)) 43 44 lay.addWidget(self._lbl('│', 'sep_label')) 45 46 lay.addWidget(self._icon_btn('⟳', _('Recargar presentaciones'), self._refresh_units)) 47 48 self.unit_tabs = QTabBar() 49 self.unit_tabs.setObjectName('unit_tabs') 50 self.unit_tabs.setExpanding(False) 51 self.unit_tabs.currentChanged.connect(self._on_tab_changed) 52 lay.addWidget(self.unit_tabs) 53 54 lay.addWidget(self._icon_btn('+', _('Nueva presentación'), self.new_presentation_requested.emit)) 55 56 lay.addStretch() 57 58 # ── Helpers de layout ───────────────────────────────────────────────────── 59 60 @staticmethod 61 def _lbl(text: str, name: str) -> QLabel: 62 w = QLabel(text) 63 w.setObjectName(name) 64 return w 65 66 @staticmethod 67 def _icon_btn(text: str, tooltip: str, slot) -> QPushButton: 68 btn = QPushButton(text) 69 btn.setObjectName('bar_icon_btn') 70 btn.setToolTip(tooltip) 71 btn.setFixedSize(22, 22) 72 btn.clicked.connect(slot) 73 return btn 74 75 # ── Acciones internas ───────────────────────────────────────────────────── 76 77 def _on_theme_changed(self) -> None: 78 self.update() 79 80 def _show_course_menu(self): 81 menu = QMenu(self) 82 for course in self._courses: 83 act = menu.addAction(course.name) 84 act.setData(course) 85 if course == self._current_course: 86 act.setCheckable(True) 87 act.setChecked(True) 88 chosen = menu.exec(self._course_btn.mapToGlobal( 89 self._course_btn.rect().bottomLeft() 90 )) 91 if chosen and chosen.data(): 92 self._select_course(self._courses.index(chosen.data())) 93 94 def _refresh_courses(self): 95 old = self._current_course 96 self._courses = get_courses() 97 if not self._courses: 98 self._course_btn.setText('— ▾') 99 self._current_course = None 100 return 101 idx = self._courses.index(old) if old in self._courses else 0 102 self._select_course(idx) 103 104 def _refresh_units(self): 105 if self._current_course: 106 self._reload_tabs(self._current_course) 107 108 # ── Lógica de carga ─────────────────────────────────────────────────────── 109 110 def _load_courses(self): 111 self._courses = get_courses() 112 if self._courses: 113 self._select_course(0) 114 else: 115 self._course_btn.setText('— ▾') 116 117 def _select_course(self, idx: int): 118 course = self._courses[idx] 119 self._current_course = course 120 self._course_btn.setText(f'{course.name} ▾') 121 self.course_changed.emit(course) 122 self._reload_tabs(course) 123 124 def _reload_tabs(self, course_dir: Path): 125 self.unit_tabs.blockSignals(True) 126 while self.unit_tabs.count(): 127 self.unit_tabs.removeTab(0) 128 self._yamls = get_yamls(course_dir) 129 for y in self._yamls: 130 self.unit_tabs.addTab(y.name) 131 self.unit_tabs.blockSignals(False) 132 if self._yamls: 133 self.unit_tabs.setCurrentIndex(0) 134 self.unit_changed.emit(self._yamls[0]) 135 136 def _on_tab_changed(self, idx: int): 137 if 0 <= idx < len(self._yamls): 138 self.unit_changed.emit(self._yamls[idx]) 139 140 # ── API pública ─────────────────────────────────────────────────────────── 141 142 def current_course(self) -> Path | None: 143 return self._current_course 144 145 def current_yaml(self) -> Path | None: 146 idx = self.unit_tabs.currentIndex() 147 if 0 <= idx < len(self._yamls): 148 return self._yamls[idx] 149 return None
QWidget(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, modal: bool | None = None, windowModality: PySide6.QtCore.Qt.WindowModality | None = None, enabled: bool | None = None, geometry: PySide6.QtCore.QRect | None = None, frameGeometry: PySide6.QtCore.QRect | None = None, normalGeometry: PySide6.QtCore.QRect | None = None, x: int | None = None, y: int | None = None, pos: PySide6.QtCore.QPoint | None = None, frameSize: PySide6.QtCore.QSize | None = None, size: PySide6.QtCore.QSize | None = None, width: int | None = None, height: int | None = None, rect: PySide6.QtCore.QRect | None = None, childrenRect: PySide6.QtCore.QRect | None = None, childrenRegion: PySide6.QtGui.QRegion | None = None, sizePolicy: PySide6.QtWidgets.QSizePolicy | None = None, minimumSize: PySide6.QtCore.QSize | None = None, maximumSize: PySide6.QtCore.QSize | None = None, minimumWidth: int | None = None, minimumHeight: int | None = None, maximumWidth: int | None = None, maximumHeight: int | None = None, sizeIncrement: PySide6.QtCore.QSize | None = None, baseSize: PySide6.QtCore.QSize | None = None, palette: PySide6.QtGui.QPalette | None = None, font: PySide6.QtGui.QFont | None = None, cursor: PySide6.QtGui.QCursor | None = None, mouseTracking: bool | None = None, tabletTracking: bool | None = None, isActiveWindow: bool | None = None, focusPolicy: PySide6.QtCore.Qt.FocusPolicy | None = None, focus: bool | None = None, contextMenuPolicy: PySide6.QtCore.Qt.ContextMenuPolicy | None = None, updatesEnabled: bool | None = None, visible: bool | None = None, minimized: bool | None = None, maximized: bool | None = None, fullScreen: bool | None = None, sizeHint: PySide6.QtCore.QSize | None = None, minimumSizeHint: PySide6.QtCore.QSize | None = None, acceptDrops: bool | None = None, windowTitle: str | None = None, windowIcon: PySide6.QtGui.QIcon | None = None, windowIconText: str | None = None, windowOpacity: float | None = None, windowModified: bool | None = None, toolTip: str | None = None, toolTipDuration: int | None = None, statusTip: str | None = None, whatsThis: str | None = None, accessibleName: str | None = None, accessibleDescription: str | None = None, accessibleIdentifier: str | None = None, layoutDirection: PySide6.QtCore.Qt.LayoutDirection | None = None, autoFillBackground: bool | None = None, styleSheet: str | None = None, locale: PySide6.QtCore.QLocale | None = None, windowFilePath: str | None = None, inputMethodHints: PySide6.QtCore.Qt.InputMethodHint | None = None) -> None
18 def __init__(self, parent=None): 19 super().__init__(parent) 20 self.setObjectName('course_bar') 21 self.setFixedHeight(36) 22 self._yamls: list[Path] = [] 23 self._courses: list[Path] = [] 24 self._current_course: Path | None = None 25 self._build_ui() 26 self._load_courses() 27 theme_signal().changed.connect(self._on_theme_changed)