コード例 #1
0
def test_on_preset_changed(skip_qtbot, default_preset):
    # Setup
    editor = PresetEditor(default_preset)
    window = LogicSettingsWindow(None, editor)

    # Run
    window.on_preset_changed(editor.create_custom_preset_with())
コード例 #2
0
    def _on_customize_button(self):
        editor = PresetEditor(self._current_preset_data)
        self._logic_settings_window = LogicSettingsWindow(
            self._window_manager, editor)

        self._logic_settings_window.on_preset_changed(
            editor.create_custom_preset_with())
        editor.on_changed = lambda: self._logic_settings_window.on_preset_changed(
            editor.create_custom_preset_with())

        result = self._logic_settings_window.exec_()
        self._logic_settings_window = None

        if result == QDialog.Accepted:
            self._add_new_preset(editor.create_custom_preset_with())
コード例 #3
0
    async def _on_customize_button(self):
        if self._logic_settings_window is not None:
            self._logic_settings_window.raise_()
            return

        editor = PresetEditor(self._current_preset_data.get_preset())
        self._logic_settings_window = LogicSettingsWindow(self._window_manager, editor)

        self._logic_settings_window.on_preset_changed(editor.create_custom_preset_with())
        editor.on_changed = lambda: self._logic_settings_window.on_preset_changed(editor.create_custom_preset_with())

        result = await async_dialog.execute_dialog(self._logic_settings_window)
        self._logic_settings_window = None

        if result == QDialog.Accepted:
            self._add_new_preset(VersionedPreset.with_preset(editor.create_custom_preset_with()))
コード例 #4
0
def test_starting_location_world_select(skip_qtbot, default_preset):
    # Setup
    editor = PresetEditor(default_preset)
    window = LogicSettingsWindow(None, editor)
    skip_qtbot.addWidget(window)

    # Run
    checkbox_list = window._starting_location_for_world
    window.on_preset_changed(editor.create_custom_preset_with())
    assert len(checkbox_list) == 10
    temple_grounds_checkbox = checkbox_list["Temple Grounds"]
    assert temple_grounds_checkbox.checkState() == Qt.PartiallyChecked
    skip_qtbot.mouseClick(temple_grounds_checkbox, Qt.LeftButton)
    assert temple_grounds_checkbox.checkState() == Qt.Checked
    assert len(editor.layout_configuration.starting_location.locations) == 40
    skip_qtbot.mouseClick(temple_grounds_checkbox, Qt.LeftButton)
    assert temple_grounds_checkbox.checkState() == Qt.Unchecked
    assert len(editor.layout_configuration.starting_location.locations) == 0
    skip_qtbot.mouseClick(temple_grounds_checkbox, Qt.LeftButton)
    window.on_preset_changed(editor.create_custom_preset_with())
    assert temple_grounds_checkbox.checkState() == Qt.Checked
    assert len(editor.layout_configuration.starting_location.locations) == 40
コード例 #5
0
class GenerateSeedTab(QWidget):
    _current_lock_state: bool = True
    _logic_settings_window = None
    _current_preset: Preset = None
    _tool_button_menu: QMenu
    _action_delete: QAction

    failed_to_generate_signal = Signal(GenerationFailure)

    def __init__(self, background_processor: BackgroundTaskMixin,
                 window: Ui_MainWindow, window_manager: WindowManager,
                 options: Options):
        super().__init__()

        self.background_processor = background_processor
        self.window = window
        self._window_manager = window_manager
        self._options = options

        self.failed_to_generate_signal.connect(
            self._show_failed_generation_exception)

    def setup_ui(self):
        window = self.window

        # Progress
        self.background_processor.background_tasks_button_lock_signal.connect(
            self.enable_buttons_with_background_tasks)

        for preset in self._window_manager.preset_manager.all_presets:
            self._create_button_for_preset(preset)

        # Menu
        self._tool_button_menu = QMenu(window.preset_tool_button)
        window.preset_tool_button.setMenu(self._tool_button_menu)

        self._action_delete = QAction(window)
        self._action_delete.setText("Delete")
        self._tool_button_menu.addAction(self._action_delete)

        action_export_preset = QAction(window)
        action_export_preset.setText("Export")
        self._tool_button_menu.addAction(action_export_preset)

        action_import_preset = QAction(window)
        action_import_preset.setText("Import")
        self._tool_button_menu.addAction(action_import_preset)

        # Signals
        window.create_customize_button.clicked.connect(
            self._on_customize_button)
        window.create_preset_combo.activated.connect(self._on_select_preset)
        window.create_generate_button.clicked.connect(
            partial(self._generate_new_seed, True))
        window.create_generate_race_button.clicked.connect(
            partial(self._generate_new_seed, False))

        self._action_delete.triggered.connect(self._on_delete_preset)
        action_export_preset.triggered.connect(self._on_export_preset)
        action_import_preset.triggered.connect(self._on_import_preset)

    def _show_failed_generation_exception(self, exception: GenerationFailure):
        QMessageBox.critical(
            self._window_manager, "An error occurred while generating a seed",
            "{}\n\nSome errors are expected to occur, please try again.".
            format(exception))

    @property
    def _current_preset_data(self) -> Optional[Preset]:
        return self._window_manager.preset_manager.preset_for_name(
            self.window.create_preset_combo.currentData())

    def enable_buttons_with_background_tasks(self, value: bool):
        self._current_lock_state = value
        self.window.welcome_tab.setEnabled(value)

    def _create_button_for_preset(self, preset: Preset):
        create_preset_combo = self.window.create_preset_combo
        create_preset_combo.addItem(preset.name, preset.name)

    def _add_new_preset(self, preset: Preset):
        with self._options as options:
            options.selected_preset_name = preset.name

        if self._window_manager.preset_manager.add_new_preset(preset):
            self._create_button_for_preset(preset)
        self.on_preset_changed(preset)

    def _on_customize_button(self):
        editor = PresetEditor(self._current_preset_data)
        self._logic_settings_window = LogicSettingsWindow(
            self._window_manager, editor)

        self._logic_settings_window.on_preset_changed(
            editor.create_custom_preset_with())
        editor.on_changed = lambda: self._logic_settings_window.on_preset_changed(
            editor.create_custom_preset_with())

        result = self._logic_settings_window.exec_()
        self._logic_settings_window = None

        if result == QDialog.Accepted:
            self._add_new_preset(editor.create_custom_preset_with())

    def _on_delete_preset(self):
        self._window_manager.preset_manager.delete_preset(
            self._current_preset_data)
        self.window.create_preset_combo.removeItem(
            self.window.create_preset_combo.currentIndex())
        self._on_select_preset()

    def _on_export_preset(self):
        path = common_qt_lib.prompt_user_for_preset_file(self._window_manager,
                                                         new_file=True)
        if path is not None:
            save_preset_file(self._current_preset_data, path)

    def _on_import_preset(self):
        path = common_qt_lib.prompt_user_for_preset_file(self._window_manager,
                                                         new_file=False)
        if path is None:
            return

        try:
            preset = read_preset_file(path)
        except (ValueError, KeyError):
            QMessageBox.critical(
                self._window_manager, "Error loading preset",
                "The file at '{}' contains an invalid preset.".format(path))
            return

        if self._window_manager.preset_manager.preset_for_name(
                preset.name) is not None:
            user_response = QMessageBox.warning(
                self._window_manager, "Preset name conflict",
                "A preset named '{}' already exists. Do you want to overwrite it?"
                .format(preset.name), QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No)
            if user_response == QMessageBox.No:
                return

        self._add_new_preset(preset)

    def _on_select_preset(self):
        preset_data = self._current_preset_data
        self.on_preset_changed(preset_data)
        with self._options as options:
            options.selected_preset_name = preset_data.name

    # Generate seed

    def _generate_new_seed(self, spoiler: bool):
        self.generate_seed_from_permalink(
            Permalink(
                seed_number=random.randint(0, 2**31),
                spoiler=spoiler,
                preset=self._current_preset_data,
            ))

    def generate_seed_from_permalink(self, permalink: Permalink):
        def work(progress_update: ProgressUpdateCallable):
            try:
                layout = simplified_patcher.generate_layout(
                    progress_update=progress_update,
                    permalink=permalink,
                    options=self._options)
                progress_update(
                    f"Success! (Seed hash: {layout.shareable_hash})", 1)
                self.window.show_seed_tab(layout)

            except GenerationFailure as generate_exception:
                self.failed_to_generate_signal.emit(generate_exception)
                progress_update(
                    "Generation Failure: {}".format(generate_exception), -1)

        self.background_processor.run_in_background_thread(
            work, "Creating a seed...")

    def on_options_changed(self, options: Options):
        if self._current_preset is None:
            preset_name = options.selected_preset_name
            if preset_name is not None:
                index = self.window.create_preset_combo.findText(preset_name)
                if index != -1:
                    self.window.create_preset_combo.setCurrentIndex(index)
                    self.on_preset_changed(self._current_preset_data)
                    return

            self.window.create_preset_combo.setCurrentIndex(0)
            self.on_preset_changed(
                self._window_manager.preset_manager.default_preset)

    def on_preset_changed(self, preset: Preset):
        self._current_preset = preset

        self.window.create_preset_description.setText(preset.description)
        self._action_delete.setEnabled(preset.base_preset_name is not None)

        create_preset_combo = self.window.create_preset_combo
        create_preset_combo.setCurrentIndex(
            create_preset_combo.findText(preset.name))

        categories = list(preset_describer.describe(preset))
        left_categories = categories[::2]
        right_categories = categories[1::2]

        self.window.create_describe_left_label.setText(
            preset_describer.merge_categories(left_categories))
        self.window.create_describe_right_label.setText(
            preset_describer.merge_categories(right_categories))
コード例 #6
0
class GenerateSeedTab(QWidget, BackgroundTaskMixin):
    _logic_settings_window: Optional[LogicSettingsWindow] = None
    _has_preset: bool = False
    _tool_button_menu: QMenu
    _action_delete: QAction

    failed_to_generate_signal = Signal(GenerationFailure)

    def __init__(self, window: Ui_MainWindow, window_manager: WindowManager,
                 options: Options):
        super().__init__()

        self.window = window
        self._window_manager = window_manager
        self._options = options

    def setup_ui(self):
        window = self.window

        # Progress
        self.background_tasks_button_lock_signal.connect(
            self.enable_buttons_with_background_tasks)
        self.progress_update_signal.connect(self.update_progress)
        self.failed_to_generate_signal.connect(
            self._show_failed_generation_exception)
        self.window.stop_background_process_button.clicked.connect(
            self.stop_background_process)

        for preset in self._window_manager.preset_manager.all_presets:
            self._create_button_for_preset(preset)

        self.window.num_players_spin_box.setVisible(
            self._window_manager.is_preview_mode)

        # Menu
        self._tool_button_menu = QMenu(window.preset_tool_button)
        window.preset_tool_button.setMenu(self._tool_button_menu)

        self._action_delete = QAction(window)
        self._action_delete.setText("Delete")
        self._tool_button_menu.addAction(self._action_delete)

        action_export_preset = QAction(window)
        action_export_preset.setText("Export")
        self._tool_button_menu.addAction(action_export_preset)

        action_import_preset = QAction(window)
        action_import_preset.setText("Import")
        self._tool_button_menu.addAction(action_import_preset)

        # Signals
        window.create_customize_button.clicked.connect(
            self._on_customize_button)
        window.create_preset_combo.activated.connect(self._on_select_preset)
        window.create_generate_button.clicked.connect(
            partial(self._generate_new_seed, True))
        window.create_generate_race_button.clicked.connect(
            partial(self._generate_new_seed, False))

        self._action_delete.triggered.connect(self._on_delete_preset)
        action_export_preset.triggered.connect(self._on_export_preset)
        action_import_preset.triggered.connect(self._on_import_preset)

    def _show_failed_generation_exception(self, exception: GenerationFailure):
        QMessageBox.critical(
            self._window_manager, "An error occurred while generating a seed",
            "{}\n\nSome errors are expected to occur, please try again.".
            format(exception))

    @property
    def _current_preset_data(self) -> Optional[VersionedPreset]:
        return self._window_manager.preset_manager.preset_for_name(
            self.window.create_preset_combo.currentData())

    def enable_buttons_with_background_tasks(self, value: bool):
        self.window.stop_background_process_button.setEnabled(not value)
        self.window.create_generate_button.setEnabled(value)
        self.window.create_generate_race_button.setEnabled(value)

    def _create_button_for_preset(self, preset: VersionedPreset):
        create_preset_combo = self.window.create_preset_combo
        create_preset_combo.addItem(preset.name, preset.name)

    def _add_new_preset(self, preset: VersionedPreset):
        with self._options as options:
            options.selected_preset_name = preset.name

        if self._window_manager.preset_manager.add_new_preset(preset):
            self._create_button_for_preset(preset)
        self.on_preset_changed(preset.get_preset())

    @asyncSlot()
    async def _on_customize_button(self):
        if self._logic_settings_window is not None:
            self._logic_settings_window.raise_()
            return

        editor = PresetEditor(self._current_preset_data.get_preset())
        self._logic_settings_window = LogicSettingsWindow(
            self._window_manager, editor)

        self._logic_settings_window.on_preset_changed(
            editor.create_custom_preset_with())
        editor.on_changed = lambda: self._logic_settings_window.on_preset_changed(
            editor.create_custom_preset_with())

        result = await async_dialog.execute_dialog(self._logic_settings_window)
        self._logic_settings_window = None

        if result == QDialog.Accepted:
            self._add_new_preset(
                VersionedPreset.with_preset(
                    editor.create_custom_preset_with()))

    def _on_delete_preset(self):
        self._window_manager.preset_manager.delete_preset(
            self._current_preset_data)
        self.window.create_preset_combo.removeItem(
            self.window.create_preset_combo.currentIndex())
        self._on_select_preset()

    def _on_export_preset(self):
        path = common_qt_lib.prompt_user_for_preset_file(self._window_manager,
                                                         new_file=True)
        if path is not None:
            self._current_preset_data.save_to_file(path)

    def _on_import_preset(self):
        path = common_qt_lib.prompt_user_for_preset_file(self._window_manager,
                                                         new_file=False)
        if path is None:
            return

        preset = VersionedPreset.from_file_sync(path)
        try:
            preset.get_preset()
        except (ValueError, KeyError):
            QMessageBox.critical(
                self._window_manager, "Error loading preset",
                "The file at '{}' contains an invalid preset.".format(path))
            return

        if self._window_manager.preset_manager.preset_for_name(
                preset.name) is not None:
            user_response = QMessageBox.warning(
                self._window_manager, "Preset name conflict",
                "A preset named '{}' already exists. Do you want to overwrite it?"
                .format(preset.name), QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No)
            if user_response == QMessageBox.No:
                return

        self._add_new_preset(preset)

    def _on_select_preset(self):
        preset_data = self._current_preset_data
        try:
            self.on_preset_changed(preset_data.get_preset())
        except InvalidPreset as e:
            QMessageBox.warning(
                self._window_manager, "Incompatible Preset",
                f"Preset {preset_data.name} can't be used as it contains the following error:\n{e.original_exception}"
            )
            self.window.create_preset_combo.setCurrentIndex(0)
            self.on_preset_changed(self._window_manager.preset_manager.
                                   default_preset.get_preset())
            return

        with self._options as options:
            options.selected_preset_name = preset_data.name

    # Generate seed

    def _generate_new_seed(self, spoiler: bool):
        preset = self._current_preset_data
        num_players = self.window.num_players_spin_box.value()

        self.generate_seed_from_permalink(
            Permalink(
                seed_number=random.randint(0, 2**31),
                spoiler=spoiler,
                presets={i: preset.get_preset()
                         for i in range(num_players)},
            ))

    def generate_seed_from_permalink(self, permalink: Permalink):
        def work(progress_update: ProgressUpdateCallable):
            try:
                layout = simplified_patcher.generate_layout(
                    progress_update=progress_update,
                    permalink=permalink,
                    options=self._options)
                progress_update(
                    f"Success! (Seed hash: {layout.shareable_hash})", 1)
                persist_layout(self._options.data_dir, layout)
                self._window_manager.open_game_details(layout)

            except GenerationFailure as generate_exception:
                self.failed_to_generate_signal.emit(generate_exception)
                progress_update(
                    "Generation Failure: {}".format(generate_exception), -1)

        self.run_in_background_thread(work, "Creating a seed...")

    def on_options_changed(self, options: Options):
        if not self._has_preset:
            preset_name = options.selected_preset_name
            if preset_name is not None:
                index = self.window.create_preset_combo.findText(preset_name)
                if index != -1:
                    self.window.create_preset_combo.setCurrentIndex(index)
                    try:
                        self.on_preset_changed(
                            self._current_preset_data.get_preset())
                        return
                    except InvalidPreset:
                        pass

            self.window.create_preset_combo.setCurrentIndex(0)
            self.on_preset_changed(self._window_manager.preset_manager.
                                   default_preset.get_preset())

    def on_preset_changed(self, preset: Preset):
        self._has_preset = True

        self.window.create_preset_description.setText(preset.description)
        self._action_delete.setEnabled(preset.base_preset_name is not None)

        create_preset_combo = self.window.create_preset_combo
        create_preset_combo.setCurrentIndex(
            create_preset_combo.findText(preset.name))

        categories = list(preset_describer.describe(preset))
        left_categories = categories[::2]
        right_categories = categories[1::2]

        self.window.create_describe_left_label.setText(
            preset_describer.merge_categories(left_categories))
        self.window.create_describe_right_label.setText(
            preset_describer.merge_categories(right_categories))

    def update_progress(self, message: str, percentage: int):
        self.window.progress_label.setText(message)
        if "Aborted" in message:
            percentage = 0
        if percentage >= 0:
            self.window.progress_bar.setRange(0, 100)
            self.window.progress_bar.setValue(percentage)
        else:
            self.window.progress_bar.setRange(0, 0)