示例#1
0
    def onFileOpen(self):
        #dir = QFileDialog.getExistingDirectory(None, 'Select a directory', "/desktop", QFileDialog.ShowDirsOnl | QFileDialog.DontResolveSymlinks)
        # self.getFileDialogDirectory() #TODO: скопировать в строчку ниже
        fnames, filter = QFileDialog.getOpenFileNames(
            self, 'Open graph from file', '/Users/ouroboros/Desktop/',
            self.getFileDialogFilter())

        try:
            for fname in fnames:
                if fname:
                    existing = self.findMdiChild(fname)
                    if existing:
                        self.mdiArea.setActiveSubWindow(existing)
                    else:
                        # we need to create new subWindow and open the file
                        nodeeditor = CalculatorSubWindow()
                        if nodeeditor.fileLoad(fname):
                            self.statusBar().showMessage(
                                "File %s loaded" % fname, 5000)
                            nodeeditor.setTitle()
                            subwnd = self.createMdiChild(nodeeditor)
                            subwnd.show()
                            self.active_flag = True
                            self.updateToolButtons()
                            self.subwindow_counter += 1
                        else:
                            nodeeditor.close()
        except Exception as e:
            dumpException(e)
示例#2
0
    def onDrop(self, event):
        if event.mimeData().hasFormat(LISTBOX_MIMETYPE):
            eventData = event.mimeData().data(LISTBOX_MIMETYPE)
            dataStream = QDataStream(eventData, QIODevice.ReadOnly)
            pixmap = QPixmap()
            dataStream >> pixmap
            op_code = dataStream.readInt()
            text = dataStream.readQString()

            mouse_position = event.pos()
            scene_position = self.scene.grScene.views()[0].mapToScene(mouse_position)

            if DEBUG: print("GOT DROP: [%d] '%s'" % (op_code, text), "mouse:", mouse_position, "scene:", scene_position)

            try:
                node = get_class_from_opcode(op_code)(self.scene)
                node.setPos(scene_position.x(), scene_position.y())
                self.scene.history.storeHistory("Created node %s" % node.__class__.__name__)
            except Exception as e: dumpException(e)


            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            # print(" ... drop ignored, not requested format '%s'" % LISTBOX_MIMETYPE)
            event.ignore()
    def startDrag(self, *args, **kwargs):
        try:
            item = self.currentItem()
            op_code = item.data(Qt.UserRole + 1)

            pixmap = QPixmap(item.data(Qt.UserRole))

            itemData = QByteArray()
            dataStream = QDataStream(itemData, QIODevice.WriteOnly)
            dataStream << pixmap
            dataStream.writeInt(op_code)
            dataStream.writeQString(item.text())

            mimeData = QMimeData()
            mimeData.setData(LISTBOX_MIMETYPE, itemData)

            drag = QDrag(self)
            drag.setMimeData(mimeData)
            drag.setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2))
            drag.setPixmap(pixmap)

            drag.exec_(Qt.MoveAction)

        except Exception as e:
            dumpException(e)
示例#4
0
    def remove(self, silent_for_socket: 'Socket' = None, silent=False):
        """
        Safely remove this Edge.

        Removes `Graphics Edge` from the ``QGraphicsScene`` and it's reference to all GC to clean it up.
        Notifies vertices previously connected :class:`~nodeeditor.node_node.Node` (s) about this event.

        Triggers Nodes':

        - :py:meth:`~nodeeditor.node_node.Node.onEdgeConnectionChanged`
        - :py:meth:`~nodeeditor.node_node.Node.onInputChanged`

        :param silent_for_socket: :class:`~nodeeditor.node_socket.Socket` of a :class:`~nodeeditor.node_node.Node` which
            won't be notified, when this ``Edge`` is going to be removed
        :type silent_for_socket: :class:`~nodeeditor.node_socket.Socket`
        :param silent: ``True`` if no events should be triggered during removing
        :type silent: ``bool``
        """
        old_sockets = [self.start_socket, self.end_socket]

        # ugly hack, since I noticed that even when you remove grEdge from scene,
        # sometimes it stays there! How dare you Qt!
        if DEBUG: print(" - hide grEdge")
        #self.node.edges_in_self.discard(self.id)
        self.grEdge.hide()

        if DEBUG: print(" - remove grEdge", self.grEdge)
        self.scene.grScene.removeItem(self.grEdge)
        if DEBUG: print("   grEdge:", self.grEdge)

        self.scene.grScene.update()

        if DEBUG: print("# Removing Edge", self)
        if DEBUG: print(" - remove edge from all sockets")
        self.remove_from_sockets()
        if DEBUG: print(" - remove edge from scene")
        try:
            self.scene.removeEdge(self)
        except ValueError:
            pass
        if DEBUG: print(" - everything is done.")

        try:
            # notify vertices from old sockets
            for socket in old_sockets:
                if socket and socket.node:
                    if silent:
                        continue
                    if silent_for_socket is not None and socket == silent_for_socket:
                        # if we requested silence for Socket and it's this one, skip notifications
                        continue

                    # notify Socket's Node
                    socket.node.onEdgeConnectionChanged(self)
                    #if socket.is_input: socket.node.onInputChanged(socket)

        except Exception as e:
            dumpException(e)
示例#5
0
 def deserialize(self, data, hashmap={}):
     res = super().deserialize(data, hashmap)
     try:
         value = data['value']
         self.edit.setText(value)
         return True & res
     except Exception as e:
         dumpException(e)
     return res
示例#6
0
    def edgeDragEnd(self, item: 'QGraphicsItem'):
        """Code handling the end of dragging an `Edge` operation. In this code return True if skip the
        rest of the mouse event processing

        :param item: Item in the `Graphics Scene` where we ended dragging an `Edge`
        :type item: ``QGraphicsItem``
        """
        self.mode = MODE_NOOP

        if DEBUG: print('View::edgeDragEnd ~ End dragging edge')
        if self.drag_edge is not None:
            self.drag_edge.remove(
                silent=True)  # don't notify sockets about removing drag_edge
        self.drag_edge = None

        try:
            if isinstance(item, QDMGraphicsSocket):
                if item.socket != self.drag_start_socket:
                    # if we released dragging on a socket (other then the beginning socket)

                    ## First remove old edges / send notifications
                    for socket in (item.socket, self.drag_start_socket):
                        if not socket.is_multi_edges:
                            if socket.is_input:
                                # print("removing SILENTLY edges from input socket (is_input and !is_multi_edges) [DragStart]:", item.socket.edges)
                                socket.removeAllEdges(silent=True)
                            else:
                                socket.removeAllEdges(silent=False)

                    ## Create new Edge
                    if item.socket.is_input:
                        new_edge = Edge(self.grScene.scene,
                                        self.drag_start_socket,
                                        item.socket,
                                        edge_type=EDGE_TYPE_BEZIER)
                    else:
                        print("wrong end socket")
                    if DEBUG:
                        print("View::edgeDragEnd ~  created new edge:",
                              new_edge, "connecting", new_edge.start_socket,
                              "<-->", new_edge.end_socket)

                    ## Send notifications for the new edge
                    # for socket in [self.drag_start_socket, item.socket]:
                    # @TODO: Add possibility (ie when an input edge was replaced) to be silent and don't trigger change
                    #                        socket.node.onEdgeConnectionChanged(new_edge)
                    #if socket.is_input: socket.node.onInputChanged(socket)

                    self.grScene.scene.history.storeHistory(
                        "Created new edge by dragging", setModified=True)
                    return True
        except Exception as e:
            dumpException(e)

        if DEBUG: print('View::edgeDragEnd ~ everything done.')
        return False
示例#7
0
 def onFileNew(self):
     try:
         subwnd = self.createMdiChild()
         subwnd.widget().fileNew()
         subwnd.show()
         self.active_flag = False
         self.updateToolButtons()
         self.subwindow_counter += 1
     except Exception as e:
         dumpException(e)
示例#8
0
    def leftMouseButtonRelease(self, event: QMouseEvent):
        """When Left  mouse button was released"""

        # get item which we release mouse button on
        item = self.getItemAtClick(event)

        try:
            # logic
            if hasattr(item, "node") or isinstance(
                    item, QDMGraphicsEdge) or item is None:
                if event.modifiers() & Qt.ShiftModifier:
                    event.ignore()
                    fakeEvent = QMouseEvent(
                        event.type(), event.localPos(), event.screenPos(),
                        Qt.LeftButton, Qt.NoButton,
                        event.modifiers() | Qt.ControlModifier)
                    super().mouseReleaseEvent(fakeEvent)
                    return

            if self.mode == MODE_EDGE_DRAG:
                if self.distanceBetweenClickAndReleaseIsOff(event):
                    res = self.edgeDragEnd(item)
                    if res: return

            if self.mode == MODE_EDGE_CUT:
                self.cutIntersectingEdges()
                self.cutline.line_points = []
                self.cutline.update()
                QApplication.setOverrideCursor(Qt.ArrowCursor)
                self.mode = MODE_NOOP
                return

            if self.rubberBandDraggingRectangle:
                self.rubberBandDraggingRectangle = False
                current_selected_items = self.grScene.selectedItems()

                if current_selected_items != self.grScene.scene._last_selected_items:
                    if current_selected_items == []:
                        self.grScene.itemsDeselected.emit()
                    else:
                        self.grScene.itemSelected.emit()
                    self.grScene.scene._last_selected_items = current_selected_items

                return

            # otherwise deselect everything
            if item is None:
                self.grScene.itemsDeselected.emit()

        except:
            dumpException()

        super().mouseReleaseEvent(event)
示例#9
0
    def updateEditMenu(self):
        try:
            # print("update Edit Menu")
            active = self.getCurrentNodeEditorWidget()
            hasMdiChild = (active is not None)

            self.actPaste.setEnabled(hasMdiChild)

            self.actCut.setEnabled(hasMdiChild and active.hasSelectedItems())
            self.actCopy.setEnabled(hasMdiChild and active.hasSelectedItems())
            self.actDelete.setEnabled(hasMdiChild
                                      and active.hasSelectedItems())

            self.actUndo.setEnabled(hasMdiChild and active.canUndo())
            self.actRedo.setEnabled(hasMdiChild and active.canRedo())
        except Exception as e:
            dumpException(e)
示例#10
0
 def edgeDragStart(self, item: 'QGraphicsItem'):
     """Code handling the start of dragging an `Edge` operation"""
     try:
         if DEBUG: print('View::edgeDragStart ~ Start dragging edge')
         if DEBUG:
             print('View::edgeDragStart ~   assign Start Socket to:',
                   item.socket)
         self.drag_start_socket = item.socket
         if self.drag_start_socket.is_input == False:
             self.drag_edge = Edge(self.grScene.scene, item.socket, None,
                                   EDGE_TYPE_BEZIER)
             if DEBUG:
                 print('View::edgeDragStart ~   dragEdge:', self.drag_edge)
         else:
             self.drag_edge = None
     except Exception as e:
         dumpException(e)
示例#11
0
    def getInput(self, index: int = 0) -> ['Node', None]:
        """
        Get the **first**  `Node` connected to the  Input specified by `index`

        :param index: Order number of the `Input Socket`
        :type index: ``int``
        :return: :class:`~nodeeditor.node_node.Node` which is connected to the specified `Input` or ``None`` if there is no connection of index is out of range
        :rtype: :class:`~nodeeditor.node_node.Node` or ``None``
        """
        try:
            input_socket = self.inputs[index]
            if len(input_socket.edges) == 0: return None
            connecting_edge = input_socket.edges[0]
            other_socket = connecting_edge.getOtherSocket(self.inputs[index])
            return other_socket.node
        except Exception as e:
            dumpException(e)
            return None
示例#12
0
    def contextMenuEvent(self, event):
        try:
            item = self.scene.getItemAt(event.pos())
            if DEBUG_CONTEXT: print(item)

            if type(item) == QGraphicsProxyWidget:
                item = item.widget()

            if hasattr(item, 'node') or hasattr(item, 'socket'):
                self.handleNodeContextMenu(event)
            elif hasattr(item, 'edge'):
                self.handleEdgeContextMenu(event)
            #elif item is None:
            else:
                self.handleNewNodeContextMenu(event)

            return super().contextMenuEvent(event)
        except Exception as e: dumpException(e)
示例#13
0
    def getInputWithSocketIndex(self, index: int = 0) -> ('Node', int):
        """
        Get the **first**  `Node` connected to the Input specified by `index` and the connection `Socket`

        :param index: Order number of the `Input Socket`
        :type index: ``int``
        :return: Tuple containing :class:`~nodeeditor.node_node.Node` and :class:`~nodeeditor.node_socket.Socket` which
            is connected to the specified `Input` or ``None`` if there is no connection of index is out of range
        :rtype: (:class:`~nodeeditor.node_node.Node`, int)
        """
        try:
            edge = self.inputs[index].edges[0]
            socket = edge.getOtherSocket(self.inputs[index])
            return socket.node, socket.index
        except IndexError:
            # print("EXC: Trying to get input with socket index %d, but none is attached to" % index, self)
            return None, None
        except Exception as e:
            dumpException(e)
            return None, None
示例#14
0
    def loadFromFile(self, filename: str):
        """
        Load `Scene` from a file on disk

        :param filename: from what file to load the `Scene`
        :type filename: ``str``
        :raises: :class:`~nodeeditor.node_scene.InvalidFile` if there was an error decoding JSON file
        """

        with open(filename, "r") as file:
            raw_data = file.read()
            try:
                data = json.loads(raw_data, encoding='utf-8')
                self.deserialize(data)
                self.has_been_modified = False
            except json.JSONDecodeError:
                raise InvalidFile("%s is not a valid JSON file" %
                                  os.path.basename(filename))
            except Exception as e:
                dumpException(e)
示例#15
0
    def getInputWithSocket(self,
                           index: int = 0
                           ) -> [('Node', 'Socket'), (None, None)]:
        """
        Get the **first**  `Node` connected to the Input specified by `index` and the connection `Socket`

        :param index: Order number of the `Input Socket`
        :type index: ``int``
        :return: Tuple containing :class:`~nodeeditor.node_node.Node` and :class:`~nodeeditor.node_socket.Socket` which
            is connected to the specified `Input` or ``None`` if there is no connection of index is out of range
        :rtype: (:class:`~nodeeditor.node_node.Node`, :class:`~nodeeditor.node_socket.Socket`)
        """
        try:
            input_socket = self.inputs[index]
            if len(input_socket.edges) == 0: return None, None
            connecting_edge = input_socket.edges[0]
            other_socket = connecting_edge.getOtherSocket(self.inputs[index])
            return other_socket.node, other_socket
        except Exception as e:
            dumpException(e)
            return None, None
示例#16
0
    def deserialize(self,
                    data: dict,
                    hashmap: dict = {},
                    restore_id: bool = True) -> bool:
        try:
            if restore_id: self.id = data['id']
            hashmap[data['id']] = self

            self.setPos(data['pos_x'], data['pos_y'])
            #self.title = data['title']

            data['inputs'].sort(key=lambda socket: socket['index'] + socket[
                'position'] * 10000)
            data['outputs'].sort(key=lambda socket: socket['index'] + socket[
                'position'] * 10000)
            num_inputs = len(data['inputs'])
            num_outputs = len(data['outputs'])

            # print("> deserialize node,   num inputs:", num_inputs, "num outputs:", num_outputs)
            # pp(data)

            # possible way to do it is reuse existing sockets...
            # dont create new ones if not necessary

            for socket_data in data['inputs']:
                found = None
                for socket in self.inputs:
                    # print("\t", socket, socket.index, "=?", socket_data['index'])
                    if socket.index == socket_data['index']:
                        found = socket
                        break
                if found is None:
                    # print("deserialization of socket data has not found input socket with index:", socket_data['index'])
                    # print("actual socket data:", socket_data)
                    # we can create new socket for this
                    found = self.__class__.Socket_class(
                        node=self,
                        index=socket_data['index'],
                        position=socket_data['position'],
                        socket_type=socket_data['socket_type'],
                        count_on_this_node_side=num_inputs,
                        is_input=True)
                    self.inputs.append(
                        found)  # append newly created input to the list
                found.deserialize(socket_data, hashmap, restore_id)

            for socket_data in data['outputs']:
                found = None
                for socket in self.outputs:
                    # print("\t", socket, socket.index, "=?", socket_data['index'])
                    if socket.index == socket_data['index']:
                        found = socket
                        break
                if found is None:
                    # print("deserialization of socket data has not found output socket with index:", socket_data['index'])
                    # print("actual socket data:", socket_data)
                    # we can create new socket for this
                    found = self.__class__.Socket_class(
                        node=self,
                        index=socket_data['index'],
                        position=socket_data['position'],
                        socket_type=socket_data['socket_type'],
                        count_on_this_node_side=num_outputs,
                        is_input=False)
                    self.outputs.append(
                        found)  # append newly created output to the list
                found.deserialize(socket_data, hashmap, restore_id)

        except Exception as e:
            dumpException(e)

        for curve in data['curves_in_self']:
            self.edges_in_self.add(curve)
        # also deseralize the content of the node
        # so far the rest was ok, now as last step the content...
        if isinstance(self.content, Serializable):
            res = self.content.deserialize(data['name'], hashmap)
            return res

        return True