예제 #1
0
파일: bg_menu.py 프로젝트: retke/skytemple
    def on_men_tiles_ani_settings_activate(self):
        dialog: Gtk.Dialog = self.parent.builder.get_object(
            'dialog_tiles_animated_settings')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        bpas_frame_info_entries = []
        for i, bpa in enumerate(self.parent.bpas):
            gui_i = i + 1
            enabled = self.parent.builder.get_object(f'bpa_enable{gui_i}')
            # If there's no second layer: Disable activating the BPA
            # TODO: Currently when removing layers, BPAs are not removed. Is that a problem?
            if self.parent.bma.number_of_layers <= 1 and i > 3:
                enabled.set_sensitive(False)
            enabled.set_active(bpa is not None)
            dialog.resize(470, 450)

            this_frame_info_entries = []
            bpas_frame_info_entries.append(this_frame_info_entries)

            bpa_duration_box: Gtk.Box = self.parent.builder.get_object(
                f'bpa_box{gui_i}')
            for child in bpa_duration_box:
                bpa_duration_box.remove(child)
            if bpa is None or len(bpa.frame_info) < 1:
                l = Gtk.Label.new(
                    "This BPA has no frames.\n"
                    "Enable the BPA and import images for a BPA to add frames."
                )
                l.show()
                bpa_duration_box.add(l)
            else:
                # Fill value for existing BPAs
                for frame_info in bpa.frame_info:
                    entry: Gtk.Entry = Gtk.Entry.new()
                    entry.set_input_purpose(Gtk.InputPurpose.NUMBER)
                    entry.set_width_chars(4)
                    entry.set_max_length(4)
                    entry.set_text(str(frame_info.duration_per_frame))
                    entry.set_halign(Gtk.Align.START)
                    entry.show()
                    this_frame_info_entries.append(entry)
                    bpa_duration_box.pack_start(entry, False, True, 5)

        resp = dialog.run()
        dialog.hide()

        if resp == ResponseType.OK:
            had_errors = False
            for i, (bpa, bpa_entries) in enumerate(
                    zip(self.parent.bpas, bpas_frame_info_entries)):
                gui_i = i + 1
                if bpa is None and self.parent.builder.get_object(
                        f'bpa_enable{gui_i}').get_active():
                    # HAS TO BE ADDED
                    map_bg_entry = self.parent.module.get_level_entry(
                        self.parent.item_id)
                    # Add file
                    new_bpa_filename = f"{map_bg_entry.bpl_name}{gui_i}"
                    new_bpa_filename_with_ext = new_bpa_filename.lower(
                    ) + BPA_EXT
                    try:
                        self.parent.module.project.create_new_file(
                            f"{DIR}/{new_bpa_filename_with_ext}",
                            FileType.BPA.new(), FileType.BPA)
                    except FileExistsError:
                        # Hm, okay, then we just re-use this file.
                        pass
                    # Add to MapBG list
                    map_bg_entry.bpa_names[i] = new_bpa_filename
                    # Refresh controller state
                    self.parent.bpas = self.parent.module.get_bpas(
                        self.parent.item_id)
                    new_bpa = self.parent.bpas[i]
                    # Add to BPC
                    self.parent.bpc.process_bpa_change(i,
                                                       new_bpa.number_of_tiles)
                    self.parent.module.mark_level_list_as_modified()
                if bpa is not None and not self.parent.builder.get_object(
                        f'bpa_enable{gui_i}').get_active():
                    # HAS TO BE DELETED
                    map_bg_entry = self.parent.module.get_level_entry(
                        self.parent.item_id)
                    # Delete from BPC
                    self.parent.bpc.process_bpa_change(i, 0)
                    # Delete from MapBG list
                    map_bg_entry.bpa_names[i] = None
                    # Refresh controller state
                    self.parent.bpas = self.parent.module.get_bpas(
                        self.parent.item_id)
                    self.parent.module.mark_level_list_as_modified()
                if bpa is not None:
                    new_frame_info = []
                    for entry_i, entry in enumerate(bpa_entries):
                        try:
                            number_of_frames = int(entry.get_text())
                        except ValueError:
                            number_of_frames = 0
                            had_errors = True
                        new_frame_info.append(BpaFrameInfo(
                            number_of_frames, 0))
                    bpa.frame_info = new_frame_info

            if had_errors:
                md = SkyTempleMessageDialog(
                    MainController.window(),
                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
                    Gtk.MessageType.WARNING,
                    Gtk.ButtonsType.OK,
                    "Some values were invalid (not a number). "
                    "They were replaced with 0.",
                    title="Warning!")
                md.set_position(Gtk.WindowPosition.CENTER)
                md.run()
                md.destroy()

            self.parent.reload_all()
            self.parent.mark_as_modified()
예제 #2
0
파일: main.py 프로젝트: Dracauu/skytemple
try:
    gi.require_foreign("cairo")
except ImportError:
    from gi.repository import Gtk

    md = SkyTempleMessageDialog(
        None,
        Gtk.DialogFlags.DESTROY_WITH_PARENT,
        Gtk.MessageType.ERROR,
        Gtk.ButtonsType.OK,
        _("PyGObject compiled without Cairo support. Can't start!"),
        title=_("SkyTemple - Error!"))
    md.set_position(Gtk.WindowPosition.CENTER)
    md.run()
    md.destroy()
    exit(1)

from gi.repository import Gtk, Gdk, GLib
from gi.repository.Gtk import Window
from skytemple.controller.main import MainController
SKYTEMPLE_LOGLEVEL = logging.INFO


def main():
    # TODO: Gtk.Application: https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html
    path = os.path.abspath(os.path.dirname(__file__))

    # Load settings
    settings = SkyTempleSettingsStore()
예제 #3
0
파일: bg_menu.py 프로젝트: retke/skytemple
    def on_men_map_width_height_activate(self):
        dialog: Gtk.Dialog = self.parent.builder.get_object(
            'dialog_width_height')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        # Set dialog settings to map settings
        map_width_chunks: Gtk.Entry = self.parent.builder.get_object(
            'map_width_chunks')
        map_height_chunks: Gtk.Entry = self.parent.builder.get_object(
            'map_height_chunks')
        map_width_tiles: Gtk.Entry = self.parent.builder.get_object(
            'map_width_tiles')
        map_height_tiles: Gtk.Entry = self.parent.builder.get_object(
            'map_height_tiles')
        map_wh_link: Gtk.Switch = self.parent.builder.get_object('map_wh_link')
        map_wh_link_target: Gtk.Switch = self.parent.builder.get_object(
            'map_wh_link_target')
        map_width_chunks.set_text(str(self.parent.bma.map_width_chunks))
        map_height_chunks.set_text(str(self.parent.bma.map_height_chunks))
        map_width_tiles.set_text(str(self.parent.bma.map_width_camera))
        map_height_tiles.set_text(str(self.parent.bma.map_height_camera))
        if self.parent.bma.map_width_camera == self.parent.bma.map_width_chunks * self.parent.bma.tiling_width and \
                self.parent.bma.map_height_camera == self.parent.bma.map_height_chunks * self.parent.bma.tiling_height:
            map_wh_link.set_active(True)
            map_wh_link_target.set_sensitive(False)
        else:
            map_wh_link.set_active(False)
            map_wh_link_target.set_sensitive(True)

        map_width_chunks.connect(
            'changed', partial(on_map_width_chunks_changed,
                               self.parent.builder))
        map_height_chunks.connect(
            'changed',
            partial(on_map_height_chunks_changed, self.parent.builder))
        map_wh_link.connect(
            'state-set', partial(on_map_wh_link_state_set,
                                 self.parent.builder))

        resp = dialog.run()
        if resp == ResponseType.OK:
            try:
                params = (
                    int(map_width_chunks.get_text()),
                    int(map_height_chunks.get_text()),
                    int(map_width_tiles.get_text()),
                    int(map_height_tiles.get_text()),
                )
            except ValueError:
                md = SkyTempleMessageDialog(
                    MainController.window(),
                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
                    Gtk.MessageType.ERROR,
                    Gtk.ButtonsType.OK,
                    "Please only enter numbers for the map size.",
                    title="Error!")
                md.set_position(Gtk.WindowPosition.CENTER)
                md.run()
                md.destroy()
            else:
                self.parent.bma.resize(*params)
                self.parent.reload_all()
                self.parent.mark_as_modified()
        dialog.hide()
예제 #4
0
파일: w16.py 프로젝트: retke/skytemple
    def on_import_clicked(self, *args):
        md = SkyTempleMessageDialog(
            MainController.window(),
            Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            f"To import, select a directory to import from. Files with the pattern 'XX.png'\n"
            f"will be imported, where XX is a number between 0 and 99. The image dimensions must be a multiple of 8.",
            title="Import Images")
        md.run()
        md.destroy()
        dialog = Gtk.FileChooserNative.new("Import images from PNGs...",
                                           MainController.window(),
                                           Gtk.FileChooserAction.SELECT_FOLDER,
                                           None, None)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            r = re.compile(rf"(\d+)\.png", re.IGNORECASE)
            imgs_dict = {
                int(match[1]): name
                for match, name in self._try_match_import(r, os.listdir(fn))
                if match is not None
            }
            imgs = []
            # Convert imgs to list
            for i in range(0, len(imgs_dict)):
                if i not in imgs_dict:
                    display_error(
                        None,
                        f'Failed importing image "{i}":\nImage for number {i} missing.',
                        f"Image for number {i} missing.")
                    return
                imgs.append(imgs_dict[i])
            if len(imgs) == 0:
                display_error(None, f'No images found.', f"No images found.")
                return
            for index, image_fn in enumerate(imgs):
                try:
                    with open(os.path.join(fn, image_fn), 'rb') as f:
                        image = Image.open(f)
                        if len(self.w16) > index:
                            # Existing image, update
                            self.w16[index].set(image)
                        else:
                            self.w16.append(
                                W16AtImage.new(W16TocEntry(0, 0, 0, 0), image))
                except Exception as err:
                    logger.error(f"Failed importing image '{index}'.",
                                 exc_info=err)
                    display_error(sys.exc_info(),
                                  f'Failed importing image "{index}":\n{err}',
                                  f"Error for '{index}'.")
            # Re-render
            self._reset()
            for draw in self._draws:
                draw.queue_draw()
            # Mark as modified
            self.module.mark_w16_as_modified(self.item_id)
예제 #5
0
    def on_fix_dungeons_clicked(self, *args):
        dialog: Gtk.Dialog = self.builder.get_object(
            'dialog_fix_dungeon_errors')
        dialog.set_attached_to(SkyTempleMainController.window())
        dialog.set_transient_for(SkyTempleMainController.window())
        dialog.resize(900, 520)

        dungeon_list = self.module.get_dungeon_list()
        validator = self.module.get_validator()
        validator.validate(dungeon_list)
        store: Gtk.Store = self.builder.get_object('store_dungeon_errors')
        store.clear()
        validator.errors.sort(key=lambda e: e.dungeon_id)
        for e in validator.errors:
            if not isinstance(e, DungeonTotalFloorCountInvalidError):
                if isinstance(e, FloorReusedError):
                    i = e.reused_of_dungeon_with_id
                    e.reused_of_dungeon_name = f'{"dungeon"} {i} ({self.module.project.get_string_provider().get_value(StringType.DUNGEON_NAMES_MAIN, i)})'
                dungeon_name = f'{e.dungeon_id}: {self.module.project.get_string_provider().get_value(StringType.DUNGEON_NAMES_MAIN, e.dungeon_id)}'
                store.append([
                    True,  # selected
                    dungeon_name,  # dungeon_name
                    e.dungeon_id,  # dungeon_id
                    textwrap.fill(e.name, 40),  # error_name
                    textwrap.fill(str(e), 40),  # error_description
                    textwrap.fill(self._get_solution_text(dungeon_list, e),
                                  40),  # solution
                    e,  # error
                ])

        resp = dialog.run()
        dialog.hide()
        if resp == Gtk.ResponseType.APPLY:
            # Step 1, fix all selected errors
            for row in store:
                if row[0]:  # selected
                    self._fix_error(dungeon_list, row[6])
            # Step 2, fix all open DungeonTotalFloorCountInvalidError
            validator.validate(dungeon_list)
            for error in validator.errors:
                if isinstance(error, DungeonTotalFloorCountInvalidError):
                    self._fix_error(dungeon_list, error)
            # Step 3 report status
            if not validator.validate(dungeon_list):
                md = SkyTempleMessageDialog(
                    MainSkyTempleController.window(),
                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
                    Gtk.MessageType.WARNING,
                    Gtk.ButtonsType.OK,
                    _("Dungeon Errors were fixed.\nHowever there are still errors left. "
                      "Re-open the dialog to fix the rest. If they are still not fixed, please report a bug!"
                      ),
                    title=_("Fix Dungeon Errors"))
            else:
                md = SkyTempleMessageDialog(
                    MainSkyTempleController.window(),
                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
                    Gtk.MessageType.INFO,
                    Gtk.ButtonsType.OK,
                    _("Dungeon Errors were successfully fixed."),
                    title=_("Fix Dungeon Errors"),
                    is_success=True)
            md.run()
            md.destroy()
            self.module.save_dungeon_list(dungeon_list)
            self.module.save_mappa()
            self.module.mark_root_as_modified()
            self.module.rebuild_dungeon_tree()
예제 #6
0
파일: monster.py 프로젝트: retke/skytemple
    def on_btn_export_clicked(self, *args):
        dialog: Gtk.Dialog = self.builder.get_object('export_dialog')
        dialog.resize(640, 560)
        dialog.set_attached_to(SkyTempleMainController.window())
        dialog.set_transient_for(SkyTempleMainController.window())

        # Fill Pokémon tree
        store: Gtk.TreeStore = self.builder.get_object('export_dialog_store')
        store.clear()
        monster_entries_by_base_id: Dict[int, List[MdEntry]] = {}
        for entry in self.module.monster_md.entries:
            if entry.md_index_base not in monster_entries_by_base_id:
                monster_entries_by_base_id[entry.md_index_base] = []
            monster_entries_by_base_id[entry.md_index_base].append(entry)

        for baseid, entry_list in monster_entries_by_base_id.items():
            name = self.module.project.get_string_provider().get_value(
                StringType.POKEMON_NAMES, baseid)
            entry_main_tree = self.module.generate_entry__entity_root(
                baseid, name)
            ent_root = store.append(None, [
                -1, -1, False, entry_main_tree[0], entry_main_tree[1], False,
                False
            ])

            for entry in entry_list:
                entry_main_tree = self.module.generate_entry__entry(
                    entry.md_index, entry.gender)
                store.append(ent_root, [
                    entry_main_tree[4], -1, True, entry_main_tree[0],
                    entry_main_tree[1], False, False
                ])

        names, md_gender1, md_gender2, moveset, moveset2, stats, portraits, portraits2 = self.module.get_export_data(
            self.entry)
        we_are_gender1 = md_gender1 == self.entry

        if md_gender2 is None:
            sw: Gtk.Switch = self.builder.get_object(
                'export_type_other_gender')
            sw.set_active(False)
            sw.set_sensitive(False)

        if portraits is None:
            sw: Gtk.Switch = self.builder.get_object(
                'export_type_portraits_current_gender')
            sw.set_active(False)
            sw.set_sensitive(False)

        if portraits2 is None:
            sw: Gtk.Switch = self.builder.get_object(
                'export_type_portraits_other_gender')
            sw.set_active(False)
            sw.set_sensitive(False)

        if stats is None:
            sw: Gtk.Switch = self.builder.get_object('export_type_stats')
            sw.set_active(False)
            sw.set_sensitive(False)

        if moveset is None:
            sw: Gtk.Switch = self.builder.get_object('export_type_moveset1')
            sw.set_active(False)
            sw.set_sensitive(False)

        if moveset2 is None:
            sw: Gtk.Switch = self.builder.get_object('export_type_moveset2')
            sw.set_active(False)
            sw.set_sensitive(False)

        resp = dialog.run()
        dialog.hide()

        if resp == Gtk.ResponseType.APPLY:
            # Create output XML

            if not self.builder.get_object(
                    'export_type_current_gender').get_active():
                if we_are_gender1:
                    md_gender1 = None
                else:
                    md_gender2 = None
            if not self.builder.get_object(
                    'export_type_other_gender').get_active():
                if not we_are_gender1:
                    md_gender1 = None
                else:
                    md_gender2 = None
            if not self.builder.get_object('export_type_names').get_active():
                names = None
            if not self.builder.get_object('export_type_stats').get_active():
                stats = None
            if not self.builder.get_object(
                    'export_type_moveset1').get_active():
                moveset = None
            if not self.builder.get_object(
                    'export_type_moveset2').get_active():
                moveset2 = None
            if not self.builder.get_object(
                    'export_type_portraits_current_gender').get_active():
                if we_are_gender1:
                    portraits = None
                else:
                    portraits2 = None
            if not self.builder.get_object(
                    'export_type_portraits_other_gender').get_active():
                if not we_are_gender1:
                    portraits = None
                else:
                    portraits2 = None

            xml = monster_xml_export(
                self.module.project.get_rom_module().get_static_data(
                ).game_version, md_gender1, md_gender2, names, moveset,
                moveset2, stats, portraits, portraits2)

            # 1. Export to file
            if self.builder.get_object('export_file_switch').get_active():
                save_diag = Gtk.FileChooserNative.new(
                    "Export Pokémon as...", SkyTempleMainController.window(),
                    Gtk.FileChooserAction.SAVE, None, None)

                add_dialog_xml_filter(save_diag)
                response = save_diag.run()
                fn = save_diag.get_filename()
                if '.' not in fn:
                    fn += '.xml'
                save_diag.destroy()

                if response == Gtk.ResponseType.ACCEPT:
                    with open(fn, 'w') as f:
                        f.write(prettify(xml))
                else:
                    md = SkyTempleMessageDialog(
                        SkyTempleMainController.window(),
                        Gtk.DialogFlags.DESTROY_WITH_PARENT,
                        Gtk.MessageType.WARNING, Gtk.ButtonsType.OK,
                        "Export was canceled.")
                    md.set_position(Gtk.WindowPosition.CENTER)
                    md.run()
                    md.destroy()
                    return

            # 2. Import to selected Pokémon
            selected_monsters: List[int] = []

            def collect_monsters_recurse(titer: Optional[Gtk.TreeIter]):
                for i in range(store.iter_n_children(titer)):
                    child = store.iter_nth_child(titer, i)
                    if store[child][2] and store[child][
                            5]:  # is floor and is selected
                        selected_monsters.append(store[child][0])
                    collect_monsters_recurse(child)

            collect_monsters_recurse(None)
            self.module.import_from_xml(selected_monsters, xml)
예제 #7
0
    def run(self):
        """
        Shows the settings dialog and processes settings changes. Doesn't return anything.
        """
        gtk_settings = Gtk.Settings.get_default()

        # Discord enabled state
        discord_enabled_previous = self.settings.get_integration_discord_enabled(
        )
        settings_discord_enable = self.builder.get_object(
            'setting_discord_enable')
        settings_discord_enable.set_active(discord_enabled_previous)

        # Gtk Theme
        if not sys.platform.startswith('linux'):
            store: Gtk.ListStore = Gtk.ListStore.new([str])
            cb: Gtk.ComboBox = self.builder.get_object('setting_gtk_theme')
            active = None
            for id, theme in enumerate(self._list_gtk_themes()):
                store.append([theme])
                if theme == gtk_settings.get_property("gtk-theme-name"):
                    active = id
            cb.set_model(store)
            if active is not None:
                cb.set_active(active)
        else:
            self.builder.get_object('frame_setting_gtk_theme').hide()

        # Languages
        cb: Gtk.ComboBox = self.builder.get_object('setting_language')
        store: Gtk.ListStore = self.builder.get_object('lang_store')
        store.clear()
        active = None
        for id, (code, name) in enumerate(LANGS):
            store.append([code, name])
            if code == self.settings.get_locale():
                active = id
        if active is not None:
            cb.set_active(active)

        response = self.window.run()

        have_to_restart = False
        if response == Gtk.ResponseType.ACCEPT:

            # Discord enabled state
            discord_enabled = settings_discord_enable.get_active()
            if discord_enabled != discord_enabled_previous:
                self.settings.set_integration_discord_enabled(discord_enabled)
                have_to_restart = True

            # Gtk Theme
            if not sys.platform.startswith('linux'):
                cb: Gtk.ComboBox = self.builder.get_object('setting_gtk_theme')
                theme_name = cb.get_model()[cb.get_active_iter()][0]
                gtk_settings.set_property("gtk-theme-name", theme_name)
                self.settings.set_gtk_theme(theme_name)

            # Languages
            cb: Gtk.ComboBox = self.builder.get_object('setting_language')
            lang_name = cb.get_model()[cb.get_active_iter()][0]
            before = self.settings.get_locale()
            if before != lang_name:
                self.settings.set_locale(lang_name)
                have_to_restart = True

        self.window.hide()

        if have_to_restart:
            md = SkyTempleMessageDialog(self.parent_window,
                                        Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                        Gtk.MessageType.INFO,
                                        Gtk.ButtonsType.OK,
                                        _("You need to restart SkyTemple to "
                                          "apply some of the settings."),
                                        title="SkyTemple")
            md.run()
            md.destroy()
예제 #8
0
    def convert(self, *args):

        mode_cb: Gtk.ComboBox = self.builder.get_object('tq_mode')
        mode = ImageConversionMode(
            mode_cb.get_model()[mode_cb.get_active_iter()][0])
        dither_level = self.builder.get_object('tq_dither_level').get_value()
        has_first_image = self.builder.get_object(
            'tq_input_file').get_filename() is not None
        has_second_image = self.builder.get_object(
            'tq_second_file').get_filename() is not None

        if not has_first_image:
            self.error(_("Please select an input image."))
            return
        if has_second_image:
            md = SkyTempleMessageDialog(
                self.window, Gtk.DialogFlags.DESTROY_WITH_PARENT,
                Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
                _("Since you selected two images to convert, you will be "
                  "asked for both images where to save them to."))
            md.run()
            md.destroy()

        dialog = Gtk.FileChooserNative.new(_("Save first image as (PNG)..."),
                                           self.window,
                                           Gtk.FileChooserAction.SAVE, None,
                                           None)
        if self._previous_output_image is not None:
            dialog.set_filename(self._previous_output_image)

        add_dialog_png_filter(dialog)

        response = dialog.run()
        output_image = dialog.get_filename()
        if output_image and '.' not in output_image:
            output_image += '.png'
        self._previous_output_image = output_image
        dialog.destroy()
        if response != Gtk.ResponseType.ACCEPT:
            return

        if has_second_image:
            dialog = Gtk.FileChooserNative.new(
                _("Save second image as (PNG)..."), self.window,
                Gtk.FileChooserAction.SAVE, None, None)
            if self._previous_second_output_image is not None:
                dialog.set_filename(self._previous_second_output_image)
            else:
                dialog.set_filename(output_image)

            add_dialog_png_filter(dialog)

            response = dialog.run()
            second_output_image = dialog.get_filename()
            if '.' not in second_output_image:
                second_output_image += '.png'
            self._previous_second_output_image = second_output_image
            dialog.destroy()
            if response != Gtk.ResponseType.ACCEPT:
                return

        try:
            num_pals = int(
                self.builder.get_object('tq_number_palettes').get_text())
            input_image = self.builder.get_object(
                'tq_input_file').get_filename()
            second_input_file = self.builder.get_object(
                'tq_second_file').get_filename()
            transparent_color = self.builder.get_object(
                'tq_transparent_color').get_color()
            transparent_color = (int(transparent_color.red_float * 255),
                                 int(transparent_color.green_float * 255),
                                 int(transparent_color.blue_float * 255))
        except ValueError:
            self.error(_("You entered invalid numbers."))
        else:
            if not os.path.exists(input_image):
                self.error(_("The input image does not exist."))
                return
            if has_second_image and not os.path.exists(second_input_file):
                self.error(_("The second input image does not exist."))
                return
            with open(input_image, 'rb') as input_file:
                try:
                    if not has_second_image:
                        # Only one image
                        image = Image.open(input_file)
                    else:
                        # Two images: Merge them.
                        image1 = Image.open(input_file)
                        image2 = Image.open(second_input_file)
                        image = Image.new('RGBA',
                                          (max(image1.width, image2.width),
                                           image1.height + image2.height),
                                          transparent_color)
                        image.paste(image1, (0, 0))
                        image.paste(image2, (0, image1.height))
                except OSError:
                    self.error(_("The input image is not a supported format."))
                    return
                try:
                    img = self._convert(image, transparent_color, mode,
                                        num_pals, dither_level)
                    if not has_second_image:
                        # Only one image
                        img.save(output_image)
                    else:
                        # Two images: Un-merge them.
                        img.crop((0, 0, image1.width,
                                  image1.height)).save(output_image)
                        img.crop(
                            (0, image1.height, image2.width, image1.height +
                             image2.height)).save(second_output_image)
                except BaseException as err:
                    logger.error("Tilequant error.", exc_info=err)
                    self.error(str(err))
                else:
                    md = SkyTempleMessageDialog(
                        self.window,
                        Gtk.DialogFlags.DESTROY_WITH_PARENT,
                        Gtk.MessageType.INFO,
                        Gtk.ButtonsType.OK,
                        _("Image was converted."),
                        is_success=True)
                    md.run()
                    md.destroy()
예제 #9
0
    def run(self):
        """
        Shows the settings dialog and processes settings changes. Doesn't return anything.
        """
        gtk_settings = Gtk.Settings.get_default()

        # Discord enabled state
        discord_enabled_previous = self.settings.get_integration_discord_enabled(
        )
        settings_discord_enable = self.builder.get_object(
            'setting_discord_enable')
        settings_discord_enable.set_active(discord_enabled_previous)

        # Gtk Theme
        if not sys.platform.startswith('linux'):
            store: Gtk.ListStore = Gtk.ListStore.new([str])
            cb: Gtk.ComboBox = self.builder.get_object('setting_gtk_theme')
            active = None
            for id, theme in enumerate(self._list_gtk_themes()):
                store.append([theme])
                if theme == gtk_settings.get_property("gtk-theme-name"):
                    active = id
            cb.set_model(store)
            if active is not None:
                cb.set_active(active)
        else:
            gbox: Gtk.Box = self.builder.get_object('setting_gtk_theme_box')
            for child in gbox:
                gbox.remove(child)
            label = Gtk.Label()
            label.set_markup(
                _("<i>Use System Settings to set this under Linux.</i>"))
            gbox.pack_start(label, False, False, 0)
            label.show()

        # Languages
        cb: Gtk.ComboBox = self.builder.get_object('setting_language')
        store: Gtk.ListStore = self.builder.get_object('lang_store')
        store.clear()
        active = None
        for id, (code, name) in enumerate(LANGS):
            store.append([code, name])
            if code == self.settings.get_locale():
                active = id
        if active is not None:
            cb.set_active(active)

        # Native file handler
        native_impl_enabled_previous = self.settings.get_implementation_type(
        ) == ImplementationType.NATIVE
        settings_native_enable = self.builder.get_object(
            'setting_native_enable')
        settings_native_enable.set_active(native_impl_enabled_previous)

        # Async modes
        cb = self.builder.get_object('setting_async')
        store = self.builder.get_object('async_store')
        store.clear()
        active = None
        for mode in AsyncConfiguration:
            if mode.available():
                store.append([mode.value, mode.name_localized])
                if mode == self.settings.get_async_configuration():
                    active = mode.value
        if active is not None:
            cb.set_active_id(active)

        # Sentry
        allow_sentry_previous = self.settings.get_allow_sentry()
        settings_allow_sentry_enable = self.builder.get_object(
            'setting_allow_sentry')
        settings_allow_sentry_enable.set_active(allow_sentry_previous)

        # CSD
        csd_before = self.settings.csd_enabled()
        settings_csd_enable = self.builder.get_object('setting_csd_enable')
        settings_csd_enable.set_active(csd_before)

        response = self.window.run()

        have_to_restart = False
        if response == Gtk.ResponseType.ACCEPT:

            # Discord enabled state
            discord_enabled = settings_discord_enable.get_active()
            if discord_enabled != discord_enabled_previous:
                self.settings.set_integration_discord_enabled(discord_enabled)
                have_to_restart = True

            # Gtk Theme
            if not sys.platform.startswith('linux'):
                cb = self.builder.get_object('setting_gtk_theme')
                iter = cb.get_active_iter()
                if iter is not None:
                    theme_name = cb.get_model()[iter][0]
                    gtk_settings.set_property("gtk-theme-name", theme_name)
                    self.settings.set_gtk_theme(theme_name)

            # Languages
            cb = self.builder.get_object('setting_language')
            lang_name = cb.get_model()[cb.get_active_iter()][0]
            before = self.settings.get_locale()
            if before != lang_name:
                self.settings.set_locale(lang_name)
                have_to_restart = True

            # Native file handler enabled state
            native_impl_enabled = settings_native_enable.get_active()
            if native_impl_enabled != native_impl_enabled_previous:
                self.settings.set_implementation_type(
                    ImplementationType.NATIVE
                    if native_impl_enabled else ImplementationType.PYTHON)
                have_to_restart = True

            # Async modes
            cb = self.builder.get_object('setting_async')
            async_mode = AsyncConfiguration(
                cb.get_model()[cb.get_active_iter()][0])
            before_async = self.settings.get_async_configuration()
            if before_async != async_mode:
                self.settings.set_async_configuration(async_mode)
                have_to_restart = True

            # Sentry
            allow_sentry_enabled = settings_allow_sentry_enable.get_active()
            if allow_sentry_enabled != allow_sentry_previous:
                self.settings.set_allow_sentry(allow_sentry_enabled)
                if not allow_sentry_enabled:
                    have_to_restart = True
                else:
                    from skytemple.core import sentry
                    sentry.init()

            # CSD
            csd_new = settings_csd_enable.get_active()
            before_csd = self.settings.csd_enabled()
            if before_csd != csd_new:
                self.settings.set_csd_enabled(csd_new)
                have_to_restart = True

        self.window.hide()

        if have_to_restart:
            md = SkyTempleMessageDialog(self.parent_window,
                                        Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                        Gtk.MessageType.INFO,
                                        Gtk.ButtonsType.OK,
                                        _("You need to restart SkyTemple to "
                                          "apply some of the settings."),
                                        title="SkyTemple")
            md.run()
            md.destroy()
예제 #10
0
    def on_btn_apply_clicked(self, *args):
        tree: Gtk.TreeView = self.builder.get_object('patch_tree')
        model, treeiter = tree.get_selection().get_selected()
        if model is not None and treeiter is not None:
            name = model[treeiter][0]
            try:
                if self._patcher.is_applied(name):
                    md = SkyTempleMessageDialog(MainAppController.window(),
                                                Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING,
                                                Gtk.ButtonsType.OK_CANCEL, _("This patch is already applied. "
                                                                             "Some patches support applying them again, "
                                                                             "but you might also run into problems with some. "
                                                                             "Proceed with care."))
                    md.set_position(Gtk.WindowPosition.CENTER)
                    response = md.run()
                    md.destroy()
                    if response != Gtk.ResponseType.OK:
                        return
            except NotImplementedError:
                self._error(_("The current ROM is not supported by this patch."))
                return

            if self.module.project.has_modifications():
                self._error(_("Please save the ROM before applying the patch."))
                return

            if name == 'ExpandPokeList':
                    md = SkyTempleMessageDialog(MainAppController.window(),
                                                Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING,
                                                Gtk.ButtonsType.YES_NO,
                                                _("This patch extends the Pokémon list. It is very experimental and "
                                                  "WILL break a few things. Once applied you can not remove it again. "
                                                  "Proceed?"))
                    md.set_position(Gtk.WindowPosition.CENTER)
                    response = md.run()
                    md.destroy()
                    if response != Gtk.ResponseType.YES:
                        return
            some_skipped = False
            try:
                dependencies = self._get_dependencies(name)
                if len(dependencies) > 0:
                    md = SkyTempleMessageDialog(MainAppController.window(),
                                                Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO,
                                                Gtk.ButtonsType.YES_NO,
                                                _("This patch requires some other patches to be applied first:\n") + '\n'.join(dependencies) + _("\nDo you want to apply these first?"))
                    md.set_position(Gtk.WindowPosition.CENTER)
                    response = md.run()
                    md.destroy()
                    if response != Gtk.ResponseType.YES:
                        return
                for patch in dependencies + [name]:
                    try:
                        self._apply(patch)
                    except PatchCanceledError:
                        some_skipped = True
                        break
            except PatchNotConfiguredError as ex:
                err = str(ex)
                if ex.config_parameter != "*":
                    err += _('\nConfiguration field with errors: "{}"\nError: {}').format(ex.config_parameter, ex.error)
                self._error(f(_("Error applying the patch:\n{err}")), exc_info=sys.exc_info(), should_report=True)
            except RuntimeError as err:
                self._error(f(_("Error applying the patch:\n{err}")), exc_info=sys.exc_info(), should_report=True)
            else:
                if not some_skipped:
                    self._error(_("Patch was successfully applied. The ROM will now be reloaded."),
                                Gtk.MessageType.INFO, is_success=True)
                else:
                    self._error(_("Not all patches were applied successfully. The ROM will now be reloaded."),
                                Gtk.MessageType.INFO)
            finally:
                self.module.mark_asm_patches_as_modified()
                self.refresh(self._current_tab)
                MainSkyTempleController.save(lambda: MainSkyTempleController.reload_project())