Added regex fitlering to the PSA sequence list

This commit is contained in:
Colin Basnett
2022-04-17 17:31:52 -07:00
parent ab52b1520e
commit e8e8d6ce8b
3 changed files with 46 additions and 33 deletions

View File

@@ -30,6 +30,26 @@ class PsaBuilder(object):
def __init__(self): def __init__(self):
pass 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: def build(self, context, options: PsaBuilderOptions) -> Psa:
performance = PsaBuilderPerformance() performance = PsaBuilderPerformance()
active_object = context.view_layer.objects.active active_object = context.view_layer.objects.active
@@ -124,26 +144,6 @@ class PsaBuilder(object):
export_sequences = [] 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': if options.sequence_source == 'ACTIONS':
for action in options.actions: for action in options.actions:
if len(action.fcurves) == 0: if len(action.fcurves) == 0:
@@ -154,7 +154,7 @@ class PsaBuilder(object):
frame_min, frame_max = [int(x) for x in action.frame_range] 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_min = frame_min
export_sequence.nla_state.frame_max = frame_max 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) export_sequences.append(export_sequence)
pass pass
elif options.sequence_source == 'TIMELINE_MARKERS': 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_min = frame_min
export_sequence.nla_state.frame_max = frame_max 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))) 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) export_sequences.append(export_sequence)
else: else:
raise ValueError(f'Unhandled sequence source: {options.sequence_source}') raise ValueError(f'Unhandled sequence source: {options.sequence_source}')

View File

@@ -10,6 +10,7 @@ from ..helpers import *
from collections import Counter from collections import Counter
import re import re
import sys import sys
import fnmatch
class PsaExporter(object): 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: if pg.sequence_filter_name is not None:
# Filter name is non-empty. # Filter name is non-empty.
import fnmatch
for i, sequence in enumerate(sequences): for i, sequence in enumerate(sequences):
if not fnmatch.fnmatch(sequence.name, f'*{pg.sequence_filter_name}*'): if not fnmatch.fnmatch(sequence.name, f'*{pg.sequence_filter_name}*'):
flt_flags[i] &= ~bitflag_filter_item 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: if pg.sequence_use_filter_invert:
# Invert filter flags for all items. # Invert filter flags for all items.
for i, sequence in enumerate(sequences): for i, sequence in enumerate(sequences):
flt_flags[i] ^= ~bitflag_filter_item flt_flags[i] ^= bitflag_filter_item
return flt_flags return flt_flags
@@ -377,7 +377,7 @@ class PSA_UL_ExportActionList(UIList):
class PsaExportActionsSelectAll(Operator): class PsaExportActionsSelectAll(Operator):
bl_idname = 'psa_export.sequences_select_all' bl_idname = 'psa_export.sequences_select_all'
bl_label = 'Select All' bl_label = 'Select All'
bl_description = 'Select all sequences' bl_description = 'Select all visible sequences'
bl_options = {'INTERNAL'} bl_options = {'INTERNAL'}
@classmethod @classmethod
@@ -408,7 +408,7 @@ class PsaExportActionsSelectAll(Operator):
class PsaExportActionsDeselectAll(Operator): class PsaExportActionsDeselectAll(Operator):
bl_idname = 'psa_export.sequences_deselect_all' bl_idname = 'psa_export.sequences_deselect_all'
bl_label = 'Deselect All' bl_label = 'Deselect All'
bl_description = 'Deselect all sequences' bl_description = 'Deselect all visible sequences'
bl_options = {'INTERNAL'} bl_options = {'INTERNAL'}
@classmethod @classmethod

View File

@@ -1,6 +1,8 @@
import bpy import bpy
import os import os
import numpy as np import numpy as np
import re
import fnmatch
from mathutils import Vector, Quaternion, Matrix 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
@@ -267,6 +269,7 @@ class PsaImportPropertyGroup(PropertyGroup):
action_name_prefix: StringProperty(default='', name='Prefix', options=set()) action_name_prefix: StringProperty(default='', name='Prefix', options=set())
sequence_filter_name: StringProperty(default='', options={'TEXTEDIT_UPDATE'}) sequence_filter_name: StringProperty(default='', options={'TEXTEDIT_UPDATE'})
sequence_use_filter_invert: BoolProperty(default=False, options=set()) 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) 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: if pg.sequence_filter_name is not None:
# Filter name is non-empty. # Filter name is non-empty.
import fnmatch if pg.sequence_use_filter_regex:
for i, sequence in enumerate(sequences): # Use regular expression
if not fnmatch.fnmatch(sequence.action_name, f'*{pg.sequence_filter_name}*'): try:
flt_flags[i] &= ~bitflag_filter_item 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: if pg.sequence_use_filter_invert:
# Invert filter flags for all items. # Invert filter flags for all items.
for i, sequence in enumerate(sequences): for i, sequence in enumerate(sequences):
flt_flags[i] ^= ~bitflag_filter_item flt_flags[i] ^= bitflag_filter_item
return flt_flags return flt_flags
@@ -313,6 +325,7 @@ class PSA_UL_SequenceList(UIList):
subrow = row.row(align=True) subrow = row.row(align=True)
subrow.prop(pg, 'sequence_filter_name', text="") subrow.prop(pg, 'sequence_filter_name', text="")
subrow.prop(pg, 'sequence_use_filter_invert', text="", icon='ARROW_LEFTRIGHT') 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): def filter_items(self, context, data, property):
pg = context.scene.psa_import pg = context.scene.psa_import
@@ -368,7 +381,7 @@ class PsaImportSequencesFromText(Operator):
class PsaImportSequencesSelectAll(Operator): class PsaImportSequencesSelectAll(Operator):
bl_idname = 'psa_import.sequences_select_all' bl_idname = 'psa_import.sequences_select_all'
bl_label = 'All' bl_label = 'All'
bl_description = 'Select all sequences' bl_description = 'Select all visible sequences'
bl_options = {'INTERNAL'} bl_options = {'INTERNAL'}
@classmethod @classmethod
@@ -389,7 +402,7 @@ class PsaImportSequencesSelectAll(Operator):
class PsaImportSequencesDeselectAll(Operator): class PsaImportSequencesDeselectAll(Operator):
bl_idname = 'psa_import.sequences_deselect_all' bl_idname = 'psa_import.sequences_deselect_all'
bl_label = 'None' bl_label = 'None'
bl_description = 'Deselect all sequences' bl_description = 'Deselect all visible sequences'
bl_options = {'INTERNAL'} bl_options = {'INTERNAL'}
@classmethod @classmethod