예제 #1
0
    def _validate_output_file(self):
        output_file = self.output_file
        has_error = output_file.is_dir() or not output_file.parent.is_dir()

        common_qt_lib.set_error_border_stylesheet(self.output_file_edit,
                                                  has_error)
        self._update_accept_button()
예제 #2
0
def add_validation(edit: QtWidgets.QLineEdit, validation: Callable[[], bool], post_validation: Callable[[], None]):
    def field_validation():
        common_qt_lib.set_error_border_stylesheet(edit, not validation())
        post_validation()

    common_qt_lib.set_error_border_stylesheet(edit, False)
    edit.field_validation = field_validation
    edit.textChanged.connect(field_validation)
예제 #3
0
    def _validate_input_file(self):
        if self._has_game:
            has_error = self.input_file_edit.text() != _VALID_GAME_TEXT
        else:
            has_error = not self.input_file.is_file()

        common_qt_lib.set_error_border_stylesheet(self.input_file_edit, has_error)
        self._update_accept_button()
예제 #4
0
    def _on_permalink_changed(self, value: str):
        common_qt_lib.set_error_border_stylesheet(self.permalink_edit, False)
        self.permalink_edit.setText(self.permalink_edit.text().strip())
        self.accept_button.setEnabled(False)

        try:
            # Ignoring return value: we only want to know if it's valid
            self.get_permalink_from_field()
            self.accept_button.setEnabled(True)
        except ValueError:
            common_qt_lib.set_error_border_stylesheet(self.permalink_edit,
                                                      True)
예제 #5
0
    def on_name_edit(self, value: str):
        has_error = False

        try:
            new_node = self.create_new_node()
        except ValueError:
            new_node = None

        if isinstance(new_node, DockNode):
            area = self.game.world_list.nodes_to_area(self.node)
            has_error = not integrity_check.dock_has_correct_name(area, new_node)[0]

        common_qt_lib.set_error_border_stylesheet(self.name_edit, has_error)
예제 #6
0
    def _on_permalink_changed(self, value: str):
        common_qt_lib.set_error_border_stylesheet(self.permalink_edit, False)
        common_qt_lib.set_edit_if_different(self.permalink_edit,
                                            self.permalink_edit.text().strip())
        self.accept_button.setEnabled(False)
        self.import_error_label.setText("")

        try:
            # Ignoring return value: we only want to know if it's valid
            self.get_permalink_from_field()
            self.accept_button.setEnabled(True)
        except ValueError as e:
            self.import_error_label.setText(f"Invalid permalink: {e}")
            common_qt_lib.set_error_border_stylesheet(self.permalink_edit,
                                                      True)
예제 #7
0
    def refresh_drive_list(self):
        old_value = self.sd_combo.currentText()

        self.sd_combo.clear()
        for drive, type_name, path in get_windows_drives():
            if type_name == 'Removable' or self.sd_non_removable.isChecked():
                self.sd_combo.addItem(f"{drive}: ({type_name})", path)

        if self.sd_combo.count() == 0:
            self.sd_combo.addItem("None found", None)
            common_qt_lib.set_error_border_stylesheet(self.sd_combo, True)
        else:
            common_qt_lib.set_error_border_stylesheet(self.sd_combo, False)

        index = self.sd_combo.findText(old_value)
        if index >= 0:
            self.sd_combo.setCurrentIndex(index)
예제 #8
0
def add_field_validation(accept_button: QtWidgets.QPushButton, fields: dict[QtWidgets.QLineEdit, Callable[[], bool]]):
    def accept_validation():
        accept_button.setEnabled(not any(f.has_error for f in fields.keys()))

    def make_validation(obj, check_err):
        def field_validation():
            common_qt_lib.set_error_border_stylesheet(obj, check_err())
            accept_validation()

        return field_validation

    accept_button.update_validation = accept_validation
    for field, check_error_function in fields.items():
        common_qt_lib.set_error_border_stylesheet(field, check_error_function())
        field.field_validation = make_validation(field, check_error_function)
        field.textChanged.connect(field.field_validation)

    accept_validation()
예제 #9
0
    def _on_permalink_changed(self, value: str):
        common_qt_lib.set_error_border_stylesheet(self.permalink_edit, False)
        common_qt_lib.set_edit_if_different(self.permalink_edit,
                                            self.permalink_edit.text().strip())
        self.accept_button.setEnabled(False)
        self.import_error_label.setText("")

        try:
            # Ignoring return value: we only want to know if it's valid
            new_permalink = self.get_permalink_from_field()
            if new_permalink.as_base64_str != self.permalink_edit.text():
                raise ValueError(
                    "Imported permalink is different from text field.")
            self.accept_button.setEnabled(True)

        except (ValueError, UnsupportedPermalink) as e:
            if isinstance(e, UnsupportedPermalink):
                msg = f"Unsupported permalink: {e}"
            else:
                msg = f"Invalid permalink: {e}"
            self.import_error_label.setText(msg)
            common_qt_lib.set_error_border_stylesheet(self.permalink_edit,
                                                      True)
예제 #10
0
    def on_preset_changed(self, preset: Preset):
        # Item alternatives
        layout = preset.configuration
        major_configuration = layout.major_items_configuration

        for progressive_widget in self._progressive_widgets:
            progressive_widget.on_preset_changed(
                preset,
                self._boxes_for_category[
                    progressive_widget.progressive_item.item_category][2],
            )

        for split_ammo in self._split_ammo_widgets:
            split_ammo.on_preset_changed(preset, self._ammo_pickup_widgets)

        # Random Starting Items
        self.minimum_starting_spinbox.setValue(
            major_configuration.minimum_random_starting_items)
        self.maximum_starting_spinbox.setValue(
            major_configuration.maximum_random_starting_items)

        # Default Items
        for category, default_item in major_configuration.default_items.items(
        ):
            combo = self._default_items[category]
            combo.setCurrentIndex(combo.findData(default_item))

            for item, widget in self._boxes_for_category[category][2].items():
                widget.setEnabled(default_item != item)

        # Major Items
        for _, _, elements in self._boxes_for_category.values():
            for major_item, widget in elements.items():
                widget.state = major_configuration.items_state[major_item]

        # Energy Tank
        energy_tank_state = major_configuration.items_state[
            self._energy_tank_item]
        self.energy_tank_starting_spinbox.setValue(
            energy_tank_state.num_included_in_starting_items)
        self.energy_tank_shuffled_spinbox.setValue(
            energy_tank_state.num_shuffled_pickups)

        # Ammo
        ammo_provided = major_configuration.calculate_provided_ammo()
        ammo_configuration = layout.ammo_configuration

        for ammo_item, maximum in ammo_configuration.maximum_ammo.items():
            for spinbox in self._ammo_maximum_spinboxes[ammo_item]:
                spinbox.setValue(maximum)

        previous_pickup_for_item = {}
        resource_database = self.game_description.resource_database

        item_for_index: Dict[int, ItemResourceInfo] = {
            ammo_index: resource_database.get_item(ammo_index)
            for ammo_index in ammo_provided.keys()
        }

        for ammo, state in ammo_configuration.items_state.items():
            widgets = self._ammo_pickup_widgets[ammo]
            widgets.pickup_spinbox.setValue(state.pickup_count)

            if widgets.require_major_item_check is not None:
                widgets.require_major_item_check.setChecked(
                    state.requires_major_item)

            try:
                if state.pickup_count == 0:
                    widgets.expected_count.setText(
                        "No expansions will be created.")
                    continue

                ammo_per_pickup = items_for_ammo(
                    ammo, state, ammo_provided, previous_pickup_for_item,
                    ammo_configuration.maximum_ammo)

                totals = functools.reduce(
                    lambda a, b: [x + y for x, y in zip(a, b)],
                    ammo_per_pickup, [0 for _ in ammo.items])

                if {total % state.pickup_count for total in totals} == {0}:
                    count_text_template = _EXPECTED_COUNT_TEXT_TEMPLATE_EXACT
                else:
                    count_text_template = _EXPECTED_COUNT_TEXT_TEMPLATE

                widgets.expected_count.setText(
                    count_text_template.format(
                        per_expansion=" and ".join(
                            "{:.3g} {}".format(
                                total / state.pickup_count,
                                item_for_index[ammo_index].long_name)
                            for ammo_index, total in zip(ammo.items, totals)),
                        total=" and ".join(
                            "{} {}".format(
                                total, item_for_index[ammo_index].long_name)
                            for ammo_index, total in zip(ammo.items, totals)),
                        from_items=" and ".join("{} {}".format(
                            ammo_provided[ammo_index],
                            item_for_index[ammo_index].long_name)
                                                for ammo_index in ammo.items),
                    ))

            except InvalidConfiguration as invalid_config:
                widgets.expected_count.setText(str(invalid_config))

        # Item pool count
        try:
            pool_items, maximum_size = pool_creator.calculate_pool_item_count(
                layout)
            self.item_pool_count_label.setText("Items in pool: {}/{}".format(
                pool_items, maximum_size))
            common_qt_lib.set_error_border_stylesheet(
                self.item_pool_count_label, pool_items > maximum_size)

        except InvalidConfiguration as invalid_config:
            self.item_pool_count_label.setText(
                "Invalid Configuration: {}".format(invalid_config))
            common_qt_lib.set_error_border_stylesheet(
                self.item_pool_count_label, True)
예제 #11
0
 def field_validation():
     common_qt_lib.set_error_border_stylesheet(edit, not validation())
     post_validation()
예제 #12
0
 def _validate_custom_path(self):
     common_qt_lib.set_error_border_stylesheet(self.custom_path_edit, is_directory_validator(self.custom_path_edit))
예제 #13
0
 def _validate_input_file(self):
     common_qt_lib.set_error_border_stylesheet(self.input_file_edit, is_directory_validator(self.input_file_edit))
예제 #14
0
    def on_preset_changed(self, preset: Preset):
        layout = preset.configuration
        major_configuration = layout.major_items_configuration

        # Random Starting Items
        self.minimum_starting_spinbox.setValue(major_configuration.minimum_random_starting_items)
        self.maximum_starting_spinbox.setValue(major_configuration.maximum_random_starting_items)

        # Default Items
        for category, default_item in major_configuration.default_items.items():
            combo = self._default_items[category]
            combo.setCurrentIndex(combo.findData(default_item))

            for item, widget in self._boxes_for_category[category.name][2].items():
                widget.setEnabled(default_item != item)

        # Major Items
        for _, _, elements in self._boxes_for_category.values():
            for major_item, widget in elements.items():
                widget.set_new_state(major_configuration.items_state[major_item])

        # Progressive Items
        for progressive_widget in self._progressive_widgets:
            progressive_widget.on_preset_changed(
                preset,
                self._boxes_for_category[progressive_widget.progressive_item.item_category.name][2],
            )

        # Ammo
        ammo_configuration = layout.ammo_configuration

        ammo_provided = major_configuration.calculate_provided_ammo()
        for ammo, state in ammo_configuration.items_state.items():
            for ammo_index, count in enumerate(state.ammo_count):
                ammo_provided[ammo.items[ammo_index]] += count * state.pickup_count

        resource_database = self.game_description.resource_database

        item_for_index: Dict[str, ItemResourceInfo] = {
            ammo_index: resource_database.get_item(ammo_index)
            for ammo_index in ammo_provided.keys()
        }

        for ammo, state in ammo_configuration.items_state.items():
            widgets = self._ammo_pickup_widgets[ammo]
            widgets.pickup_spinbox.setValue(state.pickup_count)

            if widgets.require_major_item_check is not None:
                widgets.require_major_item_check.setChecked(state.requires_major_item)
                if self.game == RandovaniaGame.METROID_PRIME:
                    widgets.require_major_item_check.setChecked(False)

            self_counts = []
            for ammo_index, count in enumerate(state.ammo_count):
                self_counts.append(count * state.pickup_count)
                self._ammo_item_count_spinboxes[ammo.name][ammo_index].setValue(count)

            try:
                if state.pickup_count == 0:
                    widgets.expected_count.setText("No expansions will be created.")
                    continue

                widgets.expected_count.setText(
                    _EXPECTED_COUNT_TEXT_TEMPLATE_EXACT.format(
                        total=" and ".join(
                            item_names.add_quantity_to_resource(item_for_index[ammo_index].long_name,
                                                                self_count, True)
                            for ammo_index, self_count in zip(ammo.items, self_counts)
                        ),
                        from_items=" and ".join(
                            item_names.add_quantity_to_resource(item_for_index[ammo_index].long_name,
                                                                ammo_provided[ammo_index] - self_count,
                                                                True)
                            for ammo_index, self_count in zip(ammo.items, self_counts)
                        ),
                        maximum=" and ".join(
                            item_names.add_quantity_to_resource(item_for_index[ammo_index].long_name,
                                                                min(ammo_provided[ammo_index],
                                                                    item_for_index[ammo_index].max_capacity),
                                                                True)
                            for ammo_index in ammo.items
                        )
                    )
                )

            except InvalidConfiguration as invalid_config:
                widgets.expected_count.setText(str(invalid_config))

        # Item pool count
        try:
            pool_items, maximum_size = pool_creator.calculate_pool_item_count(layout)
            self.item_pool_count_label.setText("Items in pool: {}/{}".format(pool_items, maximum_size))
            common_qt_lib.set_error_border_stylesheet(self.item_pool_count_label, pool_items > maximum_size)

        except InvalidConfiguration as invalid_config:
            self.item_pool_count_label.setText("Invalid Configuration: {}".format(invalid_config))
            common_qt_lib.set_error_border_stylesheet(self.item_pool_count_label, True)
예제 #15
0
 def field_validation():
     common_qt_lib.set_error_border_stylesheet(obj, check_err())
     accept_validation()
예제 #16
0
    def on_preset_changed(self, preset: Preset):
        # Item alternatives
        layout = preset.configuration
        major_configuration = layout.major_items_configuration

        for progressive_widget in self._progressive_widgets:
            progressive_widget.on_preset_changed(
                preset,
                self._boxes_for_category[
                    progressive_widget.progressive_item.item_category][2],
            )

        for split_ammo in self._split_ammo_widgets:
            split_ammo.on_preset_changed(preset, self._ammo_pickup_widgets)

        # Random Starting Items
        self.minimum_starting_spinbox.setValue(
            major_configuration.minimum_random_starting_items)
        self.maximum_starting_spinbox.setValue(
            major_configuration.maximum_random_starting_items)

        # Energy Tank
        energy_tank_state = major_configuration.items_state[
            self._energy_tank_item]

        self.energy_tank_starting_spinbox.setValue(
            energy_tank_state.num_included_in_starting_items)
        self.energy_tank_shuffled_spinbox.setValue(
            energy_tank_state.num_shuffled_pickups)

        # Ammo
        ammo_provided = major_configuration.calculate_provided_ammo()
        ammo_configuration = layout.ammo_configuration

        for ammo_item, maximum in ammo_configuration.maximum_ammo.items():
            for spinbox in self._ammo_maximum_spinboxes[ammo_item]:
                spinbox.setValue(maximum)

        previous_pickup_for_item = {}
        game = default_database.game_description_for(self.game)
        resource_database = game.resource_database

        item_for_index: Dict[int, ItemResourceInfo] = {
            ammo_index: resource_database.get_item(ammo_index)
            for ammo_index in ammo_provided.keys()
        }

        for ammo, state in ammo_configuration.items_state.items():
            self._ammo_pickup_widgets[ammo][0].setValue(state.pickup_count)

            if self._ammo_pickup_widgets[ammo][5] is not None:
                self._ammo_pickup_widgets[ammo][5].setChecked(
                    state.requires_major_item)

            try:
                if state.pickup_count == 0:
                    self._ammo_pickup_widgets[ammo][1].setText(
                        "No expansions will be created.")
                    continue

                ammo_per_pickup = items_for_ammo(
                    ammo, state, ammo_provided, previous_pickup_for_item,
                    ammo_configuration.maximum_ammo)

                totals = functools.reduce(
                    lambda a, b: [x + y for x, y in zip(a, b)],
                    ammo_per_pickup, [0 for _ in ammo.items])

                if {total % state.pickup_count for total in totals} == {0}:
                    count_text_template = _EXPECTED_COUNT_TEXT_TEMPLATE_EXACT
                else:
                    count_text_template = _EXPECTED_COUNT_TEXT_TEMPLATE

                self._ammo_pickup_widgets[ammo][1].setText(
                    count_text_template.format(
                        per_expansion=" and ".join(
                            "{:.3g} {}".format(
                                total / state.pickup_count,
                                item_for_index[ammo_index].long_name)
                            for ammo_index, total in zip(ammo.items, totals)),
                        total=" and ".join(
                            "{} {}".format(
                                total, item_for_index[ammo_index].long_name)
                            for ammo_index, total in zip(ammo.items, totals)),
                        from_items=" and ".join("{} {}".format(
                            ammo_provided[ammo_index],
                            item_for_index[ammo_index].long_name)
                                                for ammo_index in ammo.items),
                    ))

            except InvalidConfiguration as invalid_config:
                self._ammo_pickup_widgets[ammo][1].setText(str(invalid_config))

        # Item pool count
        pool_pickup = pool_creator.calculate_pool_results(
            layout, resource_database).pickups
        min_starting_items = layout.major_items_configuration.minimum_random_starting_items
        maximum_size = game.world_list.num_pickup_nodes + min_starting_items
        self.item_pool_count_label.setText("Items in pool: {}/{}".format(
            len(pool_pickup), maximum_size))
        common_qt_lib.set_error_border_stylesheet(
            self.item_pool_count_label,
            len(pool_pickup) > maximum_size)