Added regex fitlering to the PSA sequence list
This commit is contained in:
@@ -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}')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,7 +279,16 @@ 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:
|
||||||
|
# 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):
|
for i, sequence in enumerate(sequences):
|
||||||
if not fnmatch.fnmatch(sequence.action_name, f'*{pg.sequence_filter_name}*'):
|
if not fnmatch.fnmatch(sequence.action_name, f'*{pg.sequence_filter_name}*'):
|
||||||
flt_flags[i] &= ~bitflag_filter_item
|
flt_flags[i] &= ~bitflag_filter_item
|
||||||
@@ -284,7 +296,7 @@ def filter_sequences(pg: PsaImportPropertyGroup, 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
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user