def load_project_button_clicked(self): self.editor_startup_configuration['config'] = 'open project' import json file_name = QFileDialog.getOpenFileName(self, 'select project file', '../saves', 'Ryven Project(*.rpo *.rypo)')[0] j_str = '' try: f = open(file_name) j_str = f.read() f.close() except FileNotFoundError: Debugger.debug('couldn\'t open file') return # strict=False has to be to allow 'control characters' like '\n' for newline when loading the json j_obj = json.loads(j_str, strict=False) if j_obj['general info']['type'] != 'Ryven project file': return # scan for all required packages packages = [] package_file_paths = [] scripts = j_obj['scripts'] for script in scripts: flow = script['flow'] for n in flow['nodes']: package = n['parent node package'] if package != 'built in' and not packages.__contains__(package): packages.append(package) if len(packages) > 0: select_packages_dialog = SelectPackages_Dialog(self, packages) select_packages_dialog.exec_() package_file_paths = select_packages_dialog.file_paths self.editor_startup_configuration['required packages'] = package_file_paths self.editor_startup_configuration['content'] = j_obj self.accept()
def create_new_input(self, type_, label, widget_name=None, widget_pos='under', pos=-1, config=None): """Creates and adds a new input. Handy for subclasses.""" Debugger.debug('create_new_input called') pi = InputPortInstance(self, type_, label, config_data=config, widget_name=widget_name, widget_pos=widget_pos) if pos < -1: pos += len(self.inputs) if pos == -1: self.inputs.append(pi) self.add_input_to_layout(pi) else: self.inputs.insert(pos, pi) self.insert_input_into_layout(pos, pi) if not self.initializing: self.update_shape() self.update()
def import_nodes_package_from_file(self, file_path): j_str = '' try: f = open(file_path) j_str = f.read() f.close() except FileExistsError or FileNotFoundError: Debugger.debug('couldn\'t open file') return # Important: translate the package first (metacore files -> src code files) PackageTranslator = self.get_class_from_file(file_path='../pyScript_PackageTranslator', file_name='pyScript_PackageTranslator', class_name='PackageTranslator') package_translator = PackageTranslator(os.path.dirname(os.path.abspath(file_path))) self.parse_nodes(j_str, os.path.dirname(file_path), os.path.splitext(os.path.basename(file_path))[0])
def dropEvent(self, event): text = event.mimeData().text() item: QListWidgetItem = event.mimeData() Debugger.debug('drop received in Flow:', text) j_obj = None type = '' try: j_obj = json.loads(text) type = j_obj['type'] except Exception: return if type == 'variable': self.show_node_choice_widget( event.pos(), # only show get_var and set_var nodes [ n for n in self.all_nodes if find_type_in_object(n, GetVariable_Node) or find_type_in_object(n, SetVariable_Node) ])
def mousePressEvent(self, event): Debugger.debug('mouse press event received, point:', event.pos()) # to catch tablet events (for some reason, it results in a mousePrEv too) if self.ignore_mouse_event: self.ignore_mouse_event = False return # there might be a proxy widget meant to receive the event instead of the flow QGraphicsView.mousePressEvent(self, event) # to catch any Proxy that received the event. Checking for event.isAccepted() or what is returned by # QGraphicsView.mousePressEvent(...) both didn't work so far, so I do it manually if self.ignore_mouse_event: self.ignore_mouse_event = False return if event.button() == Qt.LeftButton: if self.node_choice_proxy.isVisible(): self.hide_node_choice_widget() else: if find_type_in_object(self.itemAt(event.pos()), PortInstanceGate): self.gate_selected = self.itemAt(event.pos()) self.dragging_connection = True self.left_mouse_pressed_in_flow = True elif event.button() == Qt.RightButton: if len(self.items(event.pos())) == 0: self.node_choice_widget.reset_list() self.show_node_choice_widget(event.pos()) elif event.button() == Qt.MidButton: self.panning = True self.pan_last_x = event.x() self.pan_last_y = event.y() event.accept() self.mouse_press_pos = self.mapToScene(event.pos())
def parse_nodes(self, j_str, package_path, package_name) -> bool: """Parses the nodes from a node package in JSON format. Here, all the classes get imported and for every node a Node object with specific attribute values gets created.""" import json # strict=False is necessary to allow control characters like '\n' for newline when loading the json j_obj = json.loads(j_str, strict=False) Debugger.debug(j_obj['type']) if j_obj['type'] != 'Ryven nodes package' and j_obj[ 'type'] != 'vyScriptFP nodes package': # old syntax return False j_nodes_list = j_obj['nodes'] num_nodes = len(j_nodes_list) for ni in range(num_nodes): j_node = j_nodes_list[ni] suc = self.parse_node(j_node, package_name, package_path) if not suc: Debugger.debug('error while importing nodes') return False Debugger.debug(len(self.custom_nodes), 'nodes imported') return True
def get_class_from_file(self, file_path, file_name, class_name): Debugger.debug(file_path) Debugger.debug(file_name) Debugger.debug(class_name) sys.path.append(file_path) new_module = __import__(file_name, fromlist=[class_name]) new_class = getattr(new_module, class_name) return new_class
def parse_nodes(self, j_str, package_path, package_name): """Parses the nodes from a node package in JSON format. Here, all the classes get imported and for every node a Node object with specific attribute values gets created.""" import json # strict=False is necessary to allow 'control characters' like '\n' for newline when loading the json j_obj = json.loads(j_str, strict=False) Debugger.debug(j_obj['type']) if j_obj['type'] != 'vyScriptFP nodes package': return # package_title = j_obj['title'] # package_description = j_obj['description'] j_nodes_list = j_obj['nodes'] num_nodes = len(j_nodes_list) for ni in range(num_nodes): j_node = j_nodes_list[ni] self.parse_node(j_node, package_name, package_path) Debugger.debug(len(self.custom_nodes), 'nodes imported')
def get_val(self): """applies on DATA; called NI internally AND externally""" Debugger.debug( 'get value in', self.direction, 'port instance', self.parent_node_instance.inputs.index(self) if self.direction == 'input' else self.parent_node_instance.outputs.index(self), 'of', self.parent_node_instance.parent_node.title) Debugger.debug('my value is', self.val) if self.direction == 'input': if len(self.connected_port_instances) == 0: if self.widget: return self.widget.get_val() else: return None else: Debugger.debug('calling connected port for val') return self.connected_port_instances[0].get_val() elif self.direction == 'output': Debugger.debug('returning val directly') if self.parent_node_instance.gen_data_on_request: self.parent_node_instance.update() return self.val
def on_disable_debugging_triggered(self): Debugger.disable()
def on_enable_debugging_triggered(self): Debugger.enable()
def tabletEvent(self, event): """tabletEvent gets called by stylus operations. LeftButton: std, no button pressed RightButton: upper button pressed""" # if in edit mode and not panning or starting a pan, pass on to std mouseEvent handlers above if self.stylus_mode == 'edit' and not self.panning and not \ (event.type() == QTabletEvent.TabletPress and event.button() == Qt.RightButton): return # let the mousePress/Move/Release-Events handle it if event.type() == QTabletEvent.TabletPress: self.tablet_press_pos = event.pos() self.ignore_mouse_event = True if event.button() == Qt.LeftButton: if self.stylus_mode == 'comment': new_drawing = self.create_and_place_drawing__cmd( self.mapToScene(self.tablet_press_pos), config=self.stylus_modes_widget.get_pen_settings()) self.current_drawing = new_drawing self.drawing = True elif event.button() == Qt.RightButton: self.panning = True self.pan_last_x = event.x() self.pan_last_y = event.y() elif event.type() == QTabletEvent.TabletMove: self.ignore_mouse_event = True if self.panning: self.pan(event.pos()) elif event.pointerType() == QTabletEvent.Eraser: if self.stylus_mode == 'comment': for i in self.items(event.pos()): if find_type_in_object(i, DrawingObject): self.remove_drawing(i) break elif self.stylus_mode == 'comment' and self.drawing: mapped = self.mapToScene( QPoint(event.posF().x(), event.posF().y())) # rest = QPointF(event.posF().x()%1, event.posF().y()%1) # exact = QPointF(mapped.x()+rest.x()%1, mapped.y()+rest.y()%1) # TODO: use exact position (event.posF() ). Problem: mapToScene() only uses QPoint, not QPointF. The # calculation above didn't work if self.current_drawing.try_to_append_point(mapped): self.current_drawing.stroke_weights.append( event.pressure()) self.current_drawing.update() self.viewport().update() elif event.type() == QTabletEvent.TabletRelease: if self.panning: self.panning = False if self.stylus_mode == 'comment' and self.drawing: Debugger.debug('drawing obj finished') self.current_drawing.finished() self.current_drawing = None self.drawing = False
def update(self, input_called=-1, output_called=-1): Debugger.debug('update in', self.parent_node.title, 'on input', input_called) try: self.update_event(input_called) except Exception as e: Debugger.debug('EXCEPTION IN', self.parent_node.title, 'NI:', e)
def dropEvent(self, event): text = event.mimeData().text() item: QListWidgetItem = event.mimeData() Debugger.debug('drop received in Flow:', text)
def parse_nodes(self, j_str, package_path, package_name): import json # strict=False is necessary to allow 'control characters' like '\n' for newline when loading the json j_obj = json.loads(j_str, strict=False) Debugger.debug(j_obj['type']) if j_obj['type'] != 'vyScriptFP nodes package': return # package_title = j_obj['title'] # package_description = j_obj['description'] j_nodes_list = j_obj['nodes'] num_nodes = len(j_nodes_list) for ni in range(num_nodes): # new node j_node = j_nodes_list[ni] new_node = Node() node_title = j_node['title'] node_class_name = j_node['class name'] node_description = j_node['description'] node_type = j_node['type'] node_has_main_widget = j_node['has main widget'] node_main_widget_pos = j_node['widget position'] if node_has_main_widget else None node_design_style = j_node['design style'] node_color = j_node['color'] # every node has a custom module name which differs from it's name to prevent import issues when using # multiple (different) Nodes with same titles # FOR FURTHER EXPLANATION: see node manager node_module_name = j_node['module name'] module_name_separator = '___' # CUSTOM CLASS IMPORTS ---------------------------------------------------------------------------- # creating all the necessary path variables here for all potentially imported classes # IMPORT NODE INSTANCE SUBCLASS node_instance_class_file_path = package_path+'/nodes/'+node_module_name+'/' node_instance_widgets_file_path = node_instance_class_file_path+'/widgets' node_instance_filename = node_module_name # the NI file's name is just the 'module name' new_node_instance_class = self.get_class_from_file(file_path=node_instance_class_file_path, file_name=node_instance_filename, class_name=node_class_name+'_NodeInstance') self.all_node_instance_classes[new_node] = new_node_instance_class # IMPORT MAIN WIDGET if node_has_main_widget: main_widget_filename = node_module_name+module_name_separator+'main_widget' new_node.main_widget_class = self.get_class_from_file(file_path=node_instance_widgets_file_path, file_name=main_widget_filename, class_name=node_class_name+'_NodeInstance_MainWidget') # I need to create the dict for the node's potential custom input widgets already here self.custom_node_input_widget_classes[new_node] = {} for w_name in j_node['custom input widgets']: input_widget_filename = node_module_name+module_name_separator+w_name custom_widget_class = self.get_class_from_file(file_path=node_instance_widgets_file_path, file_name=input_widget_filename, class_name=w_name+'_PortInstanceWidget') self.custom_node_input_widget_classes[new_node][w_name] = custom_widget_class # note: the input widget classes get imported below in the loop # --------------------------------------------------------------------------------------------------- j_n_inputs = j_node['inputs'] inputs = [] num_inputs = len(j_n_inputs) for ii in range(num_inputs): j_input = j_n_inputs[ii] i_type = j_input['type'] i_label = j_input['label'] i_has_widget = None i_widget_type = '' i_widget_name = '' i_widget_pos = None if i_type == 'data': i_has_widget = j_input['has widget'] if i_has_widget: i_widget_type = j_input['widget type'] i_widget_pos = j_input['widget position'] if i_widget_type == 'custom widget': i_widget_name = j_input['widget name'] new_input = NodePort() new_input.type_ = i_type new_input.label = i_label if i_has_widget: new_input.widget_type = i_widget_type new_input.widget_name = i_widget_name if i_widget_pos: new_input.widget_pos = i_widget_pos else: new_input.widget_type = 'None' inputs.append(new_input) j_n_outputs = j_node['outputs'] outputs = [] num_outputs = len(j_n_outputs) for oi in range(num_outputs): j_output = j_n_outputs[oi] o_type = j_output['type'] o_label = j_output['label'] new_output = NodePort() new_output.type_ = o_type new_output.label = o_label outputs.append(new_output) new_node.title = node_title new_node.description = node_description new_node.type_ = node_type new_node.package = package_name new_node.has_main_widget = node_has_main_widget if node_has_main_widget: new_node.main_widget_pos = node_main_widget_pos new_node.design_style = node_design_style new_node.color = QColor(node_color) new_node.inputs = inputs new_node.outputs = outputs self.custom_nodes.append(new_node) self.all_nodes.append(new_node) Debugger.debug(len(self.custom_nodes), 'nodes imported')