From e8e8d6ce8bacf6cd46ef3c0ed24fb857c29140a4 Mon Sep 17 00:00:00 2001 From: Colin Basnett Date: Sun, 17 Apr 2022 17:31:52 -0700 Subject: [PATCH] Added regex fitlering to the PSA sequence list --- io_scene_psk_psa/psa/builder.py | 44 ++++++++++++++++---------------- io_scene_psk_psa/psa/exporter.py | 8 +++--- io_scene_psk_psa/psa/importer.py | 27 +++++++++++++++----- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/io_scene_psk_psa/psa/builder.py b/io_scene_psk_psa/psa/builder.py index db7dbcd..0a24a34 100644 --- a/io_scene_psk_psa/psa/builder.py +++ b/io_scene_psk_psa/psa/builder.py @@ -30,6 +30,26 @@ class PsaBuilder(object): def __init__(self): pass + def get_sequence_fps(self, context, options: PsaBuilderOptions, actions: Iterable[Action]) -> float: + if options.fps_source == 'SCENE': + return context.scene.render.fps + if options.fps_source == 'CUSTOM': + return options.fps_custom + elif options.fps_source == 'ACTION_METADATA': + # Get the minimum value of action metadata FPS values. + psa_fps_list = [] + for action in filter(lambda x: 'psa_fps' in x, actions): + psa_fps = action['psa_fps'] + if type(psa_fps) == int or type(psa_fps) == float: + psa_fps_list.append(psa_fps) + if len(psa_fps_list) > 0: + return min(psa_fps_list) + else: + # No valid action metadata to use, fallback to scene FPS + return context.scene.render.fps + else: + raise RuntimeError(f'Invalid FPS source "{options.fps_source}"') + def build(self, context, options: PsaBuilderOptions) -> Psa: performance = PsaBuilderPerformance() active_object = context.view_layer.objects.active @@ -124,26 +144,6 @@ class PsaBuilder(object): export_sequences = [] - def get_sequence_fps(context, options: PsaBuilderOptions, actions: Iterable[Action]) -> float: - if options.fps_source == 'SCENE': - return context.scene.render.fps - if options.fps_source == 'CUSTOM': - return options.fps_custom - elif options.fps_source == 'ACTION_METADATA': - # Get the minimum value of action metadata FPS values. - psa_fps_list = [] - for action in filter(lambda x: 'psa_fps' in x, actions): - psa_fps = action['psa_fps'] - if type(psa_fps) == int or type(psa_fps) == float: - psa_fps_list.append(psa_fps) - if len(psa_fps_list) > 0: - return min(psa_fps_list) - else: - # No valid action metadata to use, fallback to scene FPS - return context.scene.render.fps - else: - raise RuntimeError(f'Invalid FPS source "{options.fps_source}"') - if options.sequence_source == 'ACTIONS': for action in options.actions: if len(action.fcurves) == 0: @@ -154,7 +154,7 @@ class PsaBuilder(object): frame_min, frame_max = [int(x) for x in action.frame_range] export_sequence.nla_state.frame_min = frame_min export_sequence.nla_state.frame_max = frame_max - export_sequence.fps = get_sequence_fps(context, options, [action]) + export_sequence.fps = self.get_sequence_fps(context, options, [action]) export_sequences.append(export_sequence) pass elif options.sequence_source == 'TIMELINE_MARKERS': @@ -167,7 +167,7 @@ class PsaBuilder(object): export_sequence.nla_state.frame_min = frame_min export_sequence.nla_state.frame_max = frame_max nla_strips_actions = set(map(lambda x: x.action, get_nla_strips_in_timeframe(active_object, frame_min, frame_max))) - export_sequence.fps = get_sequence_fps(context, options, nla_strips_actions) + export_sequence.fps = self.get_sequence_fps(context, options, nla_strips_actions) export_sequences.append(export_sequence) else: raise ValueError(f'Unhandled sequence source: {options.sequence_source}') diff --git a/io_scene_psk_psa/psa/exporter.py b/io_scene_psk_psa/psa/exporter.py index 77fcfc7..fd5d15e 100644 --- a/io_scene_psk_psa/psa/exporter.py +++ b/io_scene_psk_psa/psa/exporter.py @@ -10,6 +10,7 @@ from ..helpers import * from collections import Counter import re import sys +import fnmatch class PsaExporter(object): @@ -318,7 +319,6 @@ def filter_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_c if pg.sequence_filter_name is not None: # Filter name is non-empty. - import fnmatch for i, sequence in enumerate(sequences): if not fnmatch.fnmatch(sequence.name, f'*{pg.sequence_filter_name}*'): flt_flags[i] &= ~bitflag_filter_item @@ -331,7 +331,7 @@ def filter_sequences(pg: PsaExportPropertyGroup, sequences: bpy.types.bpy_prop_c if pg.sequence_use_filter_invert: # Invert filter flags for all items. for i, sequence in enumerate(sequences): - flt_flags[i] ^= ~bitflag_filter_item + flt_flags[i] ^= bitflag_filter_item return flt_flags @@ -377,7 +377,7 @@ class PSA_UL_ExportActionList(UIList): class PsaExportActionsSelectAll(Operator): bl_idname = 'psa_export.sequences_select_all' bl_label = 'Select All' - bl_description = 'Select all sequences' + bl_description = 'Select all visible sequences' bl_options = {'INTERNAL'} @classmethod @@ -408,7 +408,7 @@ class PsaExportActionsSelectAll(Operator): class PsaExportActionsDeselectAll(Operator): bl_idname = 'psa_export.sequences_deselect_all' bl_label = 'Deselect All' - bl_description = 'Deselect all sequences' + bl_description = 'Deselect all visible sequences' bl_options = {'INTERNAL'} @classmethod diff --git a/io_scene_psk_psa/psa/importer.py b/io_scene_psk_psa/psa/importer.py index f06bdda..1713627 100644 --- a/io_scene_psk_psa/psa/importer.py +++ b/io_scene_psk_psa/psa/importer.py @@ -1,6 +1,8 @@ import bpy import os import numpy as np +import re +import fnmatch from mathutils import Vector, Quaternion, Matrix from .data import Psa from typing import List, AnyStr, Optional @@ -267,6 +269,7 @@ class PsaImportPropertyGroup(PropertyGroup): action_name_prefix: StringProperty(default='', name='Prefix', options=set()) sequence_filter_name: StringProperty(default='', options={'TEXTEDIT_UPDATE'}) sequence_use_filter_invert: BoolProperty(default=False, options=set()) + sequence_use_filter_regex: BoolProperty(default=False, name='Regular Expression', description='Filter using regular expressions', options=set()) select_text: PointerProperty(type=bpy.types.Text) @@ -276,15 +279,24 @@ def filter_sequences(pg: PsaImportPropertyGroup, sequences: bpy.types.bpy_prop_c if pg.sequence_filter_name is not None: # Filter name is non-empty. - import fnmatch - for i, sequence in enumerate(sequences): - if not fnmatch.fnmatch(sequence.action_name, f'*{pg.sequence_filter_name}*'): - flt_flags[i] &= ~bitflag_filter_item + if pg.sequence_use_filter_regex: + # Use regular expression + try: + regex = re.compile(pg.sequence_filter_name) + for i, sequence in enumerate(sequences): + if not regex.match(sequence.action_name): + flt_flags[i] &= ~bitflag_filter_item + except re.error: + pass + else: + for i, sequence in enumerate(sequences): + if not fnmatch.fnmatch(sequence.action_name, f'*{pg.sequence_filter_name}*'): + flt_flags[i] &= ~bitflag_filter_item if pg.sequence_use_filter_invert: # Invert filter flags for all items. for i, sequence in enumerate(sequences): - flt_flags[i] ^= ~bitflag_filter_item + flt_flags[i] ^= bitflag_filter_item return flt_flags @@ -313,6 +325,7 @@ class PSA_UL_SequenceList(UIList): subrow = row.row(align=True) subrow.prop(pg, 'sequence_filter_name', text="") subrow.prop(pg, 'sequence_use_filter_invert', text="", icon='ARROW_LEFTRIGHT') + subrow.prop(pg, 'sequence_use_filter_regex', text="", icon='SORTBYEXT') def filter_items(self, context, data, property): pg = context.scene.psa_import @@ -368,7 +381,7 @@ class PsaImportSequencesFromText(Operator): class PsaImportSequencesSelectAll(Operator): bl_idname = 'psa_import.sequences_select_all' bl_label = 'All' - bl_description = 'Select all sequences' + bl_description = 'Select all visible sequences' bl_options = {'INTERNAL'} @classmethod @@ -389,7 +402,7 @@ class PsaImportSequencesSelectAll(Operator): class PsaImportSequencesDeselectAll(Operator): bl_idname = 'psa_import.sequences_deselect_all' bl_label = 'None' - bl_description = 'Deselect all sequences' + bl_description = 'Deselect all visible sequences' bl_options = {'INTERNAL'} @classmethod