import bpy
from enum import Enum
from ... utility import method_handler
from ... ui_framework import form_ui as form
from ... utility.base_modal_controls import Base_Modal_Controls
from ... ui_framework.master import Master
from ... ui_framework.utils.dialogue import Dialogue
from .cycle_node import validate_context
'''
Notes
- Ability to save a selection set as a node group
- Abilty to explode a group into the graph (something to detect if it was originally a group?)
- On exit : Remove all the other node groups
- Probably should add node groups in the current blend into the main scrolling group
- Keep an eye on : adding groups when blender auto numbers things
- Saving groups : probably the reverse of reading groups
- Write code notes:
- It overwrites every thing in the other file
- Might need to save every thing that was loaded in plus every thing created
- When saving files : it will save the node group that the main node group is also in
- Basically make sure to not save the "modifier main group"
'''
DESC = """Scroll / Save : Nodes
Press H for help"""
class HOPS_OT_Cycle_Node_Groups(bpy.types.Operator):
bl_idname = "hops.cycle_node_groups"
bl_label = "Cycle Geo Nodes"
bl_description = DESC
bl_options = {"REGISTER", "UNDO", "BLOCKING"}
@classmethod
def poll(cls, context):
if context.area:
if context.area.ui_type in {'GeometryNodeTree', 'ShaderNodeTree'}:
return True
return False
def invoke(self, context, event):
# Validate
if not validate_context(context):
return {'CANCELLED'}
# NOTE: Write code
# groups = {*bpy.data.node_groups}
# bpy.data.libraries.write(filepath=filepath, datablocks=groups, fake_user=True)
filedir = 'C:/Users/smwga/Desktop/Demos/NODE-Groups.blend'
self.imported = []
with bpy.data.libraries.load(filedir) as (data_from, data_to):
internal_groups = bpy.data.node_groups
external_groups = getattr(data_from, 'node_groups')
self.imported = [g for g in external_groups if g not in internal_groups]
setattr(data_to, 'node_groups', self.imported)
print(self.imported)
# Tool Shelf
self.show_region = bpy.context.space_data.show_region_ui
bpy.context.space_data.show_region_ui = False
# Form
self.form_exit = False
self.form = None
self.setup_form(context, event)
self.draw_handle_2D = bpy.types.SpaceNodeEditor.draw_handler_add(self.safe_draw_2D, (context,), 'WINDOW', 'POST_PIXEL')
# Input
self.dialogue = Dialogue(context, self.set_dialogue, help_text="Enter : Group Name")
self.dialogue_str = ""
# Base Systems
self.master = Master(context)
self.master.only_use_fast_ui = True
self.base_controls = Base_Modal_Controls(context, event)
context.window_manager.modal_handler_add(self)
return {"RUNNING_MODAL"}
def modal(self, context, event):
self.base_controls.update(context, event)
self.form.update(context, event)
skip_m_h = self.form.active()
self.master.receive_event(event, skip_m_h)
unblocked = True
if self.dialogue.active or self.form.active():
unblocked = False
if self.base_controls.pass_through:
if unblocked:
return {'PASS_THROUGH'}
elif self.base_controls.confirm:
if unblocked:
return self.confirm_exit(context)
elif self.base_controls.cancel:
if unblocked:
return self.cancel_exit(context)
if event.type == 'TAB' and event.value == 'PRESS':
if not self.dialogue.active:
if self.form.is_dot_open():
self.form.close_dot()
else:
self.form.open_dot()
# Dialogue Menu : Update
if self.dialogue.active:
self.dialogue.update(event)
# Actions
if unblocked:
self.actions(context, event)
self.interface(context)
context.area.tag_redraw()
return {"RUNNING_MODAL"}
def actions(self, context, event):
# Tool Shelf
if event.type == 'N' and event.value == 'PRESS':
return {'PASS_THROUGH'}
# Cycle Groups
elif self.base_controls.scroll:
scroll = self.base_controls.scroll
# Dialogue Menu : Spawn
elif event.type == 'S' and event.value == 'PRESS':
self.dialogue.start()
def interface(self, context):
self.master.setup()
if not self.master.should_build_fast_ui(): return
# Main
win_list = []
w_append = win_list.append
w_append("XXX")
# Help
help_items = {"GLOBAL" : [], "STANDARD" : []}
help_items["GLOBAL"] = [
("H", "Toggle help"),
("N", "Toggle Side Panel"),
("~", "Toggle UI Display Type"),]
h_append = help_items["STANDARD"].append
h_append(('TAB' , 'Dot UI'))
h_append(('S' , 'Save Dialogue'))
help_items["STANDARD"].reverse()
self.master.receive_fast_ui(win_list=win_list, help_list=help_items, image="green")
self.master.finished()
# --- FORM --- #
def setup_form(self, context, event):
self.form = form.Form(context, event, dot_open=False)
def spacer(height=10, label='', active=True):
row = self.form.row()
row.add_element(form.Spacer(height=height))
self.form.row_insert(row, label, active)
# --- All --- #
self.form.build()
# --- UTILS --- #
def set_dialogue(self, string):
self.dialogue_str = string
# --- EXITS --- #
def common_exit(self, context):
self.remove_shaders()
self.form.shut_down(context)
self.master.run_fade()
bpy.context.space_data.show_region_ui = self.show_region
context.area.tag_redraw()
def confirm_exit(self, context):
self.common_exit(context)
return {'FINISHED'}
def cancel_exit(self, context):
self.common_exit(context)
return {'CANCELLED'}
# --- SHADERS --- #
def remove_shaders(self):
if self.draw_handle_2D:
self.draw_handle_2D = bpy.types.SpaceNodeEditor.draw_handler_remove(self.draw_handle_2D, "WINDOW")
def safe_draw_2D(self, context):
method_handler(self.draw_shader_2D,
arguments = (context,),
identifier = 'Modal Shader 2D',
exit_method = self.remove_shaders)
def draw_shader_2D(self, context):
self.form.draw()
self.dialogue.draw()