First implementation of lazy-loading tab groups

This commit is contained in:
Luke Hubmayer-Werner 2023-07-22 22:15:55 +09:30
parent 44d54b0939
commit 55e8802835
1 changed files with 223 additions and 142 deletions

View File

@ -18,8 +18,9 @@
import sys import sys
import re import re
from struct import unpack
import time import time
from functools import cached_property
from struct import unpack
from typing import Iterable from typing import Iterable
from includes.helpers import * from includes.helpers import *
@ -33,6 +34,7 @@ from includes.snestile import (
) )
from includes.snes import * from includes.snes import *
import includes.ff5.const as const import includes.ff5.const as const
from includes.ff5.files import ROM_RPGe, ROM_SNES
from includes.ff5.strings import StringBlock, RPGe_Dialogue_Width from includes.ff5.strings import StringBlock, RPGe_Dialogue_Width
from includes.ff5.strings import Strings as FFVStrings from includes.ff5.strings import Strings as FFVStrings
import includes.ff5.structs as FFVStructs import includes.ff5.structs as FFVStructs
@ -49,102 +51,122 @@ class FF5Reader(QMainWindow):
''' '''
Main GUI class Main GUI class
''' '''
def __init__(self):
QMainWindow.__init__(self, None)
perfcount()
print('Reading ROMs')
ROM_en = ff5.files.ROM_RPGe
ROM_jp = ff5.files.ROM_SNES
ROM_FF4jp = load_raw(filename_jp_ff4)
ROM_FF6jp = load_raw(filename_jp_ff6)
print(len(ROM_FF4jp), filename_jp_ff4)
print(len(ROM_FF6jp), filename_jp_ff6)
perfcount()
print('Generating Glyphs') @cached_property
self.glyph_sprites = { def battle_strips_ff4(self):
'glyphs_en_s': generate_glyphs(ROM_en, 0x11F000), return ff4.make_character_battle_sprites(self.ROM_FF4jp)
'glyphs_en_l': generate_glyphs_large(ROM_en, 0x03E800),
'glyphs_jp_s': generate_glyphs(ROM_jp, 0x11F000), @cached_property
'glyphs_jp_l': generate_glyphs_large(ROM_jp, 0x03E800), def field_strips_ff4(self):
'glyphs_kanji': generate_glyphs_large(ROM_jp, 0x1BD000, 0x1AA), # Kanji are unchanged in EN version return ff4.make_character_field_sprites(self.ROM_FF4jp)
@cached_property
def portraits_ff4(self):
return ff4.make_character_portrait_sprites(self.ROM_FF4jp)
@cached_property
def battle_strips_ff6(self):
return ff6.make_character_battle_sprites(self.ROM_FF6jp)
@cached_property
def portraits_ff6(self):
return ff6.make_character_portrait_sprites(self.ROM_FF6jp)
@cached_property
def battle_bgs(self):
return make_battle_backgrounds(ROM_SNES)
@cached_property
def battle_strips(self):
return make_character_battle_sprites(ROM_RPGe)
@cached_property
def status_strips(self):
return make_character_status_sprites(ROM_RPGe)
@cached_property
def enemy_sprites(self):
return make_enemy_sprites(ROM_RPGe)
@cached_property
def enemy_sprite_data(self):
return FFVStructs.EnemySprite.get_data(ROM_RPGe)
@cached_property
def enemy_sprites_named(self):
return [stack_labels(s, d[-2]) for s, d in zip(self.enemy_sprites, self.enemy_sprite_data)]
@cached_property
def field_tiles(self):
return make_all_field_tiles(ROM_SNES)
@cached_property
def field_minitiles(self):
return make_all_field_minitiles(ROM_SNES)
@cached_property
def fieldmap_tiles(self):
st_field_tiles = [stitch_tileset(ts) for ts in self.field_tiles]
st_field_minitiles = [stitch_tileset(ts) for ts in self.field_minitiles]
return [make_field_map_tile_pixmap(ROM_SNES, i, st_field_tiles, st_field_minitiles) for i in range(const.zone_count)]
@cached_property
def glyph_sprites(self):
return {
'glyphs_en_s': generate_glyphs(ROM_RPGe, 0x11F000),
'glyphs_en_l': generate_glyphs_large(ROM_RPGe, 0x03E800),
'glyphs_jp_s': generate_glyphs(ROM_SNES, 0x11F000),
'glyphs_jp_l': generate_glyphs_large(ROM_SNES, 0x03E800),
'glyphs_kanji': generate_glyphs_large(ROM_SNES, 0x1BD000, 0x1AA), # Kanji are unchanged in EN version
} }
perfcount()
imglist_headers = ['ID', 'EN Pointer', 'EN Address', 'EN String', 'EN Img', 'JP Pointer', 'JP Address', 'JP String', 'JP Img'] @cached_property
def worldmap_palettes(self):
return [generate_palette(ROM_SNES, 0x0FFCC0+(i*0x100), length=0x160, transparent=True) for i in range(3)]
print('Generating String Images') @cached_property
string_images = {k: _make_string_img_list(*FFVStrings.blocks_SNES_RPGe[k], large=config.get('dialog'), **self.glyph_sprites) for k,config in FFVStrings.config.items()} def worldmap_tiles(self):
ends_in_digit = re.compile('^([\w_]+)(\d+)') return make_worldmap_tiles_pixmap(ROM_SNES, 0x1B8000, 0x0FF9C0, 0x0FFCC0) + \
for k in sorted(list(string_images.keys())): # Pre-generate keys as we destructively iterate the dict make_worldmap_tiles_pixmap(ROM_SNES, 0x1BA000, 0x0FFAC0, 0x0FFDC0) + \
if m := ends_in_digit.match(k): make_worldmap_tiles_pixmap(ROM_SNES, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
k0 = m[1]
n = int(m[2])
print(f'Collapsing strings list {k} into {k0}')
string_images[k0] += string_images.pop(k)
perfcount()
@cached_property
def world_tiles(self):
return [make_worldmap_blocks(ROM_SNES, 0x0FF0C0+(i*0x300), 0x1B8000+(i*0x2000), 0x0FF9C0+(i*0x100)) for i in range(3)]
tileset_headers = ("ID", "Offset", "Pointer", "Expected Length") @cached_property
tileset_data = [] def world_blocks_pixmaps(self):
for i in range(0x1C): output = []
offset = 0x0F0000 + (i*2) for i, tiles in enumerate(self.world_tiles):
pointer = 0x0F0000 + indirect(ROM_en, offset)
length = indirect(ROM_en, offset+2) - indirect(ROM_en, offset)
tileset_data.append((hex(i, 2), hex(offset, 6), hex(pointer, 6), hex(length, 4)))
npc_layers = []
offset = 0x0E59C0
for layer in range(const.npc_layer_count):
i = offset + (layer*2)
start = indirect(ROM_en, i) + offset
next = indirect(ROM_en, i+2) + offset
npcs = (next - start) // 7
for npc in range(npcs):
address = start + (npc*7)
npc_layers.append([hex(i, 6), hex(layer, 3)] + parse_struct(ROM_en, address, const.npc_layer_structure))
perfcount()
print('Generating map tiles')
worldmap_palettes = [generate_palette(ROM_jp, 0x0FFCC0+(i*0x100), length=0x160, transparent=True) for i in range(3)]
world_tiles = [make_worldmap_blocks(ROM_jp, 0x0FF0C0+(i*0x300), 0x1B8000+(i*0x2000), 0x0FF9C0+(i*0x100)) for i in range(3)]
#worldpixmaps = [make_worldmap_pixmap(ROM_jp, i, 0x0FFCC0+(t*0x100), world_tiles[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
world_blocks_pixmaps = []
for i, tiles in enumerate(world_tiles):
a = [] a = []
for t in tiles: for t in tiles:
t.setColorTable(worldmap_palettes[i]) t.setColorTable(self.worldmap_palettes[i])
a.append(QPixmap.fromImage(t)) a.append(QPixmap.fromImage(t))
world_blocks_pixmaps.append(a) output.append(a)
world_tile_stitches = [stitch_tileset_px(t) for t in world_blocks_pixmaps] return output
worldpixmaps = [make_worldmap_pixmap2(ROM_jp, i, world_tile_stitches[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
perfcount()
worldmap_tiles = make_worldmap_tiles_pixmap(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0)
worldmap_tiles += make_worldmap_tiles_pixmap(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0)
worldmap_tiles += make_worldmap_tiles_pixmap(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
perfcount()
field_tiles = make_all_field_tiles(ROM_jp) @cached_property
field_minitiles = make_all_field_minitiles(ROM_jp) def worldpixmaps(self):
perfcount() world_tile_stitches = [stitch_tileset_px(t) for t in self.world_blocks_pixmaps]
st_field_tiles = [stitch_tileset(ts) for ts in field_tiles] return [make_worldmap_pixmap2(ROM_SNES, i, world_tile_stitches[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
st_field_minitiles = [stitch_tileset(ts) for ts in field_minitiles] #return [make_worldmap_pixmap(ROM_SNES, i, 0x0FFCC0+(t*0x100), self.world_tiles[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
perfcount()
fieldmap_tiles = [make_field_map_tile_pixmap(ROM_jp, i, st_field_tiles, st_field_minitiles) for i in range(const.zone_count)] @cached_property
def zone_pxs_and_field_blocks(self):
perfcount() perfcount()
print('Generating field map blocks') print('Generating field map blocks')
zones = [parse_zone(ROM_jp, i) for i in range(const.zone_count)] zones = [parse_zone(ROM_SNES, i) for i in range(const.zone_count)]
field_blocksets = [get_field_map_block_layouts(ROM_jp, i) for i in range(28)] field_blocksets = [get_field_map_block_layouts(ROM_SNES, i) for i in range(28)]
perfcount() perfcount()
blockmaps = get_blockmaps(ROM_jp)
blockmaps = get_blockmaps(ROM_SNES)
field_blocks = [] field_blocks = []
zone_pxs = [] zone_pxs = []
block_cache = {'misses': 0, 'p_hits': 0, 'i_hits': 0} block_cache = {'misses': 0, 'p_hits': 0, 'i_hits': 0}
zone_px_cache = {'misses': 0, 'hits': 0} zone_px_cache = {'misses': 0, 'hits': 0}
fm_blocks = [make_field_map_blocks_px(ROM_jp, z, field_tiles, field_minitiles, field_blocksets, block_cache) for z in zones] fm_blocks = [make_field_map_blocks_px(ROM_SNES, z, self.field_tiles, self.field_minitiles, field_blocksets, block_cache) for z in zones]
#fm_blocks = [make_field_map_blocks_px2(ROM_jp, z, field_tiles, field_minitiles, field_blocksets, block_cache) for z in zones] #fm_blocks = [make_field_map_blocks_px2(ROM_SNES, z, self.field_tiles, self.field_minitiles, field_blocksets, block_cache) for z in zones]
print('Block cache results: {misses} misses, {p_hits} full hits, {i_hits} palette misses'.format(**block_cache)) print('Block cache results: {misses} misses, {p_hits} full hits, {i_hits} palette misses'.format(**block_cache))
perfcount() perfcount()
for i, z in enumerate(zones): for i, z in enumerate(zones):
@ -156,81 +178,70 @@ class FF5Reader(QMainWindow):
perfcount() perfcount()
del block_cache del block_cache
del zone_px_cache del zone_px_cache
return zone_pxs, field_blocks
print('Generating Battle backgrounds') @cached_property
battle_bgs = make_battle_backgrounds(ROM_jp) def zone_pxs(self):
return self.zone_pxs_and_field_blocks[0]
@cached_property
def field_blocks(self):
return self.zone_pxs_and_field_blocks[1]
def __init__(self):
QMainWindow.__init__(self, None)
perfcount() perfcount()
print('Generating other sprites') print('Reading ROMs')
self.battle_strips = make_character_battle_sprites(ROM_en) self.ROM_FF4jp = load_raw(filename_jp_ff4)
status_strips = make_character_status_sprites(ROM_en) self.ROM_FF6jp = load_raw(filename_jp_ff6)
enemy_sprites = make_enemy_sprites(ROM_en) print(len(self.ROM_FF4jp), filename_jp_ff4)
enemy_sprite_data = FFVStructs.EnemySprite.get_data(ROM_en) print(len(self.ROM_FF6jp), filename_jp_ff6)
enemy_sprites_named = [stack_labels(s, d[-2]) for s, d in zip(enemy_sprites, enemy_sprite_data)]
perfcount() perfcount()
print('Generating FF4 and FF6 stuff') print('Generating String Images')
self.battle_strips_ff4 = ff4.make_character_battle_sprites(ROM_FF4jp) imglist_headers = ['ID', 'EN Pointer', 'EN Address', 'EN String', 'EN Img', 'JP Pointer', 'JP Address', 'JP String', 'JP Img']
self.field_strips_ff4 = ff4.make_character_field_sprites(ROM_FF4jp) string_images = {k: _make_string_img_list(*FFVStrings.blocks_SNES_RPGe[k], large=config.get('dialog'), **self.glyph_sprites) for k,config in FFVStrings.config.items()}
self.portraits_ff4 = ff4.make_character_portrait_sprites(ROM_FF4jp) ends_in_digit = re.compile('^([\w_]+)(\d+)')
self.battle_strips_ff6 = ff6.make_character_battle_sprites(ROM_FF6jp) for k in sorted(list(string_images.keys())): # Pre-generate keys as we destructively iterate the dict
self.portraits_ff6 = ff6.make_character_portrait_sprites(ROM_FF6jp) if m := ends_in_digit.match(k):
k0 = m[1]
n = int(m[2])
print(f'Collapsing strings list {k} into {k0}')
string_images[k0] += string_images.pop(k)
perfcount() perfcount()
print('Generating Tileset and NPC Layer offsets')
tileset_headers = ("ID", "Offset", "Pointer", "Expected Length")
tileset_data = []
for i in range(0x1C):
offset = 0x0F0000 + (i*2)
pointer = 0x0F0000 + indirect(ROM_RPGe, offset)
length = indirect(ROM_RPGe, offset+2) - indirect(ROM_RPGe, offset)
tileset_data.append((hex(i, 2), hex(offset, 6), hex(pointer, 6), hex(length, 4)))
npc_layers = []
offset = 0x0E59C0
for layer in range(const.npc_layer_count):
i = offset + (layer*2)
start = indirect(ROM_RPGe, i) + offset
next = indirect(ROM_RPGe, i+2) + offset
npcs = (next - start) // 7
for npc in range(npcs):
address = start + (npc*7)
npc_layers.append([hex(i, 6), hex(layer, 3)] + parse_struct(ROM_RPGe, address, const.npc_layer_structure))
perfcount()
print('Creating Qt Widgets') print('Creating Qt Widgets')
self.gamewidget = QTabWidget() self.gamewidget = QTabWidget()
self.ff4widget = QTabWidget() self.gamewidget.addTab(welcome := QLabel('Welcome to FF5Reader, click one of the game tabs at the top to get started.'), 'Welcome')
self.ff5widget = QTabWidget() self.gamewidget.addTab(ff5widget := QTabWidget(), 'FFV')
self.ff6widget = QTabWidget() self.gamewidget.addTab(ff4widget := QTabWidget(), 'FFIV')
self.gamewidget.addTab(self.ff5widget, 'FFV') self.gamewidget.addTab(ff6widget := QTabWidget(), 'FFVI')
self.gamewidget.addTab(self.ff4widget, 'FFIV')
self.gamewidget.addTab(self.ff6widget, 'FFVI')
strings_tab = QTabWidget()
structs_tab = QTabWidget()
sprites_tab = QTabWidget()
backgrounds_tab = QTabWidget()
self.ff5widget.addTab(strings_tab, 'Strings')
self.ff5widget.addTab(structs_tab, 'Structs')
self.ff5widget.addTab(sprites_tab, 'Images')
self.ff5widget.addTab(backgrounds_tab, 'Backgrounds')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_s'], scale=4), 'Glyphs (EN)') ff5widget.addTab(strings_tab := QTabWidget(), 'Strings')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_l'], scale=2), 'Glyphs (Dialogue EN)') ff5widget.addTab(glyphs_tab := QTabWidget(), 'Glyphs')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_s'], scale=4), 'Glyphs (JP)') ff5widget.addTab(structs_tab := QTabWidget(), 'Structs')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_l'], scale=2), 'Glyphs (Large JP)') ff5widget.addTab(sprites_tab := QTabWidget(), 'Sprites')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_kanji'], scale=2),'Glyphs (Kanji)') ff5widget.addTab(backgrounds_tab := QTabWidget(), 'Backgrounds')
sprites_tab.addTab(make_px_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites')
sprites_tab.addTab(make_px_table(status_strips, cols=22, scale=2), 'Status Sprites')
sprites_tab.addTab(make_px_table(enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites')
backgrounds_tab.addTab(make_px_table(worldmap_tiles, cols=16, scale=4), 'Worldmap Tiles')
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[0], cols=16, scale=4), 'World 1 Blocks')
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[1], cols=16, scale=4), 'World 2 Blocks')
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[2], cols=16, scale=4), 'Underwater Blocks')
backgrounds_tab.addTab(make_px_table(worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
backgrounds_tab.addTab(make_px_table(fieldmap_tiles, cols=16, scale=1), 'Fieldmap Tiles')
backgrounds_tab.addTab(make_px_table(field_blocks, cols=16, scale=1), 'Field Blocks')
backgrounds_tab.addTab(make_px_table(zone_pxs, cols=4, scale=1, large=1, basicrows=True), 'Zone')
backgrounds_tab.addTab(make_px_table(battle_bgs, cols=8, scale=1), 'Battle BGs')
self.ff4widget.addTab(make_px_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites')
self.ff4widget.addTab(make_px_table(self.portraits_ff4, cols=14, scale=2), 'Character Portraits')
self.ff4widget.addTab(make_px_table(self.field_strips_ff4, cols=17, scale=2), 'Character Field Sprites')
self.ff6widget.addTab(make_px_table(self.battle_strips_ff6, cols=32, scale=2), 'Character Sprites')
self.ff6widget.addTab(make_px_table(self.portraits_ff6, cols=19, scale=2), 'Character Portraits')
structs_tab.addTab(make_table(FFVStructs.ZoneData.get_headers(), FFVStructs.ZoneData.get_data(ROM_en), True), 'Zones')
structs_tab.addTab(make_table(FFVStructs.BattleBackground.get_headers(), FFVStructs.BattleBackground.get_data(ROM_jp), True), 'BattleBGs')
structs_tab.addTab(make_table(FFVStructs.EnemySprite.get_headers(), enemy_sprite_data, True), 'Enemy Sprites')
structs_tab.addTab(make_table(tileset_headers, tileset_data, True), 'Tilesets')
structs_tab.addTab(make_table(const.npc_layer_headers, npc_layers, True), 'NPC Layers')
# Strings tabs
for k, images in string_images.items():
scale = 1 if FFVStrings.config[k].get('dialog') else 2
caption = ' '.join(f'{w[0].upper()}{w[1:]}' for w in k.split('_'))
strings_tab.addTab(make_table(imglist_headers, images, row_labels=False, scale=scale), caption)
self.string_decoder = QWidget() self.string_decoder = QWidget()
self.decoder_input = QLineEdit() self.decoder_input = QLineEdit()
@ -240,6 +251,76 @@ class FF5Reader(QMainWindow):
self.string_decoder.setLayout(self.decoder_layout) self.string_decoder.setLayout(self.decoder_layout)
strings_tab.addTab(self.string_decoder, 'String Decoder') strings_tab.addTab(self.string_decoder, 'String Decoder')
def load_tab_strings():
for k, images in string_images.items():
conf = FFVStrings.config[k]
scale = 1 if conf.get('dialog') else 2
caption = ' '.join(f'{w[0].upper()}{w[1:]}' for w in k.split('_'))
strings_tab.addTab(tab := make_table(imglist_headers, images, row_labels=False, scale=scale), caption)
tab.setColumnHidden(1, conf.get('rpge_ptr_offset') is None) # Hide EN Pointer if not indirect
tab.setColumnHidden(5, conf.get('snes_ptr_offset') is None) # Hide JP Pointer if not indirect
tab.resizeColumnsToContents()
for i in range(len(imglist_headers)):
if tab.columnWidth(i) > 360:
tab.setColumnWidth(i, 360)
def load_tab_glyphs():
glyphs_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_s'], scale=3), 'Small EN')
glyphs_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_l'], scale=2), 'Dialogue EN')
glyphs_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_s'], scale=3), 'Small JP')
glyphs_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_l'], scale=2), 'Dialogue JP')
glyphs_tab.addTab(make_px_table(self.glyph_sprites['glyphs_kanji'], scale=2),'Kanji')
def load_tab_structs():
structs_tab.addTab(make_table(FFVStructs.ZoneData.get_headers(), FFVStructs.ZoneData.get_data(ROM_RPGe), True), 'Zones')
structs_tab.addTab(make_table(FFVStructs.BattleBackground.get_headers(), FFVStructs.BattleBackground.get_data(ROM_SNES), True), 'BattleBGs')
structs_tab.addTab(make_table(FFVStructs.EnemySprite.get_headers(), self.enemy_sprite_data, True), 'Enemy Sprites')
structs_tab.addTab(make_table(tileset_headers, tileset_data, True), 'Tilesets')
structs_tab.addTab(make_table(const.npc_layer_headers, npc_layers, True), 'NPC Layers')
def load_tab_sprites():
sprites_tab.addTab(make_px_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites')
sprites_tab.addTab(make_px_table(self.status_strips, cols=22, scale=2), 'Status Sprites')
sprites_tab.addTab(make_px_table(self.enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites')
def load_tab_backgrounds():
backgrounds_tab.addTab(make_px_table(self.worldmap_tiles, cols=16, scale=4), 'Worldmap Tiles')
backgrounds_tab.addTab(make_px_table(self.world_blocks_pixmaps[0], cols=16, scale=3), 'World 1 Blocks')
backgrounds_tab.addTab(make_px_table(self.world_blocks_pixmaps[1], cols=16, scale=3), 'World 2 Blocks')
backgrounds_tab.addTab(make_px_table(self.world_blocks_pixmaps[2], cols=16, scale=3), 'Underwater Blocks')
backgrounds_tab.addTab(make_px_table(self.worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
backgrounds_tab.addTab(make_px_table(self.fieldmap_tiles, cols=16, scale=1), 'Fieldmap Tiles')
backgrounds_tab.addTab(make_px_table(self.field_blocks, cols=16, scale=1), 'Field Blocks')
backgrounds_tab.addTab(make_px_table(self.zone_pxs, cols=4, scale=1, large=1, basicrows=True), 'Zone')
backgrounds_tab.addTab(make_px_table(self.battle_bgs, cols=8, scale=1), 'Battle BGs')
tab_loaders = [load_tab_strings, load_tab_glyphs, load_tab_structs, load_tab_sprites, load_tab_backgrounds]
def load_tab(index: int):
if fn := tab_loaders[index]:
fn()
tab_loaders[index] = None
ff5widget.currentChanged.connect(load_tab)
def load_ff5():
load_tab(ff5widget.currentIndex())
pass
def load_ff4():
ff4widget.addTab(make_px_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites')
ff4widget.addTab(make_px_table(self.portraits_ff4, cols=14, scale=2), 'Character Portraits')
ff4widget.addTab(make_px_table(self.field_strips_ff4, cols=17, scale=2), 'Character Field Sprites')
def load_ff6():
ff6widget.addTab(make_px_table(self.battle_strips_ff6, cols=32, scale=2), 'Character Sprites')
ff6widget.addTab(make_px_table(self.portraits_ff6, cols=19, scale=2), 'Character Portraits')
game_tab_loaders = [None, load_ff5, load_ff4, load_ff6]
def load_game_tab(index: int):
if fn := game_tab_loaders[index]:
fn()
game_tab_loaders[index] = None
self.gamewidget.currentChanged.connect(load_game_tab)
layout = QHBoxLayout() layout = QHBoxLayout()
layout.addWidget(self.gamewidget) layout.addWidget(self.gamewidget)
self.main_widget = QWidget(self) self.main_widget = QWidget(self)