class JobCodeDialog(QDialog):
    def __init__(self, parent, on_download: bool,
                 job_codes: List[str]) -> None:
        """
        Prompt user to enter a Job Code, either at the time a download starts,
        or to zero or more selected files before the download begins.

        :param parent: rapidApp main window
        :param on_download: if True, dialog is being prompted for before a download
         starts.
        :param job_codes:
        """

        super().__init__(parent)
        self.rapidApp = parent  # type: 'RapidWindow'
        self.prefs = self.rapidApp.prefs  # type: Preferences
        thumbnailModel = self.rapidApp.thumbnailModel

        # Whether the user has opened this dialog before a download starts without
        # having selected any files first
        no_selection_made = None  # type: Optional[bool]

        if on_download:
            directive = _("Enter a new Job Code, or select a previous one")

            file_types = thumbnailModel.getNoFilesJobCodeNeeded()
            details = file_types.file_types_present_details(title_case=False)
            if sum(file_types.values()) == 1:
                # Translators: the value substituted will be something like '1 photo'.
                file_details = (_(
                    "The Job Code will be applied to %s that does not yet have a "
                    "Job Code.") % details)
            else:
                # Translators: the value substituted will be something like
                # '85 photos and 5 videos'.
                file_details = (_(
                    "The Job Code will be applied to %s that do not yet have a "
                    "Job Code.") % details)

            hint = (
                "<b>Hint:</b> To assign Job Codes before the download begins, select "
                "photos or videos and apply a new or existing Job Code to them via the "
                "Job Code panel.")
            file_details = "{}<br><br><i>{}</i>".format(file_details, hint)

            title = _("Apply Job Code to Download")
        else:
            directive = _("Enter a new Job Code")

            file_types = thumbnailModel.getNoFilesSelected()
            no_selection_made = sum(file_types.values()) == 0
            if no_selection_made:
                file_details = ("<i>" + _(
                    "<b>Hint:</b> Select photos or videos before entering a new "
                    "Job Code to have the Job Code applied to them.") + "</i>")

                _("")
            else:
                details = file_types.file_types_present_details(
                    title_case=False)
                # Translators: the value substituted will be something like
                # '100 photos and 5 videos'.
                file_details = (
                    "<i>" +
                    _("The new Job Code will be applied to %s.") % details +
                    "</i>")

            title = _("New Job Code")

        instructionLabel = QLabel("<b>%s</b><br><br>%s<br>" %
                                  (directive, file_details))
        instructionLabel.setWordWrap(True)

        self.jobCodeComboBox = QComboBox()
        self.jobCodeComboBox.addItems(job_codes)
        self.jobCodeComboBox.setEditable(True)

        if not self.prefs.strip_characters:
            exp = "[^/\\0]+"
        else:
            exp = '[^\\:\*\?"<>|\\0/]+'

        self.jobCodeExp = QRegularExpression()
        self.jobCodeExp.setPattern(exp)
        self.jobCodeValidator = QRegularExpressionValidator(
            self.jobCodeExp, self.jobCodeComboBox)
        self.jobCodeComboBox.setValidator(self.jobCodeValidator)

        if not on_download:
            self.jobCodeComboBox.clearEditText()

        if self.prefs.job_code_sort_key == 0:
            if self.prefs.job_code_sort_order == 0:
                self.jobCodeComboBox.setInsertPolicy(QComboBox.InsertAtTop)
            else:
                self.jobCodeComboBox.setInsertPolicy(QComboBox.InsertAtBottom)
        else:
            self.jobCodeComboBox.setInsertPolicy(
                QComboBox.InsertAlphabetically)

        icon = QIcon(":/rapid-photo-downloader.svg").pixmap(standardIconSize())
        iconLabel = QLabel()
        iconLabel.setPixmap(icon)
        iconLabel.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        iconLabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        jobCodeLabel = QLabel(_("&Job Code:"))
        jobCodeLabel.setBuddy(self.jobCodeComboBox)

        if on_download or not no_selection_made:
            self.rememberCheckBox = QCheckBox(_("&Remember this Job Code"))
            self.rememberCheckBox.setChecked(parent.prefs.remember_job_code)

        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Cancel)
        translateDialogBoxButtons(buttonBox)

        grid = QGridLayout()
        grid.addWidget(iconLabel, 0, 0, 4, 1)
        grid.addWidget(instructionLabel, 0, 1, 1, 2)
        grid.addWidget(jobCodeLabel, 1, 1)
        grid.addWidget(self.jobCodeComboBox, 1, 2)

        if hasattr(self, "rememberCheckBox"):
            grid.addWidget(self.rememberCheckBox, 2, 1, 1, 2)
            grid.addWidget(buttonBox, 3, 0, 1, 3)
        else:
            grid.addWidget(buttonBox, 2, 0, 1, 3)

        grid.setColumnStretch(2, 1)
        self.setLayout(grid)
        self.setWindowTitle(title)

        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

    @pyqtSlot()
    def accept(self) -> None:
        self.job_code = self.jobCodeComboBox.currentText()
        if hasattr(self, "rememberCheckBox"):
            self.remember = self.rememberCheckBox.isChecked()
            self.rapidApp.prefs.remember_job_code = self.remember
        else:
            self.remember = True
        super().accept()
Example #2
0
    def home(self):
        # self.configs = self.load_configs()
        def get_vals():
            update_config()
            self.save_config()
            a_lims = (range_a.range_min(), range_a.range_max())
            if pairs.checkState():
                b_lims = None
                range_type = fc.RangeTypes.InnerProduct
            else:
                b_lims = (range_b.range_min(), range_b.range_max())
                range_type = fc.RangeTypes.OuterProduct
            return fc.generate_pairs(a_lims, b_lims, range_type)

        def on_addition_clicked():
            self.deck = fc.generate_addition(
                get_vals(), time_threshold=mastery_time.value())
            self.run_deck()

        def on_subtraction_clicked():
            self.deck = fc.generate_subtraction(
                get_vals(), time_threshold=mastery_time.value())
            self.run_deck()

        def on_multiplication_clicked():
            self.deck = fc.generate_multiplication(
                get_vals(), time_threshold=mastery_time.value())
            self.run_deck()

        def on_division_clicked():
            self.deck = fc.generate_division(
                get_vals(), time_threshold=mastery_time.value())
            self.run_deck()

        def on_sqrt_clicked():
            self.deck = fc.generate_square_roots(
                get_vals(), time_threshold=mastery_time.value())
            self.run_deck()

        def on_custom_clicked():
            update_config()
            self.save_config()
            file, _ = QFileDialog.getOpenFileName(
                self.window, "Open Flashcard Deck",
                str(self.current_config["log_file_dir"]),
                "Flashcard decks (*.yml)")
            self.deck = fc.load_deck(file)
            for c in self.deck.cards:
                c.responses = []
            self.run_deck()

        def update_config():
            self.current_config["name"] = config_name.currentText()
            self.current_config["mastery_time"] = mastery_time.value()
            self.current_config["min_val"] = range_a.range_min()
            self.current_config["max_val"] = range_a.range_max()
            self.current_config["min_val_b"] = range_b.range_min()
            self.current_config["max_val_b"] = range_b.range_max()

        def on_config_change():
            print(config_name.currentIndex())
            config = self.configs[config_name.currentIndex()]
            mastery_time.setValue(config["mastery_time"])
            range_a.set_range(config["min_val"], config["max_val"])
            range_b.set_range(config["min_val_b"], config["max_val_b"])
            self.current_config = config

        def update_configs():
            config_name.clear()
            for cfg in self.configs:
                config_name.addItem(cfg["name"])
            config_name.setCurrentText(self.current_config["name"])

        def remove_config():
            if self.current_config["name"] == config_name.currentText():
                self.delete_config()
                config_name.removeItem(config_name.currentIndex())

        class RangeSelect(QWidget):
            def __init__(self, label="Min/Max Values", min_val=0, max_val=15):
                super().__init__()
                self.layout = QHBoxLayout()
                self.label = QLabel(label)
                self.start_val = QSpinBox()
                self.start_val.setRange(0, max_val)
                self.stop_val = QSpinBox()
                self.stop_val.setRange(min_val, 25)
                self.start_val.setValue(min_val)
                self.stop_val.setValue(max_val)
                self.start_val.valueChanged.connect(self.on_change)
                self.stop_val.valueChanged.connect(self.on_change)
                self.layout.addWidget(self.label)
                self.layout.addStretch()
                self.layout.addWidget(self.start_val)
                self.layout.addWidget(self.stop_val)
                self.setLayout(self.layout)

            def on_change(self):
                self.start_val.setRange(0, self.stop_val.value())
                self.stop_val.setRange(self.start_val.value(), 25)

            def range_min(self):
                return int(self.start_val.value())

            def range_max(self):
                return int(self.stop_val.value())

            def set_range(self, min_val, max_val):
                self.start_val.setRange(0, max_val)
                self.stop_val.setRange(min_val, 25)
                self.start_val.setValue(min_val)
                self.stop_val.setValue(max_val)
                # print(f'Setting range to ({min_val}, {max_val}).')

        def on_pairs_changed(pairs_mode):
            range_b.setDisabled(pairs_mode)
            if pairs_mode:
                division.setText('Square Roots       (√(A×A))')
                division.clicked.disconnect(on_division_clicked)
                division.clicked.connect(on_sqrt_clicked)
            else:
                division.setText('Division           ((A×B) ÷ A)')
                division.clicked.disconnect(on_sqrt_clicked)
                division.clicked.connect(on_division_clicked)

        layout = QVBoxLayout()
        layout.addStretch()
        layout_config = QHBoxLayout()
        add_config = QPushButton('Add Config')
        add_config.setMaximumWidth(400)
        layout_config.addWidget(add_config)
        config_name = QComboBox()
        update_configs()
        config_name.currentTextChanged.connect(on_config_change)
        add_config.clicked.connect(lambda _: (config_name.setEditable(True),
                                              config_name.clearEditText()))
        layout_config.addWidget(config_name)
        rm_config = QPushButton('Remove Config')
        rm_config.setMaximumWidth(500)
        rm_config.clicked.connect(remove_config)
        layout_config.addWidget(rm_config)
        layout.addLayout(layout_config)
        layout.addStretch()

        layout_mastery_time = QHBoxLayout()
        layout_mastery_time.addWidget(
            QLabel("Response time for a problem to be mastered (seconds)"))
        mastery_time = QSpinBox()
        mastery_time.setRange(1, 15)
        mastery_time.setValue(self.current_config["mastery_time"])
        layout_mastery_time.addWidget(mastery_time)
        layout.addLayout(layout_mastery_time)

        layout.addStretch()

        range_a = RangeSelect("Min/Max Range for A",
                              min_val=self.current_config["min_val"],
                              max_val=self.current_config["max_val"])
        layout.addWidget(range_a)
        range_b = RangeSelect("Min/Max Range for B",
                              min_val=self.current_config["min_val_b"],
                              max_val=self.current_config["max_val_b"])
        layout.addWidget(range_b)

        pairs_layout = QHBoxLayout()
        pairs_layout.addWidget(QLabel('Pairs?'))
        pairs_layout.addStretch()
        pairs = QCheckBox()
        pairs_layout.addWidget(pairs)
        pairs.stateChanged.connect(on_pairs_changed)
        layout.addLayout(pairs_layout)

        hline = QFrame()
        hline.setFrameShape(QFrame.HLine)
        hline.setFrameShadow(QFrame.Sunken)
        hline.setMinimumHeight(40)
        layout.addWidget(hline)

        type_selection_layout = QHBoxLayout()
        type_selection_layout.addWidget(QLabel("Select type of flashcards:"))

        button_layout = QVBoxLayout()
        addition = QPushButton("Addition                 (A + B)")
        subtraction = QPushButton("Subtraction      ((A+B) - A)")
        multiplication = QPushButton("Multiplication         (A × B)")
        division = QPushButton("Division           ((A×B) ÷ A)")
        custom = QPushButton("Load from File                   ")
        addition.clicked.connect(on_addition_clicked)
        subtraction.clicked.connect(on_subtraction_clicked)
        multiplication.clicked.connect(on_multiplication_clicked)
        division.clicked.connect(on_division_clicked)
        custom.clicked.connect(on_custom_clicked)
        button_layout.addWidget(addition)
        button_layout.addWidget(subtraction)
        button_layout.addWidget(multiplication)
        button_layout.addWidget(division)
        button_layout.addWidget(custom)
        type_selection_layout.addLayout(button_layout)

        layout.addStretch()
        layout.addLayout(type_selection_layout)
        layout.addStretch()

        # on_config_change()
        return layout