Skip to content

Commit 630300a

Browse files
committed
feat: node panels
1 parent 86f6c0f commit 630300a

File tree

2 files changed

+139
-101
lines changed

2 files changed

+139
-101
lines changed

geometry/operator.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ def _process_node_tree(self, node_tree: GeometryNodeTree,
168168

169169
ntp_nt = NTP_GeoNodeTree(node_tree, nt_var)
170170

171+
if bpy.app.version >= (4, 0, 0):
172+
self._tree_interface_settings_v4(inner, ntp_nt)
173+
171174
for node in node_tree.nodes:
172175
self._process_node(node, ntp_nt, inner)
173176

ntp_operator.py

Lines changed: 136 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
if bpy.app.version < (4, 0, 0):
77
from bpy.types import NodeSocketInterface
8+
else:
9+
from bpy.types import NodeTreeInterfacePanel, NodeTreeInterfaceItem
810

911
import os
1012
from typing import TextIO
@@ -465,114 +467,149 @@ def _set_group_socket_default_v4(self, socket_interface: bpy.types.NodeTreeInter
465467
max_val = socket_interface.max_value
466468
self._write((f"{inner}{socket_var}.max_value = {max_val}\n"))
467469

468-
def _group_io_settings_v4(self, node: bpy.types.Node, inner: str,
469-
io: str, # TODO: convert to enum
470-
ntp_node_tree: NTP_NodeTree) -> None:
470+
def _tree_interface_settings_v4(self, inner: str,
471+
ntp_nt: NTP_NodeTree) -> None:
471472
"""
472473
Set the settings for group input and output sockets
473474
474475
Parameters:
475-
node (bpy.types.Node) : group input/output node
476476
inner (str): indentation string
477-
io (str): whether we're generating the input or output settings
478-
node_tree_var (str): variable name of the generated node tree
479-
node_tree (bpy.types.NodeTree): node tree that we're generating
480-
input and output settings for
477+
ntp_nt (NTP_NodeTree): the node tree to set the interface for
481478
"""
482-
node_tree_var = ntp_node_tree.var
483-
node_tree = ntp_node_tree.node_tree
484-
485-
if io == "input":
486-
io_sockets = node.outputs # Might be removeable,
487-
# think we can get all the info from the inouts
488-
# from the socket interfaces, need to double check.
489-
# If so, then we can just run these at the initialization
490-
# of the node tree, meaning we can clean up the clunky
491-
# Group Input/Group Output node reliance, two calls
492-
# Should be pretty easy to add in panels afterwards,
493-
# looks like those are tied fairly close to the new socket
494-
# system
495-
items_tree = node_tree.interface.items_tree
496-
io_socket_interfaces = [item for item in items_tree
497-
if item.item_type == 'SOCKET'
498-
and item.in_out == 'INPUT']
499-
else:
500-
io_sockets = node.inputs
501-
items_tree = node_tree.interface.items_tree
502-
io_socket_interfaces = [item for item in items_tree
503-
if item.item_type == 'SOCKET'
504-
and item.in_out == 'OUTPUT']
505-
506-
self._write(f"{inner}#{node_tree_var} {io}s\n")
507-
for i, socket_interface in enumerate(io_socket_interfaces):
508-
self._write(f"{inner}#{io} {socket_interface.name}\n")
509-
510-
socket_interface: bpy.types.NodeTreeInterfaceSocket = io_socket_interfaces[i]
511-
512-
# initialization
513-
socket_var = clean_string(socket_interface.name) + "_socket"
514-
name = str_to_py_str(socket_interface.name)
515-
in_out_enum = enum_to_py_str(socket_interface.in_out)
516-
517-
socket_type = enum_to_py_str(socket_interface.bl_socket_idname)
518-
"""
519-
I might be missing something, but the Python API's set up a bit
520-
weird here now. The new socket initialization only accepts types
521-
from a list of basic ones, but there doesn't seem to be a way of
522-
retrieving just this basic typewithout the subtype information.
523-
"""
524-
if 'Float' in socket_type:
525-
socket_type = enum_to_py_str('NodeSocketFloat')
526-
elif 'Int' in socket_type:
527-
socket_type = enum_to_py_str('NodeSocketInt')
528-
elif 'Vector' in socket_type:
529-
socket_type = enum_to_py_str('NodeSocketVector')
530-
531-
self._write(f"{inner}{socket_var} = "
532-
f"{node_tree_var}.interface.new_socket("
533-
f"name = {name}, in_out={in_out_enum}, "
534-
f"socket_type = {socket_type})\n")
535-
536-
# subtype
537-
if hasattr(socket_interface, "subtype"):
538-
subtype = enum_to_py_str(socket_interface.subtype)
539-
self._write(f"{inner}{socket_var}.subtype = {subtype}\n")
540-
541-
self._set_group_socket_default_v4(socket_interface, inner,
542-
socket_var)
543-
544-
# default attribute name
545-
if socket_interface.default_attribute_name != "":
546-
dan = str_to_py_str(
547-
socket_interface.default_attribute_name)
548-
self._write(
549-
(f"{inner}{socket_var}.default_attribute_name = {dan}\n"))
550479

551-
# attribute domain
552-
ad = enum_to_py_str(socket_interface.attribute_domain)
553-
self._write(f"{inner}{socket_var}.attribute_domain = {ad}\n")
554-
555-
# tooltip
556-
if socket_interface.description != "":
557-
description = str_to_py_str(socket_interface.description)
558-
self._write(
559-
(f"{inner}{socket_var}.description = {description}\n"))
560-
561-
# hide_value
562-
if socket_interface.hide_value is True:
563-
self._write(f"{inner}{socket_var}.hide_value = True\n")
564-
565-
# hide in modifier
566-
if socket_interface.hide_in_modifier is True:
567-
self._write(
568-
f"{inner}{socket_var}.hide_in_modifier = True\n")
480+
self._write(f"{inner}#{ntp_nt.var} interface\n")
481+
panel_dict: dict[NodeTreeInterfacePanel, str] = {}
482+
items_processed: set[NodeTreeInterfaceItem] = set()
569483

570-
# force non field
571-
if socket_interface.force_non_field is True:
572-
self._write(
573-
f"{inner}{socket_var}.force_non_field = True\n")
484+
def _process_items(parent: NodeTreeInterfacePanel):
485+
if parent is None:
486+
items = ntp_nt.node_tree.interface.items_tree
487+
else:
488+
items = parent.interface_items
489+
490+
for item in items:
491+
if item.parent.index != -1 and item.parent not in panel_dict:
492+
continue # child of panel not processed yet
493+
if item in items_processed:
494+
continue
495+
496+
items_processed.add(item)
497+
498+
print(item.name, items_processed)
499+
500+
if item.item_type == 'SOCKET':
501+
self._write(f"{inner}#Socket {item.name}\n")
502+
# initialization
503+
socket_var = clean_string(item.name) + "_socket"
504+
name = str_to_py_str(item.name)
505+
in_out_enum = enum_to_py_str(item.in_out)
506+
507+
socket_type = enum_to_py_str(item.bl_socket_idname)
508+
"""
509+
I might be missing something, but the Python API's set up a bit
510+
weird here now. The new socket initialization only accepts types
511+
from a list of basic ones, but there doesn't seem to be a way of
512+
retrieving just this basic type without the subtype information.
513+
"""
514+
if 'Float' in socket_type:
515+
socket_type = enum_to_py_str('NodeSocketFloat')
516+
elif 'Int' in socket_type:
517+
socket_type = enum_to_py_str('NodeSocketInt')
518+
elif 'Vector' in socket_type:
519+
socket_type = enum_to_py_str('NodeSocketVector')
520+
521+
if parent is None:
522+
optional_parent_str = ""
523+
else:
524+
optional_parent_str = f", parent = {panel_dict[parent]}"
525+
526+
self._write(f"{inner}{socket_var} = "
527+
f"{ntp_nt.var}.interface.new_socket("
528+
f"name = {name}, in_out={in_out_enum}, "
529+
f"socket_type = {socket_type}"
530+
f"{optional_parent_str})\n")
531+
532+
# subtype
533+
if hasattr(item, "subtype"):
534+
subtype = enum_to_py_str(item.subtype)
535+
self._write(f"{inner}{socket_var}.subtype = {subtype}\n")
536+
537+
self._set_group_socket_default_v4(item, inner,
538+
socket_var)
539+
540+
# default attribute name
541+
if item.default_attribute_name != "":
542+
dan = str_to_py_str(
543+
item.default_attribute_name)
544+
self._write(
545+
(f"{inner}{socket_var}.default_attribute_name = {dan}\n"))
546+
547+
# attribute domain
548+
ad = enum_to_py_str(item.attribute_domain)
549+
self._write(f"{inner}{socket_var}.attribute_domain = {ad}\n")
550+
551+
# hide_value
552+
if item.hide_value is True:
553+
self._write(f"{inner}{socket_var}.hide_value = True\n")
554+
555+
# hide in modifier
556+
if item.hide_in_modifier is True:
557+
self._write(
558+
f"{inner}{socket_var}.hide_in_modifier = True\n")
559+
560+
# force non field
561+
if item.force_non_field is True:
562+
self._write(
563+
f"{inner}{socket_var}.force_non_field = True\n")
564+
565+
# tooltip
566+
if item.description != "":
567+
description = str_to_py_str(item.description)
568+
self._write(
569+
(f"{inner}{socket_var}.description = {description}\n"))
570+
571+
self._write("\n")
572+
573+
elif item.item_type == 'PANEL':
574+
575+
self._write(f"{inner}#Panel {item.name}\n")
576+
577+
panel_var = clean_string(item.name) + "_panel"
578+
panel_dict[item] = panel_var
579+
580+
description_str = ""
581+
if item.description != "":
582+
description_str = f", description = {str_to_py_str(item.description)}"
583+
584+
closed_str = ""
585+
if item.default_closed is True:
586+
closed_str = f", default_closed=True"
587+
588+
parent_str = ""
589+
if parent is not None:
590+
parent_str = f", parent = {panel_dict[parent]}"
591+
592+
593+
self._write(f"{inner}{panel_var} = "
594+
f"{ntp_nt.var}.interface.new_panel("
595+
f"{str_to_py_str(item.name)}{description_str}"
596+
f"{closed_str}{parent_str})\n")
597+
598+
# tooltip
599+
if item.description != "":
600+
description = str_to_py_str(item.description)
601+
self._write(
602+
(f"{inner}{panel_var}.description = {description}\n"))
603+
604+
panel_dict[item] = panel_var
605+
606+
if len(item.interface_items) > 0:
607+
_process_items(item)
608+
609+
self._write("\n")
610+
611+
_process_items(None)
574612

575-
self._write("\n")
576613
self._write("\n")
577614

578615
def _group_io_settings(self, node: bpy.types.Node, inner: str,
@@ -591,8 +628,6 @@ def _group_io_settings(self, node: bpy.types.Node, inner: str,
591628
"""
592629
if bpy.app.version < (4, 0, 0):
593630
self._group_io_settings_v3(node, inner, io, ntp_node_tree)
594-
else:
595-
self._group_io_settings_v4(node, inner, io, ntp_node_tree)
596631

597632
def _set_input_defaults(self, node: bpy.types.Node, inner: str,
598633
node_var: str) -> None:

0 commit comments

Comments
 (0)