1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
#
# This file is part of Power Sequencer.
#
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
from .utils.global_settings import SequenceTypes
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
class POWER_SEQUENCER_OT_select_related_strips(bpy.types.Operator):
"""
*brief* Find and select all strips related to the selection
Find and select effects related to the selection, but also inputs of selected effects.
This helps to then copy or duplicate strips with all attached effects.
"""
doc = {
"name": doc_name(__qualname__),
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [],
"keymap": "Sequencer",
}
bl_idname = doc_idname(__qualname__)
bl_label = doc["name"]
bl_description = doc_brief(doc["description"])
bl_options = {"REGISTER", "UNDO"}
find_all: bpy.props.BoolProperty(
name="Find All",
description=(
"Find all related strips recursively so that you can copy the selection"
" without getting an error from Blender"
),
default=True,
)
@classmethod
def poll(cls, context):
return context.selected_sequences
def execute(self, context):
if self.find_all:
related_strips = set()
for s in context.selected_sequences:
self.find_neighbours_recursive(related_strips, s, context)
else:
related_strips = []
# Only select direct neighbours and attached effects
effects = [s for s in context.sequences if s.type in SequenceTypes.EFFECT]
found_effects = self.find_related_effects(context.selected_sequences, effects)
related_strips.extend(found_effects)
while len(found_effects) > 0:
found_effects = self.find_related_effects(found_effects, effects)
related_strips.extend(found_effects)
for s in related_strips:
s.select = True
return {"FINISHED"}
def find_neighbours_recursive(self, visited, strip, context):
"""
Performs a depth first search traversal to the graph of strips, to find
all related strips.
Args:
- visited: A set with all the strips that have been visited.
- strip: The strip to start the search from.
"""
visited.add(strip)
neighbours = self.find_neighbours(strip, context)
for s in neighbours:
if s not in visited:
self.find_neighbours_recursive(visited, s, context)
def find_neighbours(self, strip, context):
"""
Strips and their effect strips define a graph, where each node is a
strip and edges are their connections. It finds all the neighbours of a
strip in the graph, and *sometimes neighbours of neighbours and so on*.
*In order to find the neighbours of a strip the
bpy.ops.transform.seq_slide operator is used, and usually finds many
levels of neighbours, but always finds the first level, which is needed,
the other levels are redundant, but are included for brevity reasons.
Args:
- strip: The strip to find all its neighbours.
Returns: A list with all the neighbours of the strip and sometimes
neighbours of neighbours and so on.
"""
# Respects initial selection
init_selected_strips = [s for s in context.selected_sequences]
neighbours = []
bpy.ops.sequencer.select_all(action="DESELECT")
strip.select = True
bpy.ops.transform.seq_slide(value=(0, 0))
strip.select = False
for s in context.selected_sequences:
neighbours.append(s)
try:
neighbours.append(strip.input_1)
neighbours.append(strip.input_2)
except Exception:
pass
bpy.ops.sequencer.select_all(action="DESELECT")
for s in init_selected_strips:
s.select = True
return neighbours
def find_related_effects(self, sequences, effects):
found = []
for s in sequences:
for e in effects:
try:
if e.input_1 == s:
found.append(e)
except Exception:
continue
try:
if e.input_2 == s:
found.append(e)
except Exception:
continue
return found
|