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()
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)
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()
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)
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)
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)
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)
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()
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)
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)
def field_validation(): common_qt_lib.set_error_border_stylesheet(edit, not validation()) post_validation()
def _validate_custom_path(self): common_qt_lib.set_error_border_stylesheet(self.custom_path_edit, is_directory_validator(self.custom_path_edit))
def _validate_input_file(self): common_qt_lib.set_error_border_stylesheet(self.input_file_edit, is_directory_validator(self.input_file_edit))
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)
def field_validation(): common_qt_lib.set_error_border_stylesheet(obj, check_err()) accept_validation()
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)