def test_compound_output_plugs_inform_parent_on_value_set(clear_default_graph): """ +--------------------+ +----------------------+ | Generate | | MyPrint | |--------------------| |----------------------| | out %--------->o value<{"1": 1, "> | | out.0 o +----------------------+ | out.1 o | out.2 o +--------------------+ """ @Node(outputs=["out"]) def Generate(): return {"out.{0}".format(i): i for i in range(3)} @Node(outputs=["out"]) def TestNode(value): return {"out": value} graph = Graph() generate = Generate(graph=graph) test = TestNode(graph=graph) generate.outputs["out"] >> test.inputs["value"] graph.evaluate() assert test.outputs["out"].value == {"0": 0, "1": 1, "2": 2}
def test_evaluation_matrix(): """The nodes as a 2D grid.""" start = NodeForTesting('start') n11 = NodeForTesting('11') n12 = NodeForTesting('12') n21 = NodeForTesting('21') n31 = NodeForTesting('31') n32 = NodeForTesting('32') n33 = NodeForTesting('33') end = NodeForTesting('end') # Connect them start.outputs['out'] >> n11.inputs['in1'] start.outputs['out'] >> n21.inputs['in1'] start.outputs['out'] >> n31.inputs['in1'] n31.outputs['out'] >> n32.inputs['in1'] n32.outputs['out'] >> n33.inputs['in1'] n11.outputs['out'] >> n12.inputs['in1'] n33.outputs['out'] >> n12.inputs['in2'] n12.outputs['out'] >> end.inputs['in1'] n21.outputs['out'] >> end.inputs['in2'] nodes = [start, n11, n12, n21, n31, n32, n33, end] graph = Graph(nodes=nodes) order = [[start], [n11, n21, n31], [n32], [n33], [n12], [end]] for i, row in enumerate(graph.evaluation_matrix): for node in row: assert node in order[i] graph.evaluate()
def test_compound_output_plugs_inform_parent_on_value_set(clear_default_graph): """ +--------------------+ +----------------------+ | Generate | | MyPrint | |--------------------| |----------------------| | out %--------->o value<{"1": 1, "> | | out.0 o +----------------------+ | out.1 o | out.2 o +--------------------+ """ @Node(outputs=['out']) def Generate(): return {'out.{0}'.format(i): i for i in range(3)} @Node(outputs=['out']) def TestNode(value): return {'out': value} graph = Graph() generate = Generate(graph=graph) test = TestNode(graph=graph) generate.outputs['out'] >> test.inputs['value'] graph.evaluate() assert test.outputs['out'].value == {'0': 0, '1': 1, '2': 2}
def test_if_on_node_is_dirty_the_entire_graph_is_dirty(): start = NodeForTesting('start') end = NodeForTesting('end') start.outputs['out'] >> end.inputs['in1'] graph = Graph(nodes=[start, end]) graph.evaluate() assert not graph.is_dirty start.inputs["in1"].is_dirty = True assert graph.is_dirty
def test_exception_event(clear_default_graph): """Test the proper handling of exceptions in nodes.""" g = Graph() def has_been_executed(n): n.event_happened = True @Node() def ErrorNode(): raise Exception en = ErrorNode(graph=g) en.EVENTS["evaluation-exception"].register(has_been_executed) with pytest.raises(Exception): g.evaluate() assert en.event_happened
def test_multiprocessed_evaluation_is_faster(): """Test by having sleeper nodes sleep in parallel and check total grah timing. +---------------+ +---------------+ | Sleeper1 | | Sleeper2 | |---------------| |---------------| o in1<> | +--->o in1<> | | out o-----+ | out o +---------------+ | +---------------+ | +---------------+ | | Sleeper3 | | |---------------| +--->o in1<> | | | out o | +---------------+ | +---------------+ | | Sleeper4 | | |---------------| +--->o in1<> | | out o +---------------+ """ delay = .05 graph = Graph(name='threaded') s1 = Sleeper(name='Sleeper1', graph=graph) s2 = Sleeper(name='Sleeper2', graph=graph) s3 = Sleeper(name='Sleeper3', graph=graph) s4 = Sleeper(name='Sleeper4', graph=graph) s1.outputs['out'] >> s2.inputs['in1'] s1.outputs['out'] >> s3.inputs['in1'] s1.outputs['out'] >> s4.inputs['in1'] start = time.time() graph.evaluate(mode="multiprocessing") end = time.time() runtime = end - start assert runtime < len(graph.nodes) * SLEEP_TIME + len(graph.nodes) * delay
def test_threaded_evluation(): """Test by having sleeper nodes sleep in parallel and check total grah timing. +---------------+ +---------------+ | Sleeper1 | | Sleeper2 | |---------------| |---------------| o in1<> | +--->o in1<> | | out o-----+ | out o +---------------+ | +---------------+ | +---------------+ | | Sleeper3 | | |---------------| +--->o in1<> | | out o +---------------+ """ sleep_time = .3 delay = .05 graph = Graph(name="threaded") @Node(outputs=["out"]) def Sleeper(in1): time.sleep(sleep_time) s1 = Sleeper(name="Sleeper1", graph=graph) s2 = Sleeper(name="Sleeper2", graph=graph) s3 = Sleeper(name="Sleeper3", graph=graph) s1.outputs["out"] >> s2.inputs["in1"] s1.outputs["out"] >> s3.inputs["in1"] start = time.time() graph.evaluate(threaded=True, submission_delay=delay) end = time.time() runtime = end - start assert 2 * sleep_time <= runtime assert runtime <= 2 * sleep_time + 2 * delay
def test_evaluation_matrix(): """The nodes as a 2D grid.""" @Node(outputs=["out", "out2"]) def DummyNode(in1, in2): pass start = DummyNode(name='start') n11 = DummyNode(name='11') n12 = DummyNode(name='12') n21 = DummyNode(name='21') n31 = DummyNode(name='31') n32 = DummyNode(name='32') n33 = DummyNode(name='33') end = DummyNode(name='end') # Connect them start.outputs['out'] >> n11.inputs['in1'] start.outputs['out'] >> n21.inputs['in1'] start.outputs['out'] >> n31.inputs['in1']['0'] n31.outputs['out'] >> n32.inputs['in1'] n32.outputs['out'] >> n33.inputs['in1'] n11.outputs['out'] >> n12.inputs['in1'] n33.outputs['out'] >> n12.inputs['in2'] n12.outputs['out'] >> end.inputs['in1'] n21.outputs['out2']['0'] >> end.inputs['in2'] nodes = [start, n11, n12, n21, n31, n32, n33, end] graph = Graph(nodes=nodes) order = [[start], [n11, n21, n31], [n32], [n33], [n12], [end]] for i, row in enumerate(graph.evaluation_matrix): for node in row: assert node in order[i] graph.evaluate()
"""Nodes do not necessarily have to define output and input plugs.""" print('-- World Clock -------------------') for location, t in times.items(): print('It is now: {time:%H:%M} in {location}'.format( time=datetime.fromtimestamp(t), location=location)) print('----------------------------------') # The Graph holds the nodes graph = Graph(name='World Clock') current_time = CurrentTime(graph=graph) van = ConvertTime(name='Vancouver', timezone=-8, graph=graph) ldn = ConvertTime(name='London', timezone=0, graph=graph) muc = ConvertTime(name='Munich', timezone=1, graph=graph) world_clock = ShowTimes(graph=graph) # Connecting nodes can be done via the bit shift operator as well current_time.outputs['time'].connect(van.inputs['time']) current_time.outputs['time'].connect(ldn.inputs['time']) current_time.outputs['time'].connect(muc.inputs['time']) van.outputs['converted_time'] >> world_clock.inputs['times']['Vancouver'] ldn.outputs['converted_time'] >> world_clock.inputs['times']['London'] muc.outputs['converted_time'] >> world_clock.inputs['times']['Munich'] # Display the graph print(graph) # Evaluate with debug logs logger.setLevel(logging.DEBUG) graph.evaluate()
def test_multiprocessing_evaluation_updates_the_original_graph(): """Multi processing updates the original graph object. +---------------+ +---------------+ +------------------------+ | AddNode1 | | AddNode2 | | AddNode5 | |---------------| |---------------| |------------------------| o number1<1> | +--->o number1<2> | o number1<1> | o number2<1> | | o number2<1> | o number2<1> | o numbers<> | | o numbers<> | +--->% numbers | | result o-----+ | result o |--->o numbers.0<> | | results o | | results o |--->o numbers.1<> | +---------------+ | +---------------+ | | result o | +---------------+ | | results o | | AddNode3 | | +------------------------+ | |---------------| | +--->o number1<2> | | | o number2<1> | | | o numbers<> | | | | result o | | | results o | | +---------------+ | | +------------------------+ | | | AddNode4 | | | |------------------------| | +--->o number1<2> | | | o number2<1> | | | % numbers | | +--->o numbers.0<2> | | +--->o numbers.1<2> | | | result o | | results %-----+ | results.0 o-----+ | results.1 o-----+ +------------------------+ """ graph = Graph(name='multiprocessing') n1 = AddNode(name='AddNode1', graph=graph, number1=1, number2=1) n2 = AddNode(name='AddNode2', graph=graph, number2=1) n3 = AddNode(name='AddNode3', graph=graph, number2=1) n4 = AddNode(name='AddNode4', graph=graph, number2=1) n5 = AddNode(name='AddNode5', graph=graph, number1=1, number2=1) n1.outputs['result'] >> n2.inputs['number1'] n1.outputs['result'] >> n3.inputs['number1'] n1.outputs['result'] >> n4.inputs['number1'] n1.outputs['result'] >> n4.inputs['numbers']['0'] n1.outputs['result'] >> n4.inputs['numbers']['1'] n4.outputs['results']['0'] >> n5.inputs['numbers']['0'] n4.outputs['results']['1'] >> n5.inputs['numbers']['1'] n4.outputs['results'] >> n5.inputs['numbers'] graph.evaluate(mode="multiprocessing", submission_delay=0.05) assert n2.outputs['result'].value == 3 assert n3.outputs['result'].value == 3 assert n4.outputs['results'].value == {'0': 0, '1': 1} assert n5.outputs['results'].value == {'0': 0, '1': 1} assert not n1.is_dirty assert not n2.is_dirty assert not n3.is_dirty assert not n4.is_dirty assert not n5.is_dirty
class FlowEditor(QtWidgets.QMainWindow, floweditor_ui.Ui_FlowEditorWindow): def __init__(self, parent=None): super(FlowEditor, self).__init__(parent=parent) self.setupUi(self) self.statusBar().addPermanentWidget(self.bottom_widget, 1) style = QtWidgets.QApplication.instance().style() qstyle = QtWidgets.QStyle toolbar = QtWidgets.QToolBar("Evaluation", self) self.addToolBar(QtCore.Qt.TopToolBarArea, toolbar) self.addToolBar(QtCore.Qt.TopToolBarArea, toolbar) self.evaluate_locally_action.setIcon( style.standardIcon(qstyle.SP_ComputerIcon)) self.evaluate_locally_action.triggered.connect(self.evaluate_locally) toolbar.addAction(self.evaluate_locally_action) self.fp_nodes_map = {} self.qt_nodes_map = {} self.logs = {} self.graph = None self.selected_fp_node = None # Graph View # self.graph_viewer = QTGRAPH.viewer() self.graph_widget.layout().addWidget(self.graph_viewer) QTGRAPH.nodes_deleted.connect(self.nodes_deleted) self.graph_viewer.data_dropped.connect(self.node_dropped) self.graph_viewer.connection_changed.connect(self.connection_changed) # graph_menu = QTGRAPH._viewer.context_menu() # graph_menu.clear() # self.delete_action = graph_menu.addAction( # 'Delete', self.delete_nodes, QtGui.QKeySequence.Delete) nodes = utils.discover_nodes() # --------------------------------------------------------------------- # Available Nodes - View # self.node_model = AvailableNodesModel() for node in nodes: item = QtGui.QStandardItem() item.setData(node["name"], QtCore.Qt.DisplayRole) item.setData("{0}.{1}".format(node["file"], node["name"]), QtCore.Qt.ToolTipRole) item.setData(node["node"], QtCore.Qt.UserRole) self.node_model.appendRow([item]) self.available_nodes_view.setModel(self.node_model) # Available Nodes - Actions # self.create_action = QtWidgets.QAction("Create", self.available_nodes_view) self.create_action.triggered.connect(self.create_node) self.available_nodes_view.addAction(self.create_action) self.available_nodes_view.doubleClicked.connect(self.create_node) # Available Nodes - Details # self.available_nodes_view.selection_changed.connect( self.show_node_details) # --------------------------------------------------------------------- # Selected Node - Attributes # self.name_lineedit.textChanged.connect(self.edit_node_name) self.open_code_button.clicked.connect(self.open_code) self.graph_viewer.node_selection_changed.connect( self.node_selection_changed) # self.graph_viewer.node_selected.connect(self.node_selection_changed) # Logs # self.clear_log_button.clicked.connect(self.log_textedit.clear) # Main Menu # self.actionNew.triggered.connect(self.new) self.actionOpen.triggered.connect(self.open) self.actionSave_As.triggered.connect(self.save_as) self.actionQuit.triggered.connect(self.close) # Prototyping options # self.node_inputs_widget.right_clicked.connect( self.inputs_right_clicked) self.node_outputs_widget.right_clicked.connect( self.outputs_right_clicked) self.new() # ------------------------------------------------------------------------- # Events # ------------------------------------------------------------------------- def closeEvent(self, event): self.graph_viewer.data_dropped.disconnect(self.node_dropped) self.graph_viewer.node_selection_changed.disconnect( self.node_selection_changed) QTGRAPH.nodes_deleted.disconnect(self.nodes_deleted) return event.accept() # ------------------------------------------------------------------------- # Actions # ------------------------------------------------------------------------- def new(self): self.fp_nodes_map = {} self.qt_nodes_map = {} self.graph = Graph() QTGRAPH.clear_session() self.node_deselected() def open(self): json_file = QtWidgets.QFileDialog.getOpenFileName( self, "Open JSON graph file", "", "JSON Files (*.json)")[0] if not json_file: return json_data = None with open(json_file, "r") as f: json_data = json.load(f) graph = Graph.deserialize(json_data) w.load_graph(graph) def save_as(self): save_file, file_type = QtWidgets.QFileDialog.getSaveFileName( self, "Save graph to JSON file", os.path.expanduser("~"), "JSON Files (*.json)") if not save_file: return if file_type == "JSON Files (*.json)": if not save_file.lower().endswith(".json"): save_file = "{0}.json".format(save_file) with open(save_file, "w") as f: json.dump(self.graph.serialize(), f, indent=2) print("Saved to", save_file) def open_code(self): if self.selected_fp_node is not None: webbrowser.open(self.selected_fp_node.file_location) # ------------------------------------------------------------------------- # Internals # ------------------------------------------------------------------------- def create_node(self): for index in self.available_nodes_view.selectedIndexes(): self.add_node(index.data(QtCore.Qt.UserRole), QtCore.QPoint()) def node_dropped(self, data, point): for row in range(self.node_model.rowCount()): item = self.node_model.item(row) if item.data(QtCore.Qt.ToolTipRole) == data.text(): self.add_node(item.data(QtCore.Qt.UserRole), point) return def nodes_deleted(self, nodes): for node_id in nodes: node = self.fp_nodes_map.get(node_id) self.graph.delete_node(node) del self.qt_nodes_map[node_id] del self.fp_nodes_map[node_id] def add_node(self, node_cls, point): # Check if the name is already taken name = getattr(node_cls, "__name__", None) or node_cls.name index = -1 for node in self.graph.nodes: if node.name.startswith(name): numbers = re.findall(r"(\d+$)", node.name) if numbers: if int(numbers[0]) + 1 > index: index = int(numbers[0]) + 1 else: index = 0 if index > -1: name = "{name}{index}".format(name=name, index=index) fp_node = node_cls(graph=self.graph, name=name) return self._add_node(fp_node, point) def _add_node(self, fp_node, point): qt_node = QTGRAPH.create_node('flowpipe.FlowpipeNode', name=fp_node.name, pos=[point.x(), point.y()]) for input_ in fp_node.all_inputs().values(): qt_node.add_input(input_.name) for output in fp_node.all_outputs().values(): qt_node.add_output(output.name) self.fp_nodes_map[qt_node.id] = fp_node self.qt_nodes_map[qt_node.id] = qt_node QTGRAPH.clear_selection() return qt_node def show_node_details(self, index): if index is None: self.nodes_details_text.setPlainText("") else: text = NODE_DETAILS.format( name=index.data(QtCore.Qt.DisplayRole), doc=utils.dedent_doc(index.data(QtCore.Qt.UserRole).__doc__), file=index.data(QtCore.Qt.ToolTipRole).split(".")[0], module=index.data(QtCore.Qt.ToolTipRole).split(".")[0], cls=index.data(QtCore.Qt.DisplayRole)) self.nodes_details_text.setText(text) def node_selection_changed(self, selected, deselected): selection = self.graph_viewer.selected_nodes() if len(selection) == 1: self.node_selected(selection[0].id) else: self.node_deselected() def node_selected(self, qt_node_id): fp_node = self.fp_nodes_map[qt_node_id] self.selected_fp_node = fp_node self.refresh_node_attributes() def refresh_node_attributes(self): if self.selected_fp_node is None: self.node_deselected() return self.node_name_widget.setEnabled(True) self.node_inputs_widget.setEnabled(True) self.node_outputs_widget.setEnabled(True) self.code_widget.setEnabled(True) self.errors_widget.setEnabled(True) self.node_state_label.setText( '<span style="color: #ff9999">Dirty</span>' if self. selected_fp_node. is_dirty else '<span style="color: #99ff99">Clean</span>') self.name_lineedit.setText(self.selected_fp_node.name) self.node_type_label.setText(self.selected_fp_node.__class__.__name__) self.description_textedit.setPlainText( utils.dedent_doc(self.selected_fp_node.__doc__)) inputs = {} for name, in_ in self.selected_fp_node.inputs.items(): if in_._sub_plugs: inputs[name] = {} for sub_name, sub_plug in in_._sub_plugs.items(): inputs[name][sub_name] = sub_plug else: inputs[name] = in_ outputs = {} for name, out in self.selected_fp_node.outputs.items(): if out._sub_plugs: outputs[name] = {} for sub_name, sub_plug in out._sub_plugs.items(): outputs[name][sub_name] = sub_plug else: outputs[name] = out # Code # if isinstance(self.selected_fp_node, FunctionNode): self.code_view.setPlainText( inspect.getsource(self.selected_fp_node.func)) elif self.selected_fp_node.__class__.__name__ == "PrototypeNode": self.code_view.setPlainText("") else: self.code_view.setPlainText( inspect.getsource(self.selected_fp_node.compute)) # Errors # self.errors_textedit.setHtml(self.logs.get(self.selected_fp_node, "")) # Disable/Enable certain fields # if isinstance(self.selected_fp_node, floweditor.nodes.prototype_node.PrototypeNode): self.description_textedit.setStyleSheet("") self.description_textedit.setTextInteractionFlags( QtCore.Qt.TextEditable | QtCore.Qt.TextSelectableByMouse | QtCore.Qt.TextSelectableByKeyboard) else: self.description_textedit.setStyleSheet( "background-color: palette(window)") self.description_textedit.setTextInteractionFlags( QtCore.Qt.NoTextInteraction) # Plugs # schema = self.selected_fp_node.metadata.get("datatypes", {}).get("inputs", {}) display_schema = {} for plug in self.selected_fp_node.inputs.values(): s = schema.get(plug.name, {}) display_schema[plug.name] = { "type": s.get("type", "string"), "tooltip": s.get("tooltip"), "editable": s.get("editable") } plugs = {p.name: p for p in self.selected_fp_node.inputs.values()} values = {p.name: p.value for p in plugs.values()} self.node_inputs_widget.initialize(display_schema, values=values, plugs=plugs) schema = self.selected_fp_node.metadata.get("datatypes", {}).get("outputs", {}) display_schema = {} for plug in self.selected_fp_node.outputs.values(): s = schema.get(plug.name, {}) display_schema[plug.name] = { "type": s.get("type", "string"), "tooltip": s.get("tooltip"), "editable": False } plugs = {p.name: p for p in self.selected_fp_node.outputs.values()} values = {p.name: p.value for p in plugs.values()} self.node_outputs_widget.initialize(display_schema, values=values, plugs=plugs) def node_deselected(self): self.selected_fp_node = None self.node_state_label.clear() self.name_lineedit.clear() self.node_type_label.clear() self.description_textedit.clear() self.code_view.clear() self.errors_textedit.clear() self.node_inputs_widget.initialize({}, values={}) self.node_outputs_widget.initialize({}, values={}) self.node_name_widget.setEnabled(False) self.node_inputs_widget.setEnabled(False) self.node_outputs_widget.setEnabled(False) self.code_widget.setEnabled(False) self.errors_widget.setEnabled(False) def edit_node_name(self): if len(self.graph_viewer.selected_nodes()) == 1: if self.name_lineedit.text() not in [ n.name for n in self.graph.nodes ]: self.graph_viewer.selected_nodes( )[0].name = self.name_lineedit.text() fp_node = self.fp_nodes_map[self.graph_viewer.selected_nodes() [0].id] fp_node.name = self.name_lineedit.text() def connection_changed(self, disconnected, connected): for connection in connected: start_plug = connection[0] end_plug = connection[1] start_fp_node = self.fp_nodes_map[start_plug.node.id] end_fp_node = self.fp_nodes_map[end_plug.node.id] start_fp_node.all_outputs()[start_plug.name].connect( end_fp_node.all_inputs()[end_plug.name]) for connection in disconnected: start_plug = connection[0] end_plug = connection[1] start_fp_node = self.fp_nodes_map[start_plug.node.id] end_fp_node = self.fp_nodes_map[end_plug.node.id] start_fp_node.all_outputs()[start_plug.name].disconnect( end_fp_node.all_inputs()[end_plug.name]) def evaluate_locally(self): print self.graph self.index = 0.0 self.progressbar.setValue(0) self.current_node = None for qt_node in self.qt_nodes_map.values(): qt_node.set_color(*COLORS["scheduled"]) flowpipe.node.INode.EVENTS["evaluation-started"].register( self.node_evaluation_started) flowpipe.node.INode.EVENTS["evaluation-finished"].register( self.node_evaluation_finished) try: self.graph.evaluate() self.progress_label.setText("Evaluation successful") except Exception as error: qt_node = QTGRAPH.get_node_by_name(self.current_node.name) qt_node.set_color(*COLORS["error"]) self.progress_label.setText("Evaluation failed!") tb = ''.join(traceback.format_exception(*sys.exc_info())) self.logs[self.current_node] = ( '<span style="white-space: pre-wrap; color: #ff9999;">{0}' '</span>'.format(tb)) log.exception(error) self.update_logs(tb) flowpipe.node.INode.EVENTS["evaluation-started"].deregister( self.node_evaluation_started) flowpipe.node.INode.EVENTS["evaluation-finished"].deregister( self.node_evaluation_finished) self.refresh_node_attributes() def node_evaluation_started(self, node): qt_node = QTGRAPH.get_node_by_name(node.name) self.current_node = node qt_node.set_color(*COLORS["evaluating"]) self.progress_label.setText(node.name) QtWidgets.QApplication.instance().processEvents() def node_evaluation_finished(self, node, error=False): qt_node = QTGRAPH.get_node_by_name(node.name) self.index += 1.0 self.progressbar.setValue((self.index / len(self.graph.nodes)) * 100) qt_node.set_color(*COLORS["success"]) QtWidgets.QApplication.instance().processEvents() self.update_logs("Evaluated: {0}".format(node.name)) def update_logs(self, message): self.log_textedit.append(message) def load_graph(self, graph): self.new() self.graph = graph self.graph_name_lineedit.setText(graph.name) x = 0 for row in graph.evaluation_matrix: y = 0 x_diff = 250 for fp_node in row: self._add_node(fp_node, QtCore.QPoint(x, y)) y += 150 x += x_diff for fp_node in graph.nodes: for i, output in enumerate(fp_node.all_outputs().values()): for c in output.connections: in_index = c.node.all_inputs().values().index(c) QTGRAPH.get_node_by_name(fp_node.name).set_output( i, QTGRAPH.get_node_by_name(c.node.name).input(in_index)) # ------------------------------------------------------------------------- # Actions # ------------------------------------------------------------------------- def add_input_plug(self): selection = self.graph_viewer.selected_nodes() if len(selection) != 1: return fp_node = self.fp_nodes_map[selection[0].id] qt_node = self.qt_nodes_map[selection[0].id] editor = widgets.PlugEditor(self, fp_node=fp_node, qt_node=qt_node, plug_type=flowpipe.plug.InputPlug, plug=None) if editor.exec_(): self.node_selected(selection[0].id) def add_output_plug(self): selection = self.graph_viewer.selected_nodes() if len(selection) != 1: return fp_node = self.fp_nodes_map[selection[0].id] qt_node = self.qt_nodes_map[selection[0].id] editor = widgets.PlugEditor(self, fp_node=fp_node, qt_node=qt_node, plug_type=flowpipe.plug.OutputPlug, plug=None) if editor.exec_(): self.node_selected(selection[0].id) def delete_nodes(self): for qt_node in QTGRAPH.selected_nodes(): self.graph.delete_node(self.fp_nodes_map[qt_node.id]) QTGRAPH.delete_nodes(QTGRAPH.selected_nodes()) self.node_deselected() def inputs_right_clicked(self, attribute_widget): selection = self.graph_viewer.selected_nodes() if len(selection) != 1: return fp_node = self.fp_nodes_map[selection[0].id] menu = QtWidgets.QMenu(self.node_inputs_widget) if fp_node.__class__.__name__ == "PrototypeNode": add_action = QtWidgets.QAction(menu) add_action.setText("Add Input Plug") add_action.triggered.connect(self.add_input_plug) menu.addAction(add_action) menu.exec_(QtGui.QCursor.pos()) def outputs_right_clicked(self, attribute_widget): selection = self.graph_viewer.selected_nodes() if len(selection) != 1: return fp_node = self.fp_nodes_map[selection[0].id] menu = QtWidgets.QMenu(self.node_inputs_widget) if fp_node.__class__.__name__ == "PrototypeNode": add_action = QtWidgets.QAction(menu) add_action.setText("Add Output Plug") add_action.triggered.connect(self.add_output_plug) menu.addAction(add_action) menu.exec_(QtGui.QCursor.pos())
def test_multiprocessing_evaluation_updates_the_original_graph(): """Multi processing updates the original graph object. +---------------+ +---------------+ +------------------------+ | AddNode1 | | AddNode2 | | AddNode5 | |---------------| |---------------| |------------------------| o number1<1> | +--->o number1<2> | o number1<1> | o number2<1> | | o number2<1> | o number2<1> | o numbers<> | | o numbers<> | +--->% numbers | | result o-----+ | result o |--->o numbers.0<> | | results o | | results o |--->o numbers.1<> | +---------------+ | +---------------+ | | result o | +---------------+ | | results o | | AddNode3 | | +------------------------+ | |---------------| | +--->o number1<2> | | | o number2<1> | | | o numbers<> | | | | result o | | | results o | | +---------------+ | | +------------------------+ | | | AddNode4 | | | |------------------------| | +--->o number1<2> | | | o number2<1> | | | % numbers | | +--->o numbers.0<2> | | +--->o numbers.1<2> | | | result o | | results %-----+ | results.0 o-----+ | results.1 o-----+ +------------------------+ """ graph = Graph(name="multiprocessing") n1 = AddNode(name="AddNode1", graph=graph, number1=1, number2=1) n2 = AddNode(name="AddNode2", graph=graph, number2=1) n3 = AddNode(name="AddNode3", graph=graph, number2=1) n4 = AddNode(name="AddNode4", graph=graph, number2=1) n5 = AddNode(name="AddNode5", graph=graph, number1=1, number2=1) n1.outputs["result"] >> n2.inputs["number1"] n1.outputs["result"] >> n3.inputs["number1"] n1.outputs["result"] >> n4.inputs["number1"] n1.outputs["result"] >> n4.inputs["numbers"]["0"] n1.outputs["result"] >> n4.inputs["numbers"]["1"] n4.outputs["results"]["0"] >> n5.inputs["numbers"]["0"] n4.outputs["results"]["1"] >> n5.inputs["numbers"]["1"] n4.outputs["results"] >> n5.inputs["numbers"] graph.evaluate(mode="multiprocessing", submission_delay=0.05) assert n2.outputs["result"].value == 3 assert n3.outputs["result"].value == 3 assert n4.outputs["results"].value == {"0": 0, "1": 1} assert n5.outputs["results"].value == {"0": 0, "1": 1} assert not n1.is_dirty assert not n2.is_dirty assert not n3.is_dirty assert not n4.is_dirty assert not n5.is_dirty