1
0
mirror of https://github.com/DarklightGames/io_scene_psk_psa.git synced 2024-11-15 10:47:39 +01:00

PSA Import screen now has more robust functionality now (but still aint done!)

This commit is contained in:
Colin Basnett 2022-01-24 21:50:34 -08:00
parent 0d06236bab
commit c672941663
8 changed files with 267 additions and 87 deletions

View File

@ -13,6 +13,7 @@ bl_info = {
if 'bpy' in locals(): if 'bpy' in locals():
import importlib import importlib
importlib.reload(psx_data) importlib.reload(psx_data)
importlib.reload(psx_helpers) importlib.reload(psx_helpers)
importlib.reload(psx_types) importlib.reload(psx_types)
@ -42,15 +43,14 @@ else:
from .psa import reader as psa_reader from .psa import reader as psa_reader
from .psa import importer as psa_importer from .psa import importer as psa_importer
import bpy import bpy
from bpy.props import PointerProperty from bpy.props import PointerProperty
classes = psx_types.__classes__ + \ classes = (psx_types.classes +
psk_importer.__classes__ + \ psk_importer.classes +
psk_exporter.__classes__ + \ psk_exporter.classes +
psa_exporter.__classes__ + \ psa_exporter.classes +
psa_importer.__classes__ psa_importer.classes)
def psk_export_menu_func(self, context): def psk_export_menu_func(self, context):

View File

@ -17,8 +17,11 @@ def populate_bone_group_list(armature_object, bone_group_list):
item.is_selected = True item.is_selected = True
def add_bone_groups_to_layout(layout): def get_psa_sequence_name(action, should_use_original_sequence_name):
pass if should_use_original_sequence_name and 'original_sequence_name' in action:
return action['original_sequence_name']
else:
return action.name
def get_export_bone_indices_for_bone_groups(armature_object, bone_group_indices: List[int]) -> List[int]: def get_export_bone_indices_for_bone_groups(armature_object, bone_group_indices: List[int]) -> List[int]:

View File

@ -108,10 +108,7 @@ class PsaBuilder(object):
sequence = Psa.Sequence() sequence = Psa.Sequence()
if options.should_use_original_sequence_names and 'original_sequence_name' in action: sequence_name = get_psa_sequence_name(action, options.should_use_original_sequence_names)
sequence_name = action['original_sequence_name']
else:
sequence_name = action.name
sequence.name = bytes(sequence_name, encoding='windows-1252') sequence.name = bytes(sequence_name, encoding='windows-1252')
sequence.frame_count = frame_max - frame_min + 1 sequence.frame_count = frame_max - frame_min + 1

View File

@ -1,5 +1,5 @@
import bpy import bpy
from bpy.types import Operator, PropertyGroup, Action, UIList, BoneGroup from bpy.types import Operator, PropertyGroup, Action, UIList, BoneGroup, Panel
from bpy.props import CollectionProperty, IntProperty, PointerProperty, StringProperty, BoolProperty, EnumProperty from bpy.props import CollectionProperty, IntProperty, PointerProperty, StringProperty, BoolProperty, EnumProperty
from bpy_extras.io_utils import ExportHelper from bpy_extras.io_utils import ExportHelper
from typing import Type from typing import Type
@ -7,6 +7,7 @@ from .builder import PsaBuilder, PsaBuilderOptions
from .data import * from .data import *
from ..types import BoneGroupListItem from ..types import BoneGroupListItem
from ..helpers import * from ..helpers import *
from collections import Counter
import re import re
@ -49,15 +50,13 @@ def update_action_names(context):
property_group = context.scene.psa_export property_group = context.scene.psa_export
for item in property_group.action_list: for item in property_group.action_list:
action = item.action action = item.action
if property_group.should_use_original_sequence_names and 'original_sequence_name' in action: item.action_name = get_psa_sequence_name(action, property_group.should_use_original_sequence_names)
item.action_name = action['original_sequence_name']
else:
item.action_name = action.name
def should_use_original_sequence_names_updated(property, context): def should_use_original_sequence_names_updated(property, context):
update_action_names(context) update_action_names(context)
class PsaExportPropertyGroup(PropertyGroup): class PsaExportPropertyGroup(PropertyGroup):
action_list: CollectionProperty(type=PsaExportActionListItem) action_list: CollectionProperty(type=PsaExportActionListItem)
action_list_index: IntProperty(default=0) action_list_index: IntProperty(default=0)
@ -71,11 +70,11 @@ class PsaExportPropertyGroup(PropertyGroup):
) )
bone_group_list: CollectionProperty(type=BoneGroupListItem) bone_group_list: CollectionProperty(type=BoneGroupListItem)
bone_group_list_index: IntProperty(default=0) bone_group_list_index: IntProperty(default=0)
should_use_original_sequence_names: BoolProperty(default=False, description='If the action was imported from the PSA Import panel, the original name of the action will be used instead of the action name assigned in Blender', update=should_use_original_sequence_names_updated) should_use_original_sequence_names: BoolProperty(default=False, name='Original Names', description='If the action was imported from the PSA Import panel, the original name of the sequence will be used instead of the Blender action name', update=should_use_original_sequence_names_updated)
def is_bone_filter_mode_item_available(context, identifier): def is_bone_filter_mode_item_available(context, identifier):
if identifier == "BONE_GROUPS": if identifier == 'BONE_GROUPS':
obj = context.active_object obj = context.active_object
if not obj.pose or not obj.pose.bone_groups: if not obj.pose or not obj.pose.bone_groups:
return False return False
@ -83,7 +82,7 @@ def is_bone_filter_mode_item_available(context, identifier):
class PsaExportOperator(Operator, ExportHelper): class PsaExportOperator(Operator, ExportHelper):
bl_idname = 'export.psa' bl_idname = 'psa_export.operator'
bl_label = 'Export' bl_label = 'Export'
__doc__ = 'Export actions to PSA' __doc__ = 'Export actions to PSA'
filename_ext = '.psa' filename_ext = '.psa'
@ -102,19 +101,34 @@ class PsaExportOperator(Operator, ExportHelper):
property_group = context.scene.psa_export property_group = context.scene.psa_export
# ACTIONS # ACTIONS
box = layout.box() layout.label(text='Actions', icon='ACTION')
box.label(text='Actions', icon='ACTION') row = layout.row(align=True)
row = box.row()
row.template_list('PSA_UL_ExportActionList', 'asd', property_group, 'action_list', property_group, 'action_list_index', rows=10)
row = box.row(align=True)
row.label(text='Select') row.label(text='Select')
row.operator('psa_export.actions_select_all', text='All') row.operator('psa_export.actions_select_all', text='All')
row.operator('psa_export.actions_deselect_all', text='None') row.operator('psa_export.actions_deselect_all', text='None')
row = layout.row()
rows = max(3, min(len(property_group.action_list), 10))
row.template_list('PSA_UL_ExportActionList', '', property_group, 'action_list', property_group,
'action_list_index', rows=rows)
layout.prop(property_group, 'should_use_original_sequence_names', text='Original Sequence Names') col = layout.column(heading="Options")
col.use_property_split = True
col.use_property_decorate = False
col.prop(property_group, 'should_use_original_sequence_names')
# Determine if there is going to be a naming conflict and display an error, if so.
selected_actions = [x for x in property_group.action_list if x.is_selected]
action_names = [x.action_name for x in selected_actions]
action_name_counts = Counter(action_names)
for action_name, count in action_name_counts.items():
if count > 1:
layout.label(text=f'Duplicate action: {action_name}', icon='ERROR')
break
layout.separator()
# BONES # BONES
box = layout.box() box = layout.row()
box.label(text='Bones', icon='BONE_DATA') box.label(text='Bones', icon='BONE_DATA')
bone_filter_mode_items = property_group.bl_rna.properties['bone_filter_mode'].enum_items_static bone_filter_mode_items = property_group.bl_rna.properties['bone_filter_mode'].enum_items_static
row = box.row(align=True) row = box.row(align=True)
@ -126,10 +140,9 @@ class PsaExportOperator(Operator, ExportHelper):
item_layout.enabled = is_bone_filter_mode_item_available(context, identifier) item_layout.enabled = is_bone_filter_mode_item_available(context, identifier)
if property_group.bone_filter_mode == 'BONE_GROUPS': if property_group.bone_filter_mode == 'BONE_GROUPS':
box = layout.box()
row = box.row()
rows = max(3, min(len(property_group.bone_group_list), 10)) rows = max(3, min(len(property_group.bone_group_list), 10))
row.template_list('PSX_UL_BoneGroupList', '', property_group, 'bone_group_list', property_group, 'bone_group_list_index', rows=rows) layout.template_list('PSX_UL_BoneGroupList', '', property_group, 'bone_group_list', property_group,
'bone_group_list_index', rows=rows)
def is_action_for_armature(self, action): def is_action_for_armature(self, action):
if len(action.fcurves) == 0: if len(action.fcurves) == 0:
@ -160,11 +173,11 @@ class PsaExportOperator(Operator, ExportHelper):
# Populate actions list. # Populate actions list.
property_group.action_list.clear() property_group.action_list.clear()
for action in bpy.data.actions: for action in bpy.data.actions:
if not self.is_action_for_armature(action):
continue
item = property_group.action_list.add() item = property_group.action_list.add()
item.action = action item.action = action
item.action_name = action.name item.action_name = action.name
if self.is_action_for_armature(action):
item.is_selected = True item.is_selected = True
update_action_names(context) update_action_names(context)
@ -264,11 +277,11 @@ class PsaExportDeselectAll(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
__classes__ = [ classes = (
PsaExportActionListItem, PsaExportActionListItem,
PsaExportPropertyGroup, PsaExportPropertyGroup,
PsaExportOperator, PsaExportOperator,
PSA_UL_ExportActionList, PSA_UL_ExportActionList,
PsaExportSelectAll, PsaExportSelectAll,
PsaExportDeselectAll, PsaExportDeselectAll,
] )

View File

@ -5,7 +5,7 @@ from mathutils import Vector, Quaternion, Matrix
from .data import Psa from .data import Psa
from typing import List, AnyStr, Optional from typing import List, AnyStr, Optional
from bpy.types import Operator, Action, UIList, PropertyGroup, Panel, Armature, FileSelectParams from bpy.types import Operator, Action, UIList, PropertyGroup, Panel, Armature, FileSelectParams
from bpy_extras.io_utils import ExportHelper, ImportHelper from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty
from .reader import PsaReader from .reader import PsaReader
@ -16,6 +16,7 @@ class PsaImportOptions(object):
self.should_use_fake_user = False self.should_use_fake_user = False
self.should_stash = False self.should_stash = False
self.sequence_names = [] self.sequence_names = []
self.should_use_action_name_prefix = False
self.action_name_prefix = '' self.action_name_prefix = ''
@ -216,40 +217,57 @@ class PsaImportActionListItem(PropertyGroup):
return self.action_name return self.action_name
def on_psa_file_path_updated(property, context): def load_psa_file(context):
property_group = context.scene.psa_import property_group = context.scene.psa_import
property_group.action_list.clear() property_group.sequence_list.clear()
property_group.psa_bones.clear() property_group.psa_bones.clear()
property_group.action_list.clear()
property_group.psa_error = ''
try: try:
# Read the file and populate the action list. # Read the file and populate the action list.
p = os.path.abspath(property_group.psa_file_path) p = os.path.abspath(property_group.psa_file_path)
psa_reader = PsaReader(p) psa_reader = PsaReader(p)
for sequence in psa_reader.sequences.values(): for sequence in psa_reader.sequences.values():
item = property_group.action_list.add() item = property_group.sequence_list.add()
item.action_name = sequence.name.decode('windows-1252') item.action_name = sequence.name.decode('windows-1252')
item.frame_count = sequence.frame_count item.frame_count = sequence.frame_count
item.is_selected = True
for psa_bone in psa_reader.bones: for psa_bone in psa_reader.bones:
item = property_group.psa_bones.add() item = property_group.psa_bones.add()
item.bone_name = psa_bone.name item.bone_name = psa_bone.name.decode('windows-1252')
except IOError as e: except Exception as e:
# TODO: set an error somewhere so the user knows the PSA could not be read. property_group.psa_error = str(e)
pass
class PsaImportPropertyGroup(bpy.types.PropertyGroup): def on_psa_file_path_updated(property, context):
load_psa_file(context)
class PsaBonePropertyGroup(PropertyGroup):
bone_name: StringProperty()
class PsaDataPropertyGroup(PropertyGroup):
bone_count: IntProperty(default=0)
bones: CollectionProperty(type=PsaBonePropertyGroup)
sequence_count: IntProperty(default=0)
class PsaImportPropertyGroup(PropertyGroup):
psa_file_path: StringProperty(default='', update=on_psa_file_path_updated, name='PSA File Path') psa_file_path: StringProperty(default='', update=on_psa_file_path_updated, name='PSA File Path')
psa_error: StringProperty(default='')
psa_bones: CollectionProperty(type=PsaImportPsaBoneItem) psa_bones: CollectionProperty(type=PsaImportPsaBoneItem)
sequence_list: CollectionProperty(type=PsaImportActionListItem)
sequence_list_index: IntProperty(name='', default=0)
action_list: CollectionProperty(type=PsaImportActionListItem) action_list: CollectionProperty(type=PsaImportActionListItem)
action_list_index: IntProperty(name='', default=0) action_list_index: IntProperty(name='', default=0)
action_filter_name: StringProperty(default='')
should_clean_keys: BoolProperty(default=True, name='Clean Keyframes', description='Exclude unnecessary keyframes from being written to the actions.') should_clean_keys: BoolProperty(default=True, name='Clean Keyframes', description='Exclude unnecessary keyframes from being written to the actions.')
should_use_fake_user: BoolProperty(default=True, name='Fake User', description='Assign each imported action a fake user so that the data block is saved even it has no users.') should_use_fake_user: BoolProperty(default=True, name='Fake User', description='Assign each imported action a fake user so that the data block is saved even it has no users.')
should_stash: BoolProperty(default=False, name='Stash', description='Stash each imported action as a strip on a new non-contributing NLA track') should_stash: BoolProperty(default=False, name='Stash', description='Stash each imported action as a strip on a new non-contributing NLA track')
action_name_prefix: StringProperty(default='', name='Action Name Prefix') should_use_action_name_prefix: BoolProperty(default=False, name='Prefix Action Name')
action_name_prefix: StringProperty(default='', name='Prefix')
class PSA_UL_ImportActionList(UIList): class PSA_UL_SequenceList(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
row = layout.row(align=True) row = layout.row(align=True)
@ -282,7 +300,34 @@ class PSA_UL_ImportActionList(UIList):
return flt_flags, flt_neworder return flt_flags, flt_neworder
class PsaImportSelectAll(bpy.types.Operator): class PSA_UL_ImportSequenceList(PSA_UL_SequenceList, UIList):
pass
class PSA_UL_ImportActionList(PSA_UL_SequenceList, UIList):
pass
class PsaImportSequencesSelectAll(bpy.types.Operator):
bl_idname = 'psa_import.sequences_select_all'
bl_label = 'All'
bl_description = 'Select all sequences'
@classmethod
def poll(cls, context):
property_group = context.scene.psa_import
sequence_list = property_group.sequence_list
has_unselected_actions = any(map(lambda action: not action.is_selected, sequence_list))
return len(sequence_list) > 0 and has_unselected_actions
def execute(self, context):
property_group = context.scene.psa_import
for action in property_group.sequence_list:
action.is_selected = True
return {'FINISHED'}
class PsaImportActionsSelectAll(bpy.types.Operator):
bl_idname = 'psa_import.actions_select_all' bl_idname = 'psa_import.actions_select_all'
bl_label = 'All' bl_label = 'All'
bl_description = 'Select all actions' bl_description = 'Select all actions'
@ -301,7 +346,26 @@ class PsaImportSelectAll(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
class PsaImportDeselectAll(bpy.types.Operator): class PsaImportSequencesDeselectAll(bpy.types.Operator):
bl_idname = 'psa_import.sequences_deselect_all'
bl_label = 'None'
bl_description = 'Deselect all sequences'
@classmethod
def poll(cls, context):
property_group = context.scene.psa_import
sequence_list = property_group.sequence_list
has_selected_sequences = any(map(lambda action: action.is_selected, sequence_list))
return len(sequence_list) > 0 and has_selected_sequences
def execute(self, context):
property_group = context.scene.psa_import
for action in property_group.sequence_list:
action.is_selected = False
return {'FINISHED'}
class PsaImportActionsDeselectAll(bpy.types.Operator):
bl_idname = 'psa_import.actions_deselect_all' bl_idname = 'psa_import.actions_deselect_all'
bl_label = 'None' bl_label = 'None'
bl_description = 'Deselect all actions' bl_description = 'Deselect all actions'
@ -320,6 +384,31 @@ class PsaImportDeselectAll(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
class PSA_PT_ImportPanel_Advanced(Panel):
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_label = 'Advanced'
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = 'PSA_PT_ImportPanel'
def draw(self, context):
layout = self.layout
property_group = context.scene.psa_import
col = layout.column(heading="Options")
col.use_property_split = True
col.use_property_decorate = False
col.prop(property_group, 'should_clean_keys')
col.separator()
col.prop(property_group, 'should_use_fake_user')
col.prop(property_group, 'should_stash')
col.separator()
col.prop(property_group, 'should_use_action_name_prefix')
if property_group.should_use_action_name_prefix:
col.prop(property_group, 'action_name_prefix')
class PSA_PT_ImportPanel(Panel): class PSA_PT_ImportPanel(Panel):
bl_space_type = 'PROPERTIES' bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW' bl_region_type = 'WINDOW'
@ -336,33 +425,58 @@ class PSA_PT_ImportPanel(Panel):
layout = self.layout layout = self.layout
property_group = context.scene.psa_import property_group = context.scene.psa_import
row = layout.row() row = layout.row(align=True)
row.operator('psa_import.select_file', text='', icon='FILEBROWSER')
row.prop(property_group, 'psa_file_path', text='') row.prop(property_group, 'psa_file_path', text='')
row.enabled = False row.operator('psa_import.file_reload', text='', icon='FILE_REFRESH')
if property_group.psa_error != '':
row = layout.row() row = layout.row()
row.operator('psa_import.select_file', text='Select PSA File', icon='FILEBROWSER') row.label(text='File could not be read', icon='ERROR')
if len(property_group.action_list) > 0:
box = layout.box() box = layout.box()
box.label(text=f'Actions ({len(property_group.action_list)})', icon='ACTION')
box.label(text=f'Sequences', icon='ARMATURE_DATA')
# select
rows = max(3, min(len(property_group.sequence_list) + len(property_group.action_list), 10))
row = box.row() row = box.row()
rows = max(3, min(len(property_group.action_list), 10)) col = row.column()
row.template_list('PSA_UL_ImportActionList', '', property_group, 'action_list', property_group, 'action_list_index', rows=rows)
row = box.row(align=True)
row.label(text='Select')
row.operator('psa_import.actions_select_all', text='All')
row.operator('psa_import.actions_deselect_all', text='None')
col = layout.column(heading="Options") row2 = col.row(align=True)
col.use_property_split = True row2.label(text='Select')
col.use_property_decorate = False row2.operator('psa_import.sequences_select_all', text='All')
col.prop(property_group, 'should_clean_keys') row2.operator('psa_import.sequences_deselect_all', text='None')
col.prop(property_group, 'should_use_fake_user')
col.prop(property_group, 'should_stash')
col.prop(property_group, 'action_name_prefix')
layout.operator('psa_import.import', text=f'Import') col = col.row()
col.template_list('PSA_UL_ImportSequenceList', '', property_group, 'sequence_list', property_group,
'sequence_list_index', rows=rows)
col = row.column(align=True)
col.operator('psa_import.push_to_actions', icon='TRIA_RIGHT', text='')
col.operator('psa_import.pop_from_actions', icon='TRIA_LEFT', text='')
col = row.column()
row2 = col.row(align=True)
row2.label(text='Select')
row2.operator('psa_import.actions_select_all', text='All')
row2.operator('psa_import.actions_deselect_all', text='None')
col.template_list('PSA_UL_ImportActionList', '', property_group, 'action_list', property_group,
'action_list_index', rows=rows)
col.separator()
col.operator('psa_import.import', text=f'Import')
class PsaImportFileReload(Operator):
bl_idname = 'psa_import.file_reload'
bl_label = 'Refresh'
bl_options = {'REGISTER'}
bl_description = 'Refresh the PSA file'
def execute(self, context):
load_psa_file(context)
return {"FINISHED"}
class PsaImportSelectFile(Operator): class PsaImportSelectFile(Operator):
@ -392,13 +506,12 @@ class PsaImportOperator(Operator):
property_group = context.scene.psa_import property_group = context.scene.psa_import
active_object = context.view_layer.objects.active active_object = context.view_layer.objects.active
action_list = property_group.action_list action_list = property_group.action_list
has_selected_actions = any(map(lambda action: action.is_selected, action_list)) return len(action_list) and active_object is not None and active_object.type == 'ARMATURE'
return has_selected_actions and active_object is not None and active_object.type == 'ARMATURE'
def execute(self, context): def execute(self, context):
property_group = context.scene.psa_import property_group = context.scene.psa_import
psa_reader = PsaReader(property_group.psa_file_path) psa_reader = PsaReader(property_group.psa_file_path)
sequence_names = [x.action_name for x in property_group.action_list if x.is_selected] sequence_names = [x.action_name for x in property_group.action_list]
options = PsaImportOptions() options = PsaImportOptions()
options.sequence_names = sequence_names options.sequence_names = sequence_names
options.should_clean_keys = property_group.should_clean_keys options.should_clean_keys = property_group.should_clean_keys
@ -410,6 +523,52 @@ class PsaImportOperator(Operator):
return {'FINISHED'} return {'FINISHED'}
class PsaImportPushToActions(Operator):
bl_idname = 'psa_import.push_to_actions'
bl_label = 'Push to Actions'
@classmethod
def poll(cls, context):
property_group = context.scene.psa_import
has_sequences_selected = any(map(lambda x: x.is_selected, property_group.sequence_list))
return has_sequences_selected
def execute(self, context):
property_group = context.scene.psa_import
indices_to_remove = []
for sequence_index, item in enumerate(property_group.sequence_list):
if item.is_selected:
indices_to_remove.append(sequence_index)
action = property_group.action_list.add()
action.action_name = item.action_name
for index in reversed(indices_to_remove):
property_group.sequence_list.remove(index)
return {'FINISHED'}
class PsaImportPopFromActions(Operator):
bl_idname = 'psa_import.pop_from_actions'
bl_label = 'Pop From Actions'
@classmethod
def poll(cls, context):
property_group = context.scene.psa_import
has_actions_selected = any(map(lambda x: x.is_selected, property_group.action_list))
return has_actions_selected
def execute(self, context):
property_group = context.scene.psa_import
indices_to_remove = []
for action_index, item in enumerate(property_group.action_list):
if item.is_selected:
indices_to_remove.append(action_index)
sequence = property_group.sequence_list.add()
sequence.action_name = item.action_name
for index in reversed(indices_to_remove):
property_group.action_list.remove(index)
return {'FINISHED'}
class PsaImportFileSelectOperator(Operator, ImportHelper): class PsaImportFileSelectOperator(Operator, ImportHelper):
bl_idname = 'psa_import.file_select' bl_idname = 'psa_import.file_select'
bl_label = 'File Select' bl_label = 'File Select'
@ -432,15 +591,23 @@ class PsaImportFileSelectOperator(Operator, ImportHelper):
return {'FINISHED'} return {'FINISHED'}
__classes__ = [ classes = (
PsaImportPsaBoneItem, PsaImportPsaBoneItem,
PsaImportActionListItem, PsaImportActionListItem,
PsaImportPropertyGroup, PsaImportPropertyGroup,
PSA_UL_SequenceList,
PSA_UL_ImportSequenceList,
PSA_UL_ImportActionList, PSA_UL_ImportActionList,
PsaImportSelectAll, PsaImportSequencesSelectAll,
PsaImportDeselectAll, PsaImportSequencesDeselectAll,
PsaImportActionsSelectAll,
PsaImportActionsDeselectAll,
PsaImportFileReload,
PSA_PT_ImportPanel, PSA_PT_ImportPanel,
PSA_PT_ImportPanel_Advanced,
PsaImportOperator, PsaImportOperator,
PsaImportFileSelectOperator, PsaImportFileSelectOperator,
PsaImportSelectFile, PsaImportSelectFile,
] PsaImportPushToActions,
PsaImportPopFromActions,
)

View File

@ -148,7 +148,7 @@ class PskExportPropertyGroup(PropertyGroup):
bone_group_list_index: IntProperty(default=0) bone_group_list_index: IntProperty(default=0)
__classes__ = [ classes = (
PskExportOperator, PskExportOperator,
PskExportPropertyGroup PskExportPropertyGroup
] )

View File

@ -184,6 +184,6 @@ class PskImportOperator(Operator, ImportHelper):
return {'FINISHED'} return {'FINISHED'}
__classes__ = [ classes = (
PskImportOperator PskImportOperator,
] )

View File

@ -19,7 +19,7 @@ class BoneGroupListItem(PropertyGroup):
return self.name return self.name
__classes__ = [ classes = (
BoneGroupListItem, BoneGroupListItem,
PSX_UL_BoneGroupList PSX_UL_BoneGroupList,
] )