NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

controller.py (24261B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\nodegraph\client\ui\controller.py
      2 from __future__ import print_function
      3 from copy import deepcopy
      4 import blue
      5 import signals
      6 import threadutils
      7 from utillib import KeyVal
      8 import uthread2
      9 import uuid
     10 import eveformat
     11 from nodegraph.common.nodedata import get_node_graph_data, reload_node_graph_data, get_node_type, get_remote_graph_id_from_data, get_scope_from_data
     12 from nodegraph.common.atomdata import get_atom_data
     13 from nodegraph.client.graph import ClientNodeGraph
     14 from nodegraph.client.nodes import get_node_class
     15 from nodegraph.client.custom_graph import get_custom_node_graph, get_custom_node_graph_authored_id, get_custom_node_graphs, save_custom_node_graphs, delete_custom_node_graph, customize_fsd_graph, save_fsd_node_graph, update_parameter, update_parameter_connection, get_new_node_data, copy_graph_to_clipboard, convert_to_dict_nodes, convert_from_clipboard
     16 
     17 def open_node_graph(node_graph_id):
     18     from nodegraph.client.ui.window import NodeGraphEditorWindow
     19     NodeGraphEditorWindow.Open(node_graph_id=node_graph_id)
     20 
     21 
     22 class NodeGraphController(object):
     23 
     24     def __init__(self, node_graph_id):
     25         self.node_graph_id = node_graph_id
     26         self.node_graph = None
     27         self.selected_nodes = set()
     28         self._settings = None
     29         self._node_controllers = {}
     30         self.on_update = signals.Signal('on_update')
     31         self.on_selected_nodes = signals.Signal('on_selected_nodes')
     32         self.on_focus_node = signals.Signal('on_focus_node')
     33         self.on_highlight_node = signals.Signal('on_highlight_node')
     34         sm.RegisterForNotifyEvent(self, 'OnNodeGraphStarted')
     35         sm.RegisterForNotifyEvent(self, 'OnNodeGraphStopped')
     36         self._initialize()
     37 
     38     def _initialize(self):
     39         from .controllers import get_node_graph_settings
     40         self._settings = get_node_graph_settings(self.node_graph_id)
     41 
     42     def close(self):
     43         sm.UnregisterForNotifyEvent(self, 'OnNodeGraphStarted')
     44         sm.UnregisterForNotifyEvent(self, 'OnNodeGraphStopped')
     45 
     46     def OnNodeGraphStarted(self, instance_id, node_graph_id):
     47         node_graph = self.service.get_active_node_graph(instance_id) or self.service.log_graphs.get(instance_id)
     48         if node_graph and node_graph.graph_id == self.node_graph_id:
     49             if hasattr(self.node_graph, 'instance_id'):
     50                 self.on_update()
     51             else:
     52                 open_node_graph(instance_id)
     53 
     54     def OnNodeGraphStopped(self, instance_id, node_graph_id):
     55         if node_graph_id == self.node_graph_id:
     56             self.on_update()
     57 
     58     @property
     59     def service(self):
     60         return sm.GetService('node_graph')
     61 
     62     @property
     63     def name(self):
     64         return self.node_graph.name
     65 
     66     @property
     67     def description(self):
     68         return self.node_graph.description
     69 
     70     @property
     71     def nodes_data(self):
     72         return self.node_graph.nodes_data
     73 
     74     @property
     75     def parent_graph_id(self):
     76         return self.node_graph.parent_graph_id
     77 
     78     @property
     79     def tags(self):
     80         return self.node_graph.tags
     81 
     82     @property
     83     def is_server_graph(self):
     84         return 'server' in self.tags
     85 
     86     def get_node_data(self, node_id):
     87         return self.nodes_data[node_id]
     88 
     89     def get_node_controller(self, node_id):
     90         from nodegraph.client.ui.controllers.node_controller import NodeViewController
     91         if node_id not in self._node_controllers:
     92             self._node_controllers[node_id] = NodeViewController(node_id=node_id, graph_controller=self)
     93         return self._node_controllers[node_id]
     94 
     95     def get_node_context_menu(self, node_id):
     96         node_data = self.nodes_data[node_id]
     97         nodeTypeID = node_data.nodeType
     98         node_type_name = get_node_type(nodeTypeID).name
     99         m = [['Copy nodeID: %s' % node_id, lambda *args: self._copy_value(node_id)], ['Copy nodeTypeID: %s (%s)' % (node_type_name, nodeTypeID), lambda *args: self._copy_value(nodeTypeID)]]
    100         if 'atom_id' in node_data.nodeParameters:
    101             atom_id = node_data.nodeParameters['atom_id'].value
    102             m += [['Copy atomID: %s' % atom_id, lambda *args: self._copy_value(atom_id)]]
    103         return m
    104 
    105     def _copy_value(self, copyValue, *args):
    106         return blue.pyos.SetClipboardData(unicode(copyValue))
    107 
    108     def get_node_class_title(self, node_id):
    109         node_data = self.nodes_data[node_id]
    110         node_class = get_node_class(node_data.nodeType)
    111         if node_class:
    112             return node_class.get_title(node_data)
    113         return self._get_atom_or_node_type(node_id).name
    114 
    115     def get_node_class_subtitle(self, node_id):
    116         node_data = self.nodes_data[node_id]
    117         node_class = get_node_class(node_data.nodeType)
    118         if node_class:
    119             return node_class.get_subtitle(node_data)
    120         return ''
    121 
    122     def get_node_description(self, node_id):
    123         return self._get_atom_or_node_type(node_id).description or ''
    124 
    125     def get_node_color(self, node_id):
    126         return get_node_type(self.nodes_data[node_id].nodeType).color[:3]
    127 
    128     def get_node_tags(self, node_id):
    129         node_data = self.nodes_data[node_id]
    130         if 'atom_id' in node_data.nodeParameters:
    131             return get_atom_data(node_data.nodeParameters['atom_id'].value).tags
    132         return []
    133 
    134     def get_node_output_description(self, node_id):
    135         ouput_parameters = self.get_node_output_parameters(node_id)
    136         outputs = []
    137         for parameter in ouput_parameters:
    138             outputs.append('{} ({})'.format(parameter.parameterKey, parameter.parameterType))
    139 
    140         return '\n'.join(outputs)
    141 
    142     def get_node_output_parameters(self, node_id):
    143         return self._get_atom_or_node_type(node_id).parameters.outputs
    144 
    145     def get_node_input_parameters(self, node_id):
    146         input_parameters = []
    147         node_type = get_node_type(self.nodes_data[node_id].nodeType)
    148         input_parameters.extend([ p for p in node_type.parameters.inputs if p.parameterKey != 'atom_id' ])
    149         if 'atom_id' in self.nodes_data[node_id].nodeParameters:
    150             atom_data = get_atom_data(self.nodes_data[node_id].nodeParameters['atom_id'].value)
    151             input_parameters.extend(atom_data.parameters.inputs)
    152         return input_parameters
    153 
    154     def _get_atom_or_node_type(self, node_id):
    155         node_data = self.nodes_data[node_id]
    156         if 'atom_id' in node_data.nodeParameters:
    157             return get_atom_data(node_data.nodeParameters['atom_id'].value)
    158         return get_node_type(node_data.nodeType)
    159 
    160     @property
    161     def can_edit(self):
    162         return False
    163 
    164     @property
    165     def zoom_level(self):
    166         return self._settings.zoom_level
    167 
    168     @zoom_level.setter
    169     def zoom_level(self, value):
    170         self._settings.zoom_level = value
    171 
    172     @property
    173     def pan_left(self):
    174         return self._settings.pan_left
    175 
    176     @pan_left.setter
    177     def pan_left(self, value):
    178         self._settings.pan_left = value
    179 
    180     @property
    181     def pan_top(self):
    182         return self._settings.pan_top
    183 
    184     @pan_top.setter
    185     def pan_top(self, value):
    186         self._settings.pan_top = value
    187 
    188     def open_graph(self, graph_id):
    189         open_node_graph(graph_id)
    190 
    191     def open_fsd_page(self, node_graph_id = None):
    192         import webbrowser
    193         webbrowser.open_new('http://localhost:8000/nodegraphs/{}/'.format(node_graph_id or self.node_graph_id))
    194 
    195     def toggle_selection(self, node_id):
    196         if node_id in self.selected_nodes:
    197             self.selected_nodes.remove(node_id)
    198         else:
    199             self.selected_nodes.add(node_id)
    200         self.on_selected_nodes(self.selected_nodes)
    201 
    202     def add_to_selection(self, node_id):
    203         self.add_selection([node_id])
    204 
    205     def add_selection(self, node_ids):
    206         for node_id in node_ids:
    207             if node_id in self.selected_nodes:
    208                 continue
    209             self.selected_nodes.add(node_id)
    210 
    211         self.on_selected_nodes(self.selected_nodes)
    212 
    213     def select_nodes(self, node_ids):
    214         self.selected_nodes = set(node_ids)
    215         self.on_selected_nodes(self.selected_nodes)
    216 
    217     def select_all_nodes(self):
    218         self.selected_nodes = set(self.nodes_data.keys())
    219         self.on_selected_nodes(self.selected_nodes)
    220 
    221     def clear_selection(self):
    222         self.selected_nodes.clear()
    223         self.on_selected_nodes(self.selected_nodes)
    224 
    225     def highlight_node(self, node_id):
    226         self.on_highlight_node(node_id, True)
    227 
    228     def unhighlight_node(self, node_id):
    229         self.on_highlight_node(node_id, False)
    230 
    231     def focus_node(self, node_id):
    232         self.on_focus_node(node_id)
    233 
    234 
    235 class ActiveNodeGraphController(NodeGraphController):
    236 
    237     def __init__(self, node_graph_id):
    238         node_graph = self.service.get_active_node_graph(node_graph_id)
    239         self.node_graph_id = node_graph.graph_id
    240         super(ActiveNodeGraphController, self).__init__(self.node_graph_id)
    241         self.node_graph = node_graph
    242 
    243     def stop_node_graph(self, *args, **kwargs):
    244         self.service.stop_node_graph(self.node_graph.instance_id)
    245 
    246     def stop_active_nodes(self, *args, **kwargs):
    247         self.node_graph.stop_active_nodes()
    248 
    249     def get_node_context_menu(self, node_id):
    250         m = super(ActiveNodeGraphController, self).get_node_context_menu(node_id)
    251         if not self.node_graph.is_active:
    252             return m
    253         return [('Start node', lambda : self.node_graph.start_node(node_id)), ('Stop node', lambda : self.node_graph.stop_node(node_id)), None] + m
    254 
    255     def go_to_graph(self):
    256         open_node_graph(self.node_graph_id)
    257 
    258 
    259 class LogNodeGraphController(NodeGraphController):
    260 
    261     def __init__(self, node_graph_id):
    262         node_graph = self.service.log_graphs.get(node_graph_id)
    263         self.node_graph_id = node_graph.graph_id
    264         super(LogNodeGraphController, self).__init__(self.node_graph_id)
    265         self.node_graph = node_graph
    266 
    267     def stop_node_graph(self, *args, **kwargs):
    268         self.node_graph.stop_node_graph()
    269 
    270     def stop_active_nodes(self, *args, **kwargs):
    271         self.node_graph.stop_active_nodes()
    272 
    273     def get_node_context_menu(self, node_id):
    274         m = super(LogNodeGraphController, self).get_node_context_menu(node_id)
    275         if not self.node_graph.is_active:
    276             return m
    277         return [('Start node', lambda : self.node_graph.start_node(node_id)), ('Stop node', lambda : self.node_graph.stop_node(node_id)), None] + m
    278 
    279     def go_to_graph(self):
    280         open_node_graph(self.node_graph_id)
    281 
    282 
    283 class AuthoredNodeGraphController(NodeGraphController):
    284 
    285     def __init__(self, node_graph_id):
    286         super(AuthoredNodeGraphController, self).__init__(node_graph_id)
    287         self._load_from_fsd()
    288         self._is_building = False
    289 
    290     def _load_from_fsd(self):
    291         fsd_data = get_node_graph_data(self.node_graph_id)
    292         self.node_graph = KeyVal(name=fsd_data.name, description=fsd_data.description, nodes_data=fsd_data.nodes, tags=fsd_data.tags, parent_graph_id=fsd_data.parentGraph, remote_graph_id=get_remote_graph_id_from_data(fsd_data), scope=get_scope_from_data(fsd_data))
    293 
    294     def start_node_graph(self, *args, **kwargs):
    295         if self.is_server_graph:
    296             self.service.qa_start_node_graph(self.node_graph_id)
    297         else:
    298             self.service.start_node_graph(self.node_graph_id)
    299 
    300     def reload_from_fsd(self, *args, **kwargs):
    301         reload_node_graph_data()
    302         self._load_from_fsd()
    303         self.on_update()
    304 
    305     @property
    306     def is_building(self):
    307         return self._is_building
    308 
    309     @property
    310     def has_custom_graph(self):
    311         custom_node_graph_id = 'custom_{}'.format(self.node_graph_id)
    312         return bool(get_custom_node_graph(custom_node_graph_id))
    313 
    314     def customize_graph(self, *args, **kwargs):
    315         custom_node_graph_id = customize_fsd_graph(self.node_graph_id)
    316         open_node_graph(custom_node_graph_id)
    317 
    318 
    319 class CustomNodeGraphController(NodeGraphController):
    320 
    321     def __init__(self, node_graph_id):
    322         super(CustomNodeGraphController, self).__init__(node_graph_id)
    323         authored_id = get_custom_node_graph_authored_id(node_graph_id)
    324         if authored_id:
    325             fsd_data = get_node_graph_data(authored_id)
    326             name = fsd_data.name
    327             description = fsd_data.description
    328             tags = fsd_data.tags
    329             parent_graph_id = fsd_data.parentGraph
    330             remote_graph_id = get_remote_graph_id_from_data(fsd_data)
    331             scope = get_scope_from_data(fsd_data)
    332         else:
    333             name = node_graph_id
    334             description = ''
    335             tags = []
    336             parent_graph_id = None
    337             remote_graph_id = None
    338             scope = None
    339         self.node_graph = KeyVal(name=name, description=description, nodes_data=get_custom_node_graph(node_graph_id, as_object=True), tags=tags, parent_graph_id=parent_graph_id, remote_graph_id=remote_graph_id, scope=scope)
    340         self.selected_connection_point = None
    341         self.on_selected_connection_point = signals.Signal('on_selected_connection_point')
    342 
    343     def _initialize(self):
    344         from .controllers import get_node_graph_settings
    345         self._settings = get_node_graph_settings(self.get_authored_id() or self.node_graph_id)
    346 
    347     @property
    348     def can_edit(self):
    349         return True
    350 
    351     def open_fsd_page(self, node_graph_id = None):
    352         node_graph_id = self.get_authored_id()
    353         if node_graph_id:
    354             super(CustomNodeGraphController, self).open_fsd_page(node_graph_id)
    355 
    356     def start_node_graph(self, *args, **kwargs):
    357         if self.is_server_graph:
    358             return
    359         node_graph = ClientNodeGraph(graph_id=self.node_graph_id, graph_data=KeyVal(name=self.name, description=self.description, nodes=self.nodes_data, tags=self.tags))
    360         self.service._active_graphs[node_graph.instance_id] = node_graph
    361         open_node_graph(node_graph.instance_id)
    362         node_graph.start_node_graph()
    363 
    364     def get_authored_id(self):
    365         return get_custom_node_graph_authored_id(self.node_graph_id)
    366 
    367     def copy_graph_to_clipboard(self):
    368         copy_graph_to_clipboard(self.node_graph_id)
    369 
    370     def copy_nodes_to_clipboard(self, node_ids):
    371         copy_graph_to_clipboard(self.node_graph_id, node_ids=node_ids)
    372 
    373     def clear_selected_connection_point(self):
    374         if self.selected_connection_point:
    375             self.selected_connection_point = None
    376             self.on_selected_connection_point(self.selected_connection_point)
    377 
    378     def node_connection_clicked(self, connection_point):
    379         self.clear_selection()
    380         if not self.selected_connection_point:
    381             self.selected_connection_point = connection_point
    382             self.on_selected_connection_point(self.selected_connection_point)
    383             return
    384         input_point = None
    385         output_point = None
    386         if 'input' in connection_point.connection_type:
    387             input_point = connection_point
    388         else:
    389             output_point = connection_point
    390         if 'input' in self.selected_connection_point.connection_type:
    391             input_point = self.selected_connection_point
    392         else:
    393             output_point = self.selected_connection_point
    394         if not input_point or not output_point:
    395             print('cannot connect of same group - input->input or output->output')
    396             self.clear_selected_connection_point()
    397             return
    398         if output_point.node_id == input_point.node_id:
    399             print('cannot connect the same node - input->output or output->input')
    400             self.clear_selected_connection_point()
    401             return
    402         if 'variable' in output_point.connection_type and 'variable' in input_point.connection_type:
    403             self._add_variable_connection(output_point.node_id, output_point.connection_id, input_point.node_id, input_point.connection_id)
    404         elif 'variable' not in output_point.connection_type and 'variable' not in input_point.connection_type:
    405             self.add_node_connection(output_point.node_id, input_point.node_id, output_point.connection_id)
    406         else:
    407             print('cannot connect variable to non-variable port')
    408         self.clear_selected_connection_point()
    409 
    410     def get_node_context_menu(self, node_id):
    411         m = super(CustomNodeGraphController, self).get_node_context_menu(node_id)
    412         if self.selected_nodes:
    413             result = [('MULTIPLE NODES SELECTED', None),
    414              ('Copy to clipboard', lambda : self.copy_nodes_to_clipboard(self.selected_nodes)),
    415              (eveformat.color('! Remove nodes', (1, 0, 0)), lambda : self.remove_nodes(self.selected_nodes)),
    416              None] + m
    417         else:
    418             result = [('Duplicate node', lambda : self.duplicate_node(node_id)),
    419              ('Copy to clipboard', lambda : self.copy_nodes_to_clipboard([node_id])),
    420              None,
    421              (eveformat.color('! Remove node', (1, 0, 0)), lambda : self.remove_nodes([node_id])),
    422              None]
    423         return result + m
    424 
    425     def add_node(self, node_type_id, position, atom_type_id):
    426         node_id = self._get_new_node_id()
    427         self.nodes_data[node_id] = get_new_node_data(node_type_id, position, atom_type_id)
    428         if self.selected_connection_point:
    429             if self.selected_connection_point.connection_type == 'output':
    430                 self.add_node_connection(self.selected_connection_point.node_id, node_id, self.selected_connection_point.connection_id)
    431             self.clear_selected_connection_point()
    432         self.save_graph()
    433         return node_id
    434 
    435     def duplicate_node(self, node_id):
    436         node_data = KeyVal(deepcopy(self.nodes_data[node_id].__dict__))
    437         node_data['connections'] = {connection_id:[] for connection_id in node_data['connections']}
    438         for parameter_id, parameter in node_data['nodeParameters'].items():
    439             if parameter.connection:
    440                 del node_data['nodeParameters'][parameter_id]
    441 
    442         for parameter_id, parameter in node_data.get('atomParameters', {}).items():
    443             if parameter.connection:
    444                 del node_data['atomParameters'][parameter_id]
    445 
    446         node_data['position'] = [node_data['position'][0] + 3, node_data['position'][1] + 1]
    447         new_node_id = self._get_new_node_id()
    448         self.nodes_data[new_node_id] = node_data
    449         self.save_graph()
    450 
    451     def remove_nodes(self, node_ids):
    452         for node_id in node_ids:
    453             for from_node_id in self.nodes_data:
    454                 if from_node_id == node_id:
    455                     continue
    456                 connections = self.nodes_data[from_node_id]['connections']
    457                 for connection_id in connections:
    458                     try:
    459                         connections[connection_id].remove(node_id)
    460                     except Exception:
    461                         pass
    462 
    463                 for parameter_id, parameter in self.nodes_data[from_node_id]['nodeParameters'].items():
    464                     if parameter.connection and parameter.connection.node == node_id:
    465                         del self.nodes_data[from_node_id]['nodeParameters'][parameter_id]
    466 
    467                 for parameter_id, parameter in self.nodes_data[from_node_id].get('atomParameters', {}).items():
    468                     if parameter.connection and parameter.connection.node == node_id:
    469                         del self.nodes_data[from_node_id]['atomParameters'][parameter_id]
    470 
    471             self.nodes_data.pop(node_id, None)
    472 
    473         self.save_graph()
    474 
    475     def add_node_connection(self, from_node_id, to_node_id, connection_id):
    476         if 'input' not in get_node_type(self.nodes_data[to_node_id].nodeType).ports.inPorts:
    477             return
    478         if connection_id not in self.nodes_data[from_node_id]['connections']:
    479             self.nodes_data[from_node_id]['connections'][connection_id] = []
    480         connections = self.nodes_data[from_node_id]['connections'][connection_id]
    481         if to_node_id not in connections:
    482             connections.append(to_node_id)
    483             self.save_graph()
    484 
    485     def remove_all_connection_lines(self, connection_lines):
    486         for connection_line in connection_lines:
    487             self.remove_connection_line(connection_line)
    488 
    489     def remove_connection_line(self, connection_line):
    490         if connection_line.point_b.connection_type == 'input_variable':
    491             self.remove_variable_connection(connection_line.point_b.node_id, connection_line.point_b.connection_id)
    492         else:
    493             self.remove_node_connection(connection_line.point_a.node_id, connection_line.point_b.node_id, connection_line.point_a.connection_id)
    494 
    495     def remove_all_node_connections(self, node_id, connection_id):
    496         self.nodes_data[node_id]['connections'][connection_id] = []
    497         self.save_graph()
    498 
    499     def remove_node_connection(self, from_node_id, to_node_id, connection_id):
    500         connections = self.nodes_data[from_node_id]['connections'][connection_id]
    501         if to_node_id in connections:
    502             connections.remove(to_node_id)
    503             self.save_graph()
    504 
    505     def remove_variable_connection(self, node_id, parameter_id):
    506         update_parameter_connection(self.nodes_data[node_id], parameter_id, None, None)
    507         self.save_graph()
    508 
    509     def paste_from_clipboard(self, position, clipboard_data):
    510         import yaml
    511         nodes_data = convert_from_clipboard(yaml.safe_load(clipboard_data or '{}'), position)
    512         if nodes_data:
    513             self.nodes_data.update(nodes_data)
    514             self.save_graph()
    515 
    516     def move_node(self, node_id, position):
    517         self.nodes_data[node_id]['position'] = position
    518         self.save_graph()
    519 
    520     def move_nodes(self, node_ids, main_node_id, new_position):
    521         current_position = self.nodes_data[main_node_id]['position']
    522         pos_diff_x = new_position[0] - current_position[0]
    523         pos_diff_y = new_position[1] - current_position[1]
    524         adjust_x = 0
    525         adjust_y = 0
    526         for node_id in node_ids:
    527             position = self.nodes_data[node_id]['position']
    528             new_pos_x = position[0] + pos_diff_x
    529             new_pos_y = position[1] + pos_diff_y
    530             if new_pos_x < 0:
    531                 adjust_x = min(adjust_x, new_pos_x)
    532             if new_pos_y < 0:
    533                 adjust_y = min(adjust_y, new_pos_y)
    534 
    535         pos_diff_x -= adjust_x
    536         pos_diff_y -= adjust_y
    537         for node_id in node_ids:
    538             position = self.nodes_data[node_id]['position']
    539             new_pos_x = position[0] + pos_diff_x
    540             new_pos_y = position[1] + pos_diff_y
    541             self.nodes_data[node_id]['position'] = (new_pos_x, new_pos_y)
    542 
    543         self.save_graph()
    544 
    545     def edit_nodes_data(self, data):
    546         self.node_graph.nodes_data = data
    547         self.save_graph()
    548 
    549     def update_node_parameter(self, node_id, parameter_id, value):
    550         update_parameter(self.nodes_data[node_id], parameter_id, value)
    551         self.save_graph()
    552 
    553     def update_node_comment(self, node_id, comment):
    554         if not comment:
    555             comment = None
    556         if self.nodes_data[node_id].comment != comment:
    557             self.nodes_data[node_id].comment = comment
    558             self.save_graph()
    559 
    560     @uthread2.debounce(wait=0.1)
    561     def save_graph(self):
    562         self.clear_selection()
    563         custom_node_graphs = get_custom_node_graphs()
    564         data = convert_to_dict_nodes(self.nodes_data)
    565         custom_node_graphs[self.node_graph_id] = data
    566         save_custom_node_graphs(custom_node_graphs)
    567         self.node_graph.nodes_data = get_custom_node_graph(self.node_graph_id, as_object=True)
    568         self.on_update()
    569 
    570     @threadutils.threaded
    571     def save_to_fsd(self):
    572         authored_id = self.get_authored_id()
    573         if not authored_id:
    574             return
    575         open_node_graph(authored_id)
    576         save_fsd_node_graph(authored_id, get_custom_node_graph(self.node_graph_id))
    577 
    578     def delete_graph(self, *args, **kwargs):
    579         authored_id = self.get_authored_id()
    580         delete_custom_node_graph(self.node_graph_id)
    581         open_node_graph(authored_id)
    582 
    583     def go_to_authored_graph(self):
    584         authored_id = self.get_authored_id()
    585         if authored_id:
    586             open_node_graph(authored_id)
    587 
    588     def _get_new_node_id(self):
    589         return str(uuid.uuid4())
    590 
    591     def _add_variable_connection(self, from_node_id, from_key, to_node_id, to_key):
    592         update_parameter_connection(self.nodes_data[to_node_id], to_key, from_node_id, from_key)
    593         self.save_graph()