nodeeditor-qtpy 0.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. nodeeditor/__init__.py +93 -0
  2. nodeeditor/node_colors_config.py +343 -0
  3. nodeeditor/node_content_widget.py +129 -0
  4. nodeeditor/node_decorators.py +65 -0
  5. nodeeditor/node_edge.py +518 -0
  6. nodeeditor/node_edge_dragging.py +185 -0
  7. nodeeditor/node_edge_intersect.py +219 -0
  8. nodeeditor/node_edge_rerouting.py +216 -0
  9. nodeeditor/node_edge_snapping.py +95 -0
  10. nodeeditor/node_edge_validators.py +82 -0
  11. nodeeditor/node_editor_widget.py +257 -0
  12. nodeeditor/node_editor_window.py +448 -0
  13. nodeeditor/node_graph.py +583 -0
  14. nodeeditor/node_graphics_cutline.py +57 -0
  15. nodeeditor/node_graphics_edge.py +375 -0
  16. nodeeditor/node_graphics_edge_path.py +226 -0
  17. nodeeditor/node_graphics_node.py +693 -0
  18. nodeeditor/node_graphics_scene.py +369 -0
  19. nodeeditor/node_graphics_socket.py +262 -0
  20. nodeeditor/node_graphics_view.py +907 -0
  21. nodeeditor/node_group.py +1286 -0
  22. nodeeditor/node_group_node.py +5 -0
  23. nodeeditor/node_icon_content_widget.py +176 -0
  24. nodeeditor/node_icon_graphics_node.py +524 -0
  25. nodeeditor/node_multi_input_node.py +450 -0
  26. nodeeditor/node_node.py +848 -0
  27. nodeeditor/node_scene.py +1106 -0
  28. nodeeditor/node_scene_clipboard.py +314 -0
  29. nodeeditor/node_scene_history.py +556 -0
  30. nodeeditor/node_serializable.py +43 -0
  31. nodeeditor/node_socket.py +445 -0
  32. nodeeditor/node_socket_shapes.py +261 -0
  33. nodeeditor/py.typed +0 -0
  34. nodeeditor/qss/nodestyle.qss +3 -0
  35. nodeeditor/utils.py +108 -0
  36. nodeeditor/utils_no_qt.py +21 -0
  37. nodeeditor_qtpy-0.8.1.dist-info/METADATA +165 -0
  38. nodeeditor_qtpy-0.8.1.dist-info/RECORD +40 -0
  39. nodeeditor_qtpy-0.8.1.dist-info/WHEEL +4 -0
  40. nodeeditor_qtpy-0.8.1.dist-info/licenses/LICENCE +23 -0
@@ -0,0 +1,257 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ A module containing ``NodeEditorWidget`` class
4
+ """
5
+ import os
6
+ from qtpy.QtCore import Qt
7
+ from qtpy.QtGui import QBrush, QPen, QFont, QColor
8
+ from qtpy.QtWidgets import QWidget, QVBoxLayout, QApplication, QMessageBox, QLabel, QGraphicsItem, QTextEdit, QPushButton
9
+
10
+ from nodeeditor.node_scene import Scene, InvalidFile
11
+ from nodeeditor.node_node import Node
12
+ from nodeeditor.node_edge import Edge, EDGE_TYPE_BEZIER
13
+ from nodeeditor.node_graphics_view import QDMGraphicsView
14
+ from nodeeditor.utils import dumpException
15
+
16
+ from typing import TYPE_CHECKING, List, Optional, Tuple, Any
17
+
18
+
19
+ if TYPE_CHECKING:
20
+ from nodeeditor.node_graphics_view import QDMGraphicsView
21
+ from nodeeditor.node_edge import Edge
22
+ from nodeeditor.node_socket import Socket
23
+
24
+
25
+ class NodeEditorWidget(QWidget):
26
+ """Widget hosting a graph :class:`~nodeeditor.node_scene.Scene` and its graphics view.
27
+
28
+ The ``Scene`` and the ``GraphicsView`` classes used by this widget can be replaced
29
+ by subclasses through the ``Scene_class`` and ``GraphicsView_class`` attributes.
30
+ """
31
+
32
+ Scene_class = Scene
33
+ GraphicsView_class = QDMGraphicsView
34
+
35
+ def __init__(self, parent: QWidget = None):
36
+ """
37
+ :param parent: parent widget
38
+ :type parent: ``QWidget``
39
+
40
+ :Instance Attributes:
41
+
42
+ - **filename** - currently graph's filename or ``None``
43
+ """
44
+ super().__init__(parent)
45
+
46
+ self.filename: Optional[str] = None
47
+
48
+ self.initUI()
49
+
50
+ def initUI(self):
51
+ """Set up this ``NodeEditorWidget`` with its layout, :class:`~nodeeditor.node_scene.Scene` and
52
+ :class:`~nodeeditor.node_graphics_view.QDMGraphicsView`"""
53
+ self.layout = QVBoxLayout()
54
+ self.layout.setContentsMargins(0, 0, 0, 0)
55
+ self.setLayout(self.layout)
56
+
57
+ # crate graphics scene
58
+ self.scene = self.__class__.Scene_class()
59
+
60
+ # create graphics view
61
+ self.view = self.__class__.GraphicsView_class(self.scene.grScene, self)
62
+ self.layout.addWidget(self.view)
63
+
64
+ def isModified(self) -> bool:
65
+ """Has the `Scene` been modified?
66
+
67
+ :return: ``True`` if the `Scene` has been modified
68
+ :rtype: ``bool``
69
+ """
70
+ return self.scene.isModified()
71
+
72
+ def isFilenameSet(self) -> bool:
73
+ """Do we have a graph loaded from file or are we creating a new one?
74
+
75
+ :return: ``True`` if filename is set. ``False`` if it is a new graph not yet saved to a file
76
+ :rtype: ''bool''
77
+ """
78
+ return self.filename is not None
79
+
80
+ def getSelectedItems(self) -> list:
81
+ """Shortcut returning `Scene`'s currently selected items
82
+
83
+ :return: list of ``QGraphicsItems``
84
+ :rtype: list[QGraphicsItem]
85
+ """
86
+ return self.scene.getSelectedItems()
87
+
88
+ def hasSelectedItems(self) -> bool:
89
+ """Is there something selected in the :class:`nodeeditor.node_scene.Scene`?
90
+
91
+ :return: ``True`` if there is something selected in the `Scene`
92
+ :rtype: ``bool``
93
+ """
94
+ return self.getSelectedItems() != []
95
+
96
+ def canUndo(self) -> bool:
97
+ """Can Undo be performed right now?
98
+
99
+ :return: ``True`` if we can undo
100
+ :rtype: ``bool``
101
+ """
102
+ return self.scene.history.canUndo()
103
+
104
+ def canRedo(self) -> bool:
105
+ """Can Redo be performed right now?
106
+
107
+ :return: ``True`` if we can redo
108
+ :rtype: ``bool``
109
+ """
110
+ return self.scene.history.canRedo()
111
+
112
+ def getUserFriendlyFilename(self) -> str:
113
+ """Get user friendly filename. Used in the window title
114
+
115
+ :return: just a base name of the file or `'New Graph'`
116
+ :rtype: ``str``
117
+ """
118
+ if self.filename is None:
119
+ return "New Graph"
120
+
121
+ print("filename:", self.filename)
122
+
123
+ name = os.path.basename(
124
+ self.filename) if self.isFilenameSet() else "New Graph"
125
+
126
+ print("name:", name)
127
+
128
+ return name + ("*" if self.isModified() else "")
129
+
130
+ def fileNew(self):
131
+ """Empty the scene (create new file)"""
132
+ self.scene.clear()
133
+ self.filename = None
134
+ self.scene.history.clear()
135
+ self.scene.history.storeInitialHistoryStamp()
136
+
137
+ def fileLoad(self, filename: str):
138
+ """Load serialized graph from JSON file
139
+
140
+ :param filename: file to load
141
+ :type filename: ``str``
142
+ """
143
+ QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
144
+ try:
145
+ self.scene.loadFromFile(filename)
146
+ self.filename = filename
147
+ self.scene.history.clear()
148
+ self.scene.history.storeInitialHistoryStamp()
149
+ return True
150
+ except FileNotFoundError as e:
151
+ dumpException(e)
152
+ QMessageBox.warning(self, "Error loading %s" % os.path.basename(
153
+ filename), str(e).replace('[Errno 2]', ''))
154
+ return False
155
+ except InvalidFile as e:
156
+ dumpException(e)
157
+ # QApplication.restoreOverrideCursor()
158
+ QMessageBox.warning(self, "Error loading %s" %
159
+ os.path.basename(filename), str(e))
160
+ return False
161
+ finally:
162
+ QApplication.restoreOverrideCursor()
163
+
164
+ def fileSave(self, filename: str = None) -> bool:
165
+ """Save serialized graph to JSON file. When called with an empty parameter, we won't store/remember the filename.
166
+
167
+ :param filename: file to store the graph
168
+ :type filename: ``str``
169
+ :return: ``True`` if the file was saved successfully, ``False`` otherwise
170
+ """
171
+ # Check if we have a valid filename
172
+ if not self.filename and not filename:
173
+ return False
174
+
175
+ # Update filename if provided
176
+ if filename:
177
+ self.filename = filename
178
+
179
+ try:
180
+ QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
181
+ self.scene.saveToFile(self.filename)
182
+ return True
183
+ except Exception as e:
184
+ dumpException(e)
185
+ return False
186
+ finally:
187
+ QApplication.restoreOverrideCursor()
188
+
189
+ def addNodes(self):
190
+ """Testing method to create 3 `Nodes` with 3 `Edges` connecting them"""
191
+ node1 = Node(self.scene, "My Awesome Node 1",
192
+ inputs=[0, 0, 0], outputs=[1, 5])
193
+ node2 = Node(self.scene, "My Awesome Node 2",
194
+ inputs=[3, 3, 3], outputs=[1])
195
+ node3 = Node(self.scene, "My Awesome Node 3",
196
+ inputs=[2, 2, 2], outputs=[1])
197
+ node1.setPos(-350, -250)
198
+ node2.setPos(-75, 0)
199
+ node3.setPos(200, -200)
200
+
201
+ edge1 = Edge(
202
+ self.scene, node1.outputs[0], node2.inputs[0], edge_type=EDGE_TYPE_BEZIER)
203
+ edge2 = Edge(
204
+ self.scene, node2.outputs[0], node3.inputs[0], edge_type=EDGE_TYPE_BEZIER)
205
+ edge3 = Edge(
206
+ self.scene, node1.outputs[0], node3.inputs[2], edge_type=EDGE_TYPE_BEZIER)
207
+
208
+ self.scene.history.storeInitialHistoryStamp()
209
+
210
+ def addCustomNode(self):
211
+ """Testing method to create a custom Node with custom content"""
212
+ from nodeeditor.node_content_widget import QDMNodeContentWidget
213
+ from nodeeditor.node_serializable import Serializable
214
+
215
+ class NNodeContent(QLabel): # , Serializable):
216
+ def __init__(self, node, parent=None):
217
+ super().__init__("FooBar")
218
+ self.node = node
219
+ self.setParent(parent)
220
+
221
+ class NNode(Node):
222
+ NodeContent_class = NNodeContent
223
+
224
+ self.scene.setNodeClassSelector(lambda data: NNode)
225
+ node = NNode(self.scene, "A Custom Node 1", inputs=[0, 1, 2])
226
+
227
+ print("node content:", node.content)
228
+
229
+ def addDebugContent(self):
230
+ """Testing method to put random QGraphicsItems and elements into QGraphicsScene"""
231
+ greenBrush = QBrush(Qt.green)
232
+ outlinePen = QPen(Qt.black)
233
+ outlinePen.setWidth(2)
234
+
235
+ rect = self.grScene.addRect(-100, -100, 80,
236
+ 100, outlinePen, greenBrush)
237
+ rect.setFlag(QGraphicsItem.ItemIsMovable)
238
+
239
+ text = self.grScene.addText(
240
+ "This is my Awesome text!", QFont("Ubuntu"))
241
+ text.setFlag(QGraphicsItem.ItemIsSelectable)
242
+ text.setFlag(QGraphicsItem.ItemIsMovable)
243
+ text.setDefaultTextColor(QColor.fromRgbF(1.0, 1.0, 1.0))
244
+
245
+ widget1 = QPushButton("Hello World")
246
+ proxy1 = self.grScene.addWidget(widget1)
247
+ proxy1.setFlag(QGraphicsItem.ItemIsMovable)
248
+ proxy1.setPos(0, 30)
249
+
250
+ widget2 = QTextEdit()
251
+ proxy2 = self.grScene.addWidget(widget2)
252
+ proxy2.setFlag(QGraphicsItem.ItemIsSelectable)
253
+ proxy2.setPos(0, 60)
254
+
255
+ line = self.grScene.addLine(-200, -200, 400, -100, outlinePen)
256
+ line.setFlag(QGraphicsItem.ItemIsMovable)
257
+ line.setFlag(QGraphicsItem.ItemIsSelectable)