Source code for blenderneuron.blender.blendernode

import bpy

from blenderneuron.blender.blenderroot import BlenderRoot
from blenderneuron.blender.blenderrootgroup import *
from blenderneuron.commnode import CommNode


[docs]class BlenderNode(CommNode): def __init__(self, *args, **kwargs): super(BlenderNode, self).__init__("Blender", *args, **kwargs) @property def offline_results(self): from blenderneuron.blender.dataset import is_offline return is_offline()
[docs] def try_setup_client(self, warn=True): if self.offline_results: self.client = None self.client_address = None return old_peer = getattr(self, '_last_live_peer', None) disconnected = getattr(self, 'client', None) is None result = super().try_setup_client(warn=warn) if self.client is not None: transport = vars(self.client).get('_ServerProxy__transport') peer = (self.client_address, getattr(transport, 'epoch', None)) imported = any(group.state == 'imported' for group in self.groups.values()) if old_peer is not None and imported and (disconnected or peer != old_peer): self.export_requires_confirmation = True self._last_live_peer = peer return result
@property def ui_properties(self): return bpy.data.scenes[0].BlenderNEURON
[docs] def add_group(self, name=None, include_groupless_roots=True): self.update_root_index() if name is None: name = self.find_unique_group_name() group = BlenderRootGroup(name, self) # Attach group to node self.groups[name] = group # Add group to the UI list group.add_to_UI() if include_groupless_roots: group.add_groupless_roots() return group
[docs] def update_root_index(self): from blenderneuron.blender.dataset import require_live require_live(self) # Keep track which roots have been removed from NRN roots_to_delete = set(self.root_index.keys()) # Get the list of root sections from NEURON try: root_data = self.client.get_roots() # Update new or existing root entries for i, root_info in enumerate(root_data): name = root_info["name"] existing_root = self.root_index.get(name) # Update existing root if existing_root is not None: existing_root.index = root_info["index"] existing_root.name = root_info["name"] # Don't remove roots that previously existed and are present roots_to_delete.remove(name) # Add a new root else: new_root = self.root_index[name] = BlenderRoot( root_info["index"], root_info["name"] ) # Make sure it's listed as selectable in all groups for group in self.groups.values(): new_root.add_to_UI_group(group.ui_group) except ConnectionRefusedError: root_data = [] finally: # Delete removed roots for name_to_delete in roots_to_delete: self.root_index[name_to_delete].remove(node=self)
[docs] def find_unique_group_name(self): i_name = len(self.groups.values()) while True: name = "Group." + str(i_name).zfill(3) if name in self.groups: i_name += 1 else: break return name
[docs] def get_group_data_from_neuron(self, group_list): # Convert blender groups to skeletal dicts (needed for XML rcp with NRN) # These dicts contain basic information (e.g. no 3D data, activity) blender_groups = self.get_group_dicts(group_list) # Send a request to NRN for the selected groups compressed = self.client.initialize_groups(blender_groups) # Decompress the result nrn_groups = self.decompress(compressed) return nrn_groups
[docs] def import_groups_from_neuron(self, group_list): from blenderneuron.blender.dataset import require_live require_live(self) nrn_groups = self.get_group_data_from_neuron(group_list) # Update each blender node group with the data received from NRN for nrn_group in nrn_groups: node_group = self.groups[nrn_group["name"]] print('Importing group: ' + node_group.name + ' from NEURON...') # Remove any views of the cells if node_group.view is not None: node_group.view.remove() node_group.view = None # Update blender node group with the data received from NRN node_group.from_full_NEURON_group(nrn_group) if all(group in group_list for group in self.groups.values() if group.state == 'imported'): self.export_requires_confirmation = False
[docs] def get_selected_groups(self): return [group for group in self.groups.values() if group.selected]
[docs] def get_group_dicts(self, group_list): return [group.to_dict() for group in group_list]
@property def synapse_sets(self): return bpy.context.scene.BlenderNEURON.synapse_sets
[docs] def add_synapse_set(self, name=None): new_set = self.synapse_sets.add() if name is None: i_name = len(self.synapse_sets.values()) while True: name = "SynapseSet." + str(i_name).zfill(3) if name in self.synapse_sets.keys(): i_name += 1 else: break new_set.name = name return new_set
[docs] def display_groups(self): for group in self.groups.values(): if group.selected: print('Showing group ' + group.name + ' in Blender') group.show() else: group.remove_view()
[docs] def add_neon_effect(self): """ Adds a neon-style glare effect via the compositor. """ scene = bpy.context.scene # Eevee bloom was removed in newer Blender versions. Keep using it # where it exists, and fall back to the compositor glare setup below. eevee_settings = getattr(scene, "eevee", None) if eevee_settings is not None and hasattr(eevee_settings, "use_bloom"): # pragma: no cover eevee_settings.use_bloom = True use_blender_5_compositor = bpy.app.version[0] >= 5 if use_blender_5_compositor: nt = scene.compositing_node_group if nt is None: nt = bpy.data.node_groups.new("BlenderNEURON Neon Composite", "CompositorNodeTree") scene.compositing_node_group = nt for window in bpy.context.window_manager.windows: for area in window.screen.areas: if area.type != 'VIEW_3D': continue space = area.spaces.active if hasattr(space, "shading") and hasattr(space.shading, "use_compositor"): space.shading.use_compositor = 'ALWAYS' else: # pragma: no cover scene.use_nodes = True nt = scene.node_tree nodes = nt.nodes links = nt.links # ------------------------------------------------------------ # Get or create Render Layers node # ------------------------------------------------------------ rl = nodes.get('Render Layers') if rl is None: rl = nodes.new('CompositorNodeRLayers') rl.name = 'Render Layers' # ------------------------------------------------------------ # Get or create Composite output node # ------------------------------------------------------------ comp = nodes.get('Composite') if comp is None: comp = nodes.new('NodeGroupOutput' if use_blender_5_compositor else 'CompositorNodeComposite') comp.name = 'Composite' if use_blender_5_compositor and 'Image' not in comp.inputs: nt.interface.new_socket(name="Image", in_out='OUTPUT', socket_type='NodeSocketColor') # ------------------------------------------------------------ # Optional Viewer node for compositor preview # ------------------------------------------------------------ viewer = None if not use_blender_5_compositor: # pragma: no cover viewer = nodes.get('Viewer') if viewer is None: viewer = nodes.new('CompositorNodeViewer') viewer.name = 'Viewer' viewer.use_alpha = False # ------------------------------------------------------------ # Get or create Glare node (no duplication) # ------------------------------------------------------------ glare = None for node in nodes: if node.type == 'GLARE': glare = node break if glare is None: glare = nodes.new('CompositorNodeGlare') glare.name = 'NeonGlare' def set_node_setting(node, prop_name, prop_value, input_name=None, input_value=None): if hasattr(node, prop_name): # pragma: no cover setattr(node, prop_name, prop_value) return if input_name is not None and input_name in node.inputs: node.inputs[input_name].default_value = input_value if input_value is not None else prop_value # ------------------------------------------------------------ # Configure glare for neon-style streak effect # ------------------------------------------------------------ set_node_setting(glare, 'glare_type', 'STREAKS', 'Type', 'Streaks') set_node_setting(glare, 'quality', 'HIGH', 'Quality', 'High') set_node_setting(glare, 'iterations', 3, 'Iterations') set_node_setting(glare, 'color_modulation', 0.2, 'Color Modulation') set_node_setting(glare, 'threshold', 0.1, 'Threshold') set_node_setting(glare, 'streaks', 7, 'Streaks') set_node_setting(glare, 'fade', 0.75, 'Fade') set_node_setting(glare, 'mix', 0, 'Mix') # ------------------------------------------------------------ # Remove old links to Composite/Viewer to avoid stacking # ------------------------------------------------------------ for link in list(links): output_nodes = {comp} if viewer is not None: output_nodes.add(viewer) if link.to_node in output_nodes: links.remove(link) # ------------------------------------------------------------ # Connect nodes cleanly: # Render Layers -> Glare -> Composite (+ Viewer before Blender 5) # ------------------------------------------------------------ links.new(rl.outputs['Image'], glare.inputs['Image']) links.new(glare.outputs['Image'], comp.inputs['Image']) if viewer is not None: # pragma: no cover links.new(glare.outputs['Image'], viewer.inputs['Image'])