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
class CourseBar(PySide6.QtWidgets.QWidget):
 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

CourseBar(parent=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)
def course_changed(unknown):
def unit_changed(unknown):
def new_course_requested(unknown):
def new_presentation_requested(unknown):
def current_course(self) -> pathlib.Path | None:
142    def current_course(self) -> Path | None:
143        return self._current_course
def current_yaml(self) -> pathlib.Path | None:
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
staticMetaObject = PySide6.QtCore.QMetaObject("CourseBar" inherits "QWidget": Methods: #33 type=Signal, signature=course_changed(PyObject), parameters=PyObject #34 type=Signal, signature=unit_changed(PyObject), parameters=PyObject #35 type=Signal, signature=new_course_requested() #36 type=Signal, signature=new_presentation_requested() )