Source code for blackboard_sync.qt.SetupWizard

# Copyright (C) 2024, Jacob Sánchez Pérez

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301, USA.

from enum import IntEnum
from pathlib import Path
# from typing import override
from datetime import datetime

from PyQt6.QtCore import pyqtSlot, Qt
from PyQt6.QtWidgets import (QWizard, QWizardPage,
                             QCompleter, QComboBox,
                             QCheckBox, QSpinBox,
                             QLabel, QPushButton)

from .assets import load_ui, get_wizard_pixmap
from .dialogs import DirDialog, Dialogs


[docs] class SetupWizard(QWizard): """Guide user through initial setup process."""
[docs] class Pages(IntEnum): INTRO = 0 INSTITUTION = 1 DOWNLOAD_LOCATION = 2 DOWNLOAD_SINCE = 3 LAST = 3
def __init__(self, support_url: str, institutions: list[str], selected: int | None = None): super().__init__() # Typing information self.uni_selection_page: QWizardPage self.sync_location_page: QWizardPage self.uni_selection_box: QComboBox self.since_all_checkbox: QCheckBox self.date_spinbox: QSpinBox self.autodetect_label: QLabel self.sync_location_button: QPushButton self._institutions = institutions self._support_url = support_url self._has_chosen_location = False self._init_ui(selected) def _init_ui(self, selected: int | None) -> None: load_ui(self) pixmap_roles = [ QWizard.WizardPixmap.BackgroundPixmap ] for role in pixmap_roles: self.setPixmap(role, get_wizard_pixmap(role)) # Institution self.uni_selection_box.addItems(self._institutions) self.uni_selection_box.clearEditText() self.autodetect_label.setVisible(selected is not None) self.completer = QCompleter(self._institutions, self) self.completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) self.completer.setFilterMode(Qt.MatchFlag.MatchContains) self.uni_selection_box.setCompleter(self.completer) # Download location self.sync_location_button.clicked.connect( self._choose_location ) # Minimum year year = datetime.today().year self.date_spinbox.setRange(2000, year) self.date_spinbox.setValue(year) self.date_spinbox.setEnabled(False) self.since_all_checkbox.stateChanged.connect(self._toggle_all_content) # Prepare wizard self.register_fields() # Select autodetected university # Must happen after the fields are registered if selected is not None: self.uni_selection_box.setCurrentIndex(selected) self.dialogs = Dialogs()
[docs] def register_fields(self) -> None: # Make these actions obligatory self.uni_selection_page.registerField( "userInstitution*", self.uni_selection_box.lineEdit() ) self.sync_location_page.registerField( "syncLocation*", self.sync_location_button, property="text", changedSignal=self.sync_location_button.clicked )
def _update_download_location_button(self) -> None: dir = self.download_location.name or str(self.download_location) self.sync_location_button.setText(dir) # @override
[docs] def initializePage(self, id: int) -> None: if id == self.Pages.DOWNLOAD_LOCATION: if self._has_chosen_location: self._update_download_location_button()
# @override
[docs] def validateCurrentPage(self) -> bool: valid = True # Do not progress if institution is not valid if self.currentId() == self.Pages.INSTITUTION: if not self._institution_is_valid(): self.dialogs.uni_not_supported_dialog(self._support_url) valid = False return valid
@pyqtSlot() def _choose_location(self) -> None: location = DirDialog().choose() if location is not None: self.download_location = location @pyqtSlot(int) def _toggle_all_content(self, state: int) -> None: unchecked = Qt.CheckState(state) != Qt.CheckState.Checked self.date_spinbox.setEnabled(unchecked) def _institution_is_valid(self) -> bool: return self.institution == str(self.field("userInstitution")) @property def institution(self) -> str: """Name of selected institution.""" return self.uni_selection_box.itemText(self.institution_index) @property def institution_index(self) -> int: """Index of selected institution.""" return self.uni_selection_box.currentIndex() @property def download_location(self) -> Path: """Download location selected by user.""" return self._download_location @download_location.setter def download_location(self, location: Path) -> None: self._download_location = location.resolve() self._update_download_location_button() self._has_chosen_location = True @property def min_year(self) -> int | None: """Minimum year selected by user.""" if not self.since_all_checkbox.isChecked(): return self.date_spinbox.value() return None