Открыть боковую панель
nikitronn
sverchok
Коммиты
20704e6c
Коммит
20704e6c
создал
Июн 01, 2022
по автору
Durman
Просмотр файлов
add code documentation
владелец
41351866
Изменения
6
Скрыть пробелы
Построчно
Рядом
core/events.py
Просмотр файла @
20704e6c
...
...
@@ -6,12 +6,8 @@
# License-Filename: LICENSE
"""
Purpose of this module is centralization of update events.
For now it can be used in debug mode for understanding which event method are triggered by Blender
during evaluation of Python code.
Details: https://github.com/nortikin/sverchok/issues/3077
Events keep information about which Blender trigger was executed and with which
context
"""
from
__future__
import
annotations
...
...
@@ -29,8 +25,8 @@ if TYPE_CHECKING:
class
TreeEvent
:
"""
Keeps information about what was changed during the even
"""
# t
ask
should be run via timer only https://developer.blender.org/T82318#1053877
"""
Adding removing nodes or links but not necessarily
"""
# t
he event
should be run via timer only https://developer.blender.org/T82318#1053877
tree
:
SvTree
def
__init__
(
self
,
tree
):
...
...
@@ -41,10 +37,12 @@ class TreeEvent:
class
ForceEvent
(
TreeEvent
):
"""Indicates the whole tree should be recalculated"""
pass
class
AnimationEvent
(
TreeEvent
):
"""Frame was changed. Last event can be with the same frame"""
is_frame_changed
:
bool
is_animation_playing
:
bool
...
...
@@ -55,10 +53,12 @@ class AnimationEvent(TreeEvent):
class
SceneEvent
(
TreeEvent
):
"""Something was changed in the scene"""
pass
class
PropertyEvent
(
TreeEvent
):
"""Property of the node(s) was changed"""
updated_nodes
:
Iterable
[
SvNode
]
def
__init__
(
self
,
tree
,
updated_nodes
):
...
...
@@ -67,6 +67,7 @@ class PropertyEvent(TreeEvent):
class
GroupTreeEvent
(
TreeEvent
):
"""The same as Tree event but inside a group tree"""
tree
:
GrTree
update_path
:
list
[
GrNode
]
...
...
@@ -76,6 +77,7 @@ class GroupTreeEvent(TreeEvent):
class
GroupPropertyEvent
(
GroupTreeEvent
):
"""Property of a node(s) inside a group tree was changed"""
updated_nodes
:
Iterable
[
SvNode
]
def
__init__
(
self
,
tree
,
update_path
,
update_nodes
):
...
...
@@ -84,8 +86,11 @@ class GroupPropertyEvent(GroupTreeEvent):
class
FileEvent
:
"""It indicates that new file was loaded"""
pass
class
TreesGraphEvent
:
"""It indicates that something was changed in trees relations defined via
group nodes"""
pass
core/group_update_system.py
Просмотр файла @
20704e6c
...
...
@@ -20,11 +20,10 @@ def control_center(event):
1. Update tree model lazily
2. Check whether the event should be processed
3. Process event or create task to process via timer"""
was_executed
=
Fals
e
was_executed
=
Tru
e
# property of some node of a group tree was changed
if
type
(
event
)
is
ev
.
GroupPropertyEvent
:
was_executed
=
True
gr_tree
=
GroupUpdateTree
.
get
(
event
.
tree
)
gr_tree
.
add_outdated
(
event
.
updated_nodes
)
gr_tree
.
update_path
=
event
.
update_path
...
...
@@ -35,10 +34,9 @@ def control_center(event):
# topology of a group tree was changed
elif
type
(
event
)
is
ev
.
GroupTreeEvent
:
was_executed
=
True
gr_tree
=
GroupUpdateTree
.
get
(
event
.
tree
)
gr_tree
.
is_updated
=
False
#
gr_tree.update_path = event.update_path
gr_tree
.
update_path
=
event
.
update_path
for
main_tree
in
trees_graph
[
event
.
tree
]:
us
.
UpdateTree
.
get
(
main_tree
).
add_outdated
(
trees_graph
[
main_tree
,
event
.
tree
])
if
main_tree
.
sv_process
:
...
...
@@ -46,16 +44,25 @@ def control_center(event):
# Connections between trees were changed
elif
type
(
event
)
is
ev
.
TreesGraphEvent
:
was_executed
=
True
trees_graph
.
is_updated
=
False
else
:
was_executed
=
False
return
was_executed
class
GroupUpdateTree
(
us
.
UpdateTree
):
get
:
Callable
[[
'GrTree'
],
'GroupUpdateTree'
]
"""Group trees has their own update method separate from main tree to have
more nice profiling statistics. Also, it keeps some specific to grop trees
statuses."""
get
:
Callable
[[
'GrTree'
],
'GroupUpdateTree'
]
# type hinting does not work grate :/
def
update
(
self
,
node
:
'GrNode'
):
"""Updates outdated nodes of group tree. Also, it keeps proper state of
the exec_path. If exec_path is equal to update path it also updates UI
of the tree
:node: group node which tree is executed"""
self
.
_exec_path
.
append
(
node
)
try
:
is_opened_tree
=
self
.
update_path
==
self
.
_exec_path
...
...
@@ -71,7 +78,7 @@ class GroupUpdateTree(us.UpdateTree):
if
is_opened_tree
:
if
self
.
_tree
.
show_time_mode
==
"Cumulative"
:
times
=
self
.
calc_cam_update_time
()
times
=
self
.
_
calc_cam_update_time
()
else
:
times
=
None
us
.
update_ui
(
self
.
_tree
,
times
)
...
...
@@ -82,6 +89,13 @@ class GroupUpdateTree(us.UpdateTree):
self
.
_exec_path
.
pop
()
def
__init__
(
self
,
tree
):
"""Should node be used directly but wia the get class method
:update_path: list of group nodes via which update trigger was executed
:_exec_path: list of group nodes via which the tree is executed
:_viewer_nodes: output nodes which should be updated. If not presented
all output nodes will be updated. The main reason of having them is to
update viewer nodes only in opened group tree, as a side effect it
optimises nodes execution"""
super
().
__init__
(
tree
)
# update UI for the tree opened under the given path
self
.
update_path
:
list
[
'GrNode'
]
=
[]
...
...
@@ -94,6 +108,11 @@ class GroupUpdateTree(us.UpdateTree):
self
.
_copy_attrs
.
extend
([
'_exec_path'
,
'update_path'
,
'_viewer_nodes'
])
def
_walk
(
self
)
->
tuple
[
Node
,
list
[
NodeSocket
]]:
"""Yields nodes in order of their proper execution. It starts yielding
from outdated nodes. It keeps the outdated_nodes storage in proper
state. It checks after yielding the error status of the node. If the
node has error it goes into outdated_nodes. If tree has viewer nodes
it yields only nodes which should be called to update viewers."""
# walk all nodes in the tree
if
self
.
_outdated_nodes
is
None
:
outdated
=
None
...
...
@@ -104,7 +123,7 @@ class GroupUpdateTree(us.UpdateTree):
else
:
outdated
=
frozenset
(
self
.
_outdated_nodes
)
viewers
=
frozenset
(
self
.
_viewer_nodes
)
self
.
_outdated_nodes
.
clear
()
# todo what if execution was canceled?
self
.
_outdated_nodes
.
clear
()
self
.
_viewer_nodes
.
clear
()
for
node
,
other_socks
in
self
.
_sort_nodes
(
outdated
,
viewers
):
...
...
@@ -118,10 +137,17 @@ class GroupUpdateTree(us.UpdateTree):
class
TreesGraph
:
"""It keeps relationships between main trees and group trees."""
_group_main
:
dict
[
'GrTree'
,
set
[
'SvTree'
]]
_entry_nodes
:
dict
[
'SvTree'
,
dict
[
'GrTree'
,
set
[
'SvNode'
]]]
def
__init__
(
self
):
""":is_updated: the graph can be marked as outdated in this case it will
be updated automatically whenever data will be fetched from it
:_group_main: it stores information about in which main trees a group
tree is used. The group tree can be located in some nested groups too
:_entry_nodes: it stores information about which group nodes in main
tree should be called to update a group tree"""
self
.
is_updated
=
False
self
.
_group_main
=
defaultdict
(
set
)
...
...
@@ -133,7 +159,8 @@ class TreesGraph:
def
__getitem__
(
self
,
item
:
tuple
[
'SvTree'
,
'GrTree'
])
->
set
[
'SvNode'
]:
...
def
__getitem__
(
self
,
item
):
# print(self)
"""It either returns related to given group tree Main tree or collection
of group nodes to update given group tree"""
if
not
self
.
is_updated
:
self
.
_update
()
if
isinstance
(
item
,
tuple
):
...
...
@@ -143,7 +170,7 @@ class TreesGraph:
return
self
.
_group_main
[
item
]
def
_update
(
self
):
# print("REFRESH TreesGraph")
"""Calculate relationships between group trees and main trees"""
self
.
_group_main
.
clear
()
self
.
_entry_nodes
.
clear
()
for
tree
in
BlTrees
().
sv_main_trees
:
...
...
@@ -154,6 +181,7 @@ class TreesGraph:
@
staticmethod
def
_walk
(
from_
:
NodeTree
)
->
Iterator
[
tuple
[
NodeTree
,
'SvNode'
]]:
"""Iterate over all nested node trees"""
current_entry_node
=
None
def
next_
(
_tree
):
...
...
core/tasks.py
Просмотр файла @
20704e6c
...
...
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
class
Tasks
:
"""
It keeps tasks which should be executed and executes the on demand.
1. Execute tasks
2. Time the whole execution
3. Display the progress in the UI
...
...
@@ -24,17 +25,23 @@ class Tasks:
_current
:
Optional
[
'Task'
]
def
__init__
(
self
):
""":_todo: list of tasks to run
:_current: task which was started to execute"""
self
.
_todo
=
set
()
self
.
_current
=
None
def
__bool__
(
self
):
"""Has anything to do?"""
return
bool
(
self
.
_current
or
self
.
_todo
)
def
add
(
self
,
task
:
'Task'
):
"""Add new tasks to run them via timer"""
self
.
_todo
.
add
(
task
)
@
profile
(
section
=
"UPDATE"
)
def
run
(
self
):
"""Run given tasks to update trees and report execution process in the
header of a node tree editor"""
max_duration
=
0.15
# 0.15 is max timer frequency
duration
=
0
...
...
@@ -52,6 +59,7 @@ class Tasks:
self
.
_finish
()
def
cancel
(
self
):
"""Remove all tasks in the queue and abort current one"""
self
.
_todo
.
clear
()
if
self
.
_current
:
try
:
...
...
@@ -63,6 +71,8 @@ class Tasks:
@
property
def
current
(
self
)
->
Optional
[
'Task'
]:
"""Return current task if it is absent it tries to pop it from the tasks
queue if it's empty returns None"""
if
self
.
_current
:
return
self
.
_current
elif
self
.
_todo
:
...
...
@@ -73,15 +83,19 @@ class Tasks:
return
None
def
_start
(
self
):
"""Preprocessing before executing the whole queue of events"""
self
.
_start_time
gc
.
disable
()
# for performance
def
_next
(
self
):
"""Should be called to switch to next tasks when current is exhausted
It made some cleanups after the previous task"""
self
.
_report_progress
()
self
.
_current
=
self
.
_todo
.
pop
()
if
self
.
_todo
else
None
del
self
.
_main_area
def
_finish
(
self
):
"""Cleanups. Also triggers scene handler and mark trees to skip it"""
self
.
_report_progress
()
del
self
.
_main_area
...
...
@@ -100,6 +114,7 @@ class Tasks:
@
cached_property
def
_start_time
(
self
):
"""Start time of execution the whole queue of tasks"""
return
time
()
@
cached_property
...
...
@@ -114,6 +129,8 @@ class Tasks:
return
area
def
_report_progress
(
self
,
text
:
str
=
None
):
"""Show text in the tree editor header. If text is none the header
returns in its initial condition"""
if
self
.
_main_area
:
self
.
_main_area
.
header_text_set
(
text
)
...
...
@@ -133,7 +150,15 @@ tree_event_loop = partial(tree_event_loop, 0.01)
class
Task
:
"""Generator which should update some node tree. The task is hashable, and
it is equal to another task if booth of them update the same tree.
The generator is suspendable and can limit its execution by given time"""
def
__init__
(
self
,
tree
,
updater
):
""":tree: tree which should be updated
:_updater: generator which should update given tree
:is_exhausted: the status of the generator - read only
:last_node: last node which going to be processed by the generator
- read only"""
self
.
tree
:
SvTree
=
tree
self
.
is_exhausted
=
False
self
.
last_node
=
None
...
...
@@ -142,6 +167,9 @@ class Task:
self
.
__hash__
=
cache
(
self
.
__hash__
)
def
run
(
self
,
max_duration
):
"""Starts the tree updating
:max_duration: if updating of the tree takes more time than given
maximum duration it saves its state and returns execution flow"""
duration
=
0
try
:
start_time
=
time
()
...
...
@@ -154,14 +182,12 @@ class Task:
self
.
is_exhausted
=
True
return
duration
def
throw
(
self
,
error
):
def
throw
(
self
,
error
:
CancelError
):
"""Should be used to cansel tree execution. Updater should add
the error to current node and abort the execution"""
self
.
_updater
.
throw
(
error
)
self
.
is_exhausted
=
True
@
property
def
id
(
self
):
return
self
.
tree
.
tree_id
def
__eq__
(
self
,
other
:
'Task'
):
return
self
.
tree
.
tree_id
==
other
.
tree
.
tree_id
...
...
core/update_system.py
Просмотр файла @
20704e6c
...
...
@@ -30,28 +30,25 @@ def control_center(event):
1. Update tree model lazily
2. Check whether the event should be processed
3. Process event or create task to process via timer"""
was_executed
=
Fals
e
was_executed
=
Tru
e
# frame update
# This event can't be handled via NodesUpdater during animation rendering
# because new frame change event can arrive before timer finishes its tusk.
# Or timer can start working before frame change is handled.
if
type
(
event
)
is
ev
.
AnimationEvent
:
was_executed
=
True
if
event
.
tree
.
sv_animate
:
UpdateTree
.
get
(
event
.
tree
).
is_animation_updated
=
False
UpdateTree
.
update_animation
(
event
)
# something changed in the scene
elif
type
(
event
)
is
ev
.
SceneEvent
:
was_executed
=
True
if
event
.
tree
.
sv_scene_update
and
event
.
tree
.
sv_process
:
UpdateTree
.
get
(
event
.
tree
).
is_scene_updated
=
False
ts
.
tasks
.
add
(
ts
.
Task
(
event
.
tree
,
UpdateTree
.
main_update
(
event
.
tree
)))
# nodes changed properties
elif
type
(
event
)
is
ev
.
PropertyEvent
:
was_executed
=
True
tree
=
UpdateTree
.
get
(
event
.
tree
)
tree
.
add_outdated
(
event
.
updated_nodes
)
if
event
.
tree
.
sv_process
:
...
...
@@ -59,26 +56,29 @@ def control_center(event):
# update the whole tree anyway
elif
type
(
event
)
is
ev
.
ForceEvent
:
was_executed
=
True
UpdateTree
.
reset_tree
(
event
.
tree
)
ts
.
tasks
.
add
(
ts
.
Task
(
event
.
tree
,
UpdateTree
.
main_update
(
event
.
tree
)))
# mark that the tree topology has changed
elif
type
(
event
)
is
ev
.
TreeEvent
:
was_executed
=
True
UpdateTree
.
get
(
event
.
tree
).
is_updated
=
False
if
event
.
tree
.
sv_process
:
ts
.
tasks
.
add
(
ts
.
Task
(
event
.
tree
,
UpdateTree
.
main_update
(
event
.
tree
)))
# new file opened
elif
type
(
event
)
is
ev
.
FileEvent
:
was_executed
=
True
UpdateTree
.
reset_tree
()
else
:
was_executed
=
False
return
was_executed
class
SearchTree
:
"""Data structure which represents Blender node trees but with ability
of efficient search tree elements. Also it keeps tree state so it can be
compared with new one to define differences."""
_from_nodes
:
dict
[
'SvNode'
,
set
[
'SvNode'
]]
_to_nodes
:
dict
[
'SvNode'
,
set
[
'SvNode'
]]
_from_sock
:
dict
[
NodeSocket
,
NodeSocket
]
...
...
@@ -107,6 +107,7 @@ class SearchTree:
self
.
_remove_wifi_nodes
()
def
nodes_from
(
self
,
from_nodes
:
Iterable
[
'SvNode'
])
->
set
[
'SvNode'
]:
"""Returns all next nodes from given ones"""
def
node_walker_to
(
node_
:
'SvNode'
):
for
nn
in
self
.
_to_nodes
.
get
(
node_
,
[]):
yield
nn
...
...
@@ -114,6 +115,7 @@ class SearchTree:
return
set
(
bfs_walk
(
from_nodes
,
node_walker_to
))
def
nodes_to
(
self
,
to_nodes
:
Iterable
[
'SvNode'
])
->
set
[
'SvNode'
]:
"""Returns all previous nodes from given ones"""
def
node_walker_from
(
node_
:
'SvNode'
):
for
nn
in
self
.
_from_nodes
.
get
(
node_
,
[]):
yield
nn
...
...
@@ -121,6 +123,7 @@ class SearchTree:
return
set
(
bfs_walk
(
to_nodes
,
node_walker_from
))
def
sort_nodes
(
self
,
nodes
:
Iterable
[
'SvNode'
])
->
list
[
'SvNode'
]:
"""Returns nodes in order of their correct execution"""
walk_structure
:
dict
[
SvNode
,
set
[
SvNode
]]
=
defaultdict
(
set
)
for
n
in
nodes
:
if
n
in
self
.
_from_nodes
:
...
...
@@ -131,11 +134,17 @@ class SearchTree:
nodes
.
append
(
node
)
return
nodes
def
previous_sockets
(
self
,
node
:
'SvNode'
)
->
list
[
NodeSocket
]:
def
previous_sockets
(
self
,
node
:
'SvNode'
)
->
list
[
Optional
[
NodeSocket
]]:
"""Return output sockets connected to input ones of given node
If input socket is not linked the output socket will be None"""
return
[
self
.
_from_sock
.
get
(
s
)
for
s
in
node
.
inputs
]
def
update_node
(
self
,
node
:
'SvNode'
,
supress
=
True
):
with
AddStatistic
(
node
,
supress
):
def
update_node
(
self
,
node
:
'SvNode'
,
suppress
=
True
):
"""Fetches data from previous node, makes data conversion if connected
sockets have different types, calls process method of the given node
records nodes statistics
If suppress is True an error during node execution will be suppressed"""
with
AddStatistic
(
node
,
suppress
):
prepare_input_data
(
self
.
previous_sockets
(
node
),
node
.
inputs
)
node
.
process
()
...
...
@@ -251,15 +260,16 @@ class SearchTree:
class
UpdateTree
(
SearchTree
):
"""It ca
t
ches
some data for more efficient searches compare to Blender
tree data structure
"""
"""It caches
the trees to keep outdated nodes and to perform tree updating
efficiently.
"""
_tree_catch
:
dict
[
str
,
'UpdateTree'
]
=
dict
()
# the module should be auto-reloaded to prevent crashes
@
classmethod
def
get
(
cls
,
tree
:
"SvTree"
,
refresh_tree
=
False
)
->
"UpdateTree"
:
"""
Get cached tree. If tree was not cached it will be.
:refresh_tree: if True it will convert update flags into outdated
nodes. This can be expensive so it should be called only before tree
nodes. This can be expensive
,
so it should be called only before tree
reevaluation
"""
if
tree
.
tree_id
not
in
cls
.
_tree_catch
:
...
...
@@ -291,6 +301,7 @@ class UpdateTree(SearchTree):
@
classmethod
@
profile
(
section
=
"UPDATE"
)
def
update_animation
(
cls
,
event
:
ev
.
AnimationEvent
):
"""Should be called to updated animated nodes"""
try
:
g
=
cls
.
main_update
(
event
.
tree
,
event
.
is_frame_changed
,
not
event
.
is_animation_playing
)
while
True
:
...
...
@@ -300,8 +311,11 @@ class UpdateTree(SearchTree):
@
classmethod
def
main_update
(
cls
,
tree
:
NodeTree
,
update_nodes
=
True
,
update_interface
=
True
)
->
Generator
[
'SvNode'
,
None
,
None
]:
"""Only for main trees
1. Whe it called the tree should have information of what is outdated"""
"""Thi generator is for the triggers. It can update outdated nodes and
update UI. Should be used only with main trees, the group trees should
use different method to separate profiling statistics. Whe it called the
tree should have information of what is outdated"""
# print(f"UPDATE NODES {event.type=}, {event.tree.name=}")
up_tree
=
cls
.
get
(
tree
,
refresh_tree
=
True
)
if
update_nodes
:
...
...
@@ -318,14 +332,14 @@ class UpdateTree(SearchTree):
if
update_interface
:
if
up_tree
.
_tree
.
show_time_mode
==
"Cumulative"
:
times
=
up_tree
.
calc_cam_update_time
()
times
=
up_tree
.
_
calc_cam_update_time
()
else
:
times
=
None
update_ui
(
tree
,
times
)
@
classmethod
def
reset_tree
(
cls
,
tree
:
NodeTree
=
None
):
"""Remove tree data or data of all trees"""
"""Remove tree data or data of all trees
from the cache
"""
if
tree
is
not
None
and
tree
.
tree_id
in
cls
.
_tree_catch
:
del
cls
.
_tree_catch
[
tree
.
tree_id
]
else
:
...
...
@@ -333,29 +347,29 @@ class UpdateTree(SearchTree):
def
copy
(
self
)
->
'UpdateTree'
:
"""They copy will be with new topology if original tree was changed
since
berth
of the first tree. Other attributes copied as is."""
since
instancing
of the first tree. Other attributes copied as is."""
copy_
=
type
(
self
)(
self
.
_tree
)
for
attr
in
self
.
_copy_attrs
:
setattr
(
copy_
,
attr
,
copy
(
getattr
(
self
,
attr
)))
return
copy_
def
add_outdated
(
self
,
nodes
:
Iterable
):
"""Add outdated nodes explicitly. Animation and scene dependent nodes
can be marked as outdated via dedicated flags for performance."""
if
self
.
_outdated_nodes
is
not
None
:
self
.
_outdated_nodes
.
update
(
nodes
)
def
calc_cam_update_time
(
self
)
->
Iterable
[
'SvNode'
]:
cum_time_nodes
=
dict
()
# don't have frame nodes
for
node
,
prev_socks
in
self
.
__sort_nodes
():
prev_nodes
=
self
.
_from_nodes
[
node
]
if
len
(
prev_nodes
)
>
1
:
cum_time
=
sum
(
n
.
get
(
TIME_KEY
,
0
)
for
n
in
self
.
nodes_to
([
node
]))
else
:
cum_time
=
sum
(
cum_time_nodes
.
get
(
n
,
0
)
for
n
in
prev_nodes
)
cum_time
+=
node
.
get
(
TIME_KEY
,
0
)
cum_time_nodes
[
node
]
=
cum_time
return
(
cum_time_nodes
.
get
(
n
)
for
n
in
self
.
_tree
.
nodes
)
def
__init__
(
self
,
tree
:
NodeTree
):
"""Should not use be used directly, only via the get class method
:is_updated: Should be False if topology of the tree was changed
:is_animation_updated: Should be False animation dependent nodes should
be updated
:is_scene_updated: Should be False if scene dependent nodes should be
updated
:_outdated_nodes: Keeps nodes which properties were changed or which
have errors. Can be None when what means that all nodes are outdated
:_copy_attrs: list of attributes which should be copied by the copy
method"""
super
().
__init__
(
tree
)
self
.
_tree_catch
[
tree
.
tree_id
]
=
self
...
...
@@ -375,6 +389,7 @@ class UpdateTree(SearchTree):
]
def
_animation_nodes
(
self
)
->
set
[
'SvNode'
]:
"""Returns nodes which are animation dependent"""
an_nodes
=
set
()
if
not
self
.
is_animation_updated
:
for
node
in
self
.
_tree
.
nodes
:
...
...
@@ -384,6 +399,7 @@ class UpdateTree(SearchTree):
return
an_nodes
def
_scene_nodes
(
self
)
->
set
[
'SvNode'
]:
"""Returns nodes which are scene dependent"""
sc_nodes
=
set
()
if
not
self
.
is_scene_updated
:
for
node
in
self
.
_tree
.
nodes
:
...
...
@@ -393,6 +409,13 @@ class UpdateTree(SearchTree):
return
sc_nodes
def
_walk
(
self
)
->
tuple
[
Node
,
list
[
NodeSocket
]]:
"""Yields nodes in order of their proper execution. It starts yielding
from outdated nodes. It keeps the outdated_nodes storage in proper
state. It checks after yielding the error status of the node. If the
node has error it goes into outdated_nodes. It uses cached walker, so
it works more efficient when outdated nodes are the same between the
method calls."""
# walk all nodes in the tree
if
self
.
_outdated_nodes
is
None
:
outdated
=
None
...
...
@@ -415,6 +438,12 @@ class UpdateTree(SearchTree):
from_nodes
:
frozenset
[
'SvNode'
]
=
None
,
to_nodes
:
frozenset
[
'SvNode'
]
=
None
)
\
->
list
[
tuple
[
'SvNode'
,
list
[
NodeSocket
]]]:
"""Sort nodes of the tree in proper execution order. Whe all given
parameters are None it uses all tree nodes
:from_nodes: if given it sorts only next nodes from given ones
:to_nodes: if given it sorts only previous nodes from given
If from_nodes and to_nodes are given it uses only intersection of next
nodes from from_nodes and previous nodes from to_nodes"""
nodes_to_walk
=
set
()
walk_structure
=
None
if
from_nodes
is
None
and
to_nodes
is
None
:
...
...
@@ -442,6 +471,9 @@ class UpdateTree(SearchTree):
return
nodes
def
_update_difference
(
self
,
old
:
'UpdateTree'
)
->
set
[
'SvNode'
]:
"""Returns nodes which should be updated according to changes in the
tree topology
:old: previous state of the tree to compare with"""
nodes_to_update
=
self
.
_from_nodes
.
keys
()
-
old
.
_from_nodes
.
keys
()
new_links
=
self
.
_links
-
old
.
_links
for
from_sock
,
to_sock
in
new_links
:
...
...
@@ -455,7 +487,22 @@ class UpdateTree(SearchTree):
nodes_to_update
.
add
(
old
.
_sock_node
[
to_sock
])
return
nodes_to_update
def
_calc_cam_update_time
(
self
)
->
Iterable
[
'SvNode'
]:
"""Return cumulative update time in order of node_group.nodes collection"""
cum_time_nodes
=
dict
()
# don't have frame nodes
for
node
,
prev_socks
in
self
.
__sort_nodes
():
prev_nodes
=
self
.
_from_nodes
[
node
]
if
len
(
prev_nodes
)
>
1
:
cum_time
=
sum
(
n
.
get
(
TIME_KEY
,
0
)
for
n
in
self
.
nodes_to
([
node
]))
else
:
cum_time
=
sum
(
cum_time_nodes
.
get
(
n
,
0
)
for
n
in
prev_nodes
)
cum_time
+=
node
.
get
(
TIME_KEY
,
0
)
cum_time_nodes
[
node
]
=
cum_time
return
(
cum_time_nodes
.
get
(
n
)
for
n
in
self
.
_tree
.
nodes
)
def
_debug_color
(
self
,
walker
:
Generator
,
use_color
:
bool
=
True
):
"""Colorize nodes which were previously executed. Before execution, it
resets all dbug colors"""
def
_set_color
(
node
:
'SvNode'
,
_use_color
:
bool
):
use_key
=
"DEBUG_use_user_color"
color_key
=
"DEBUG_user_color"
...
...
@@ -485,9 +532,14 @@ class UpdateTree(SearchTree):
class
AddStatistic
:
"""It caches errors during execution of process method of a node and saves
update time, update status and error"""
# this probably can be inside the Node class as an update method
# using context manager from contextlib has big overhead
# https://stackoverflow.com/questions/26152934/why-the-staggering-overhead-50x-of-contextlib-and-the-with-statement-in-python
def
__init__
(
self
,
node
:
'SvNode'
,
supress
=
True
):
""":supress: if True any errors during node execution will be suppressed"""
self
.
_node
=
node
self
.
_start
=
perf_counter
()
self
.
_supress
=
supress
...
...
@@ -511,7 +563,11 @@ class AddStatistic:
return
issubclass
(
exc_type
,
Exception
)
def
prepare_input_data
(
prev_socks
,
input_socks
):
def
prepare_input_data
(
prev_socks
:
list
[
Optional
[
NodeSocket
]],
input_socks
:
list
[
NodeSocket
]):
"""Reads data from given outputs socket make it conversion if necessary and
put data into input given socket"""
# this can be a socket method
for
ps
,
ns
in
zip
(
prev_socks
,
input_socks
):
if
ps
is
None
:
continue
...
...
@@ -526,6 +582,9 @@ def prepare_input_data(prev_socks, input_socks):
def
update_ui
(
tree
:
NodeTree
,
times
:
Iterable
[
float
]
=
None
):
"""Updates UI of the given tree
:times: optional node timing in order of group_tree.nodes collection"""
# probably this can be moved to tree.update_ui method
errors
=
(
n
.
get
(
ERROR_KEY
,
None
)
for
n
in
tree
.
nodes
)
times
=
times
or
(
n
.
get
(
TIME_KEY
,
0
)
for
n
in
tree
.
nodes
)
tree
.
update_ui
(
errors
,
times
)
nodes/logic/evolver.py
Просмотр файла @
20704e6c
...
...
@@ -365,7 +365,7 @@ class DNA:
tree
.
sv_process
=
True
for
node
in
exec_order
:
try
:
s_tree
.
update_node
(
node
,
supress
=
False
)
s_tree
.
update_node
(
node
,
sup
p
ress
=
False
)
except
Exception
:
raise
...
...
nodes/logic/loop_out.py
Просмотр файла @
20704e6c
...
...
@@ -251,7 +251,7 @@ class SvLoopOutNode(SverchCustomTreeNode, bpy.types.Node):
print
(
f
"Looping Object Number
{
idx
}
"
)
for
node
in
sort_loop_nodes
[
1
:
-
1
]:
try
:
tree
.
update_node
(
node
,
supress
=
False
)
tree
.
update_node
(
node
,
sup
p
ress
=
False
)
except
Exception
:
raise
Exception
(
f
"Element:
{
idx
}
"
)
...
...
@@ -308,7 +308,7 @@ class SvLoopOutNode(SverchCustomTreeNode, bpy.types.Node):
print
(
f
"Looping iteration Number
{
i
+
1
}
"
)
for
node
in
sort_loop_nodes
[
1
:
-
1
]:
try
:
tree
.
update_node
(
node
,
supress
=
False
)
tree
.
update_node
(
node
,
sup
p
ress
=
False
)
except
Exception
:
raise
Exception
(
f
"Iteration number:
{
i
+
1
}
"
)
...
...
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать