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
nodeeditor/py.typed ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ QDMNodeContentWidget { background: transparent; }
2
+ QDMNodeContentWidget QLabel { color: #e0e0e0; }
3
+ QGraphicsView { selection-background-color: rgb(255, 255, 255); }
nodeeditor/utils.py ADDED
@@ -0,0 +1,108 @@
1
+ # -*- encoding: utf-8 -*-
2
+ """
3
+ Module with some helper functions
4
+ """
5
+ from typing import cast
6
+ from nodeeditor import _QT_API_NAME as QT_API
7
+ from qtpy.QtCore import QFile, QByteArray
8
+ from qtpy.QtWidgets import QApplication
9
+
10
+ from nodeeditor.utils_no_qt import pp, dumpException
11
+
12
+
13
+ def loadStylesheet(filename: str) -> None:
14
+ """
15
+ Loads an qss stylesheet to the current QApplication instance
16
+
17
+ :param filename: Filename of qss stylesheet
18
+ :type filename: str
19
+ Raises:
20
+ RuntimeError: If no QApplication instance exists
21
+ """
22
+ print('STYLE loading:', filename)
23
+ # app = QApplication.instance()
24
+ app = cast(QApplication, QApplication.instance())
25
+ if app is None:
26
+ raise RuntimeError("No QApplication instance found")
27
+
28
+ file = QFile(filename)
29
+ file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text)
30
+ try:
31
+ stylesheet: QByteArray = file.readAll()
32
+ stylesheet_str = stylesheet.data().decode('utf-8')
33
+ app.setStyleSheet(stylesheet_str)
34
+ finally:
35
+ file.close()
36
+
37
+
38
+ def loadStylesheets(*args) -> None:
39
+ """
40
+ Loads multiple qss stylesheets. Concatenates them together and applies the final stylesheet to the current QApplication instance
41
+
42
+ :param args: variable number of filenames of qss stylesheets
43
+ :type args: str, str,...
44
+ Raises:
45
+ RuntimeError: If no QApplication instance exists
46
+ """
47
+ app = cast(QApplication, QApplication.instance())
48
+ if app is None:
49
+ raise RuntimeError("No QApplication instance found")
50
+
51
+ res = ''
52
+ for arg in args:
53
+ file = QFile(arg)
54
+ file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text)
55
+ try:
56
+ stylesheet: QByteArray = file.readAll()
57
+ res += "\n" + stylesheet.data().decode('utf-8')
58
+ finally:
59
+ file.close()
60
+ app.setStyleSheet(res)
61
+
62
+
63
+ def isCTRLPressed(event):
64
+ """
65
+ Checks whether the Control key is held down in the given event.
66
+
67
+ :param event: Qt's mouse or key event
68
+ :type event: ``QEvent``
69
+ :return: the Control modifier of the event, truthy when the key is held
70
+ :rtype: ``int``
71
+ """
72
+ from qtpy.QtCore import Qt
73
+ if QT_API in ("pyqt5", "pyside2"):
74
+ return event.modifiers() & Qt.CTRL
75
+ if QT_API in ("pyqt6", "pyside6"):
76
+ return event.modifiers() & Qt.KeyboardModifier.ControlModifier
77
+
78
+
79
+ def isSHIFTPressed(event):
80
+ """
81
+ Checks whether the Shift key is held down in the given event.
82
+
83
+ :param event: Qt's mouse or key event
84
+ :type event: ``QEvent``
85
+ :return: the Shift modifier of the event, truthy when the key is held
86
+ :rtype: ``int``
87
+ """
88
+ from qtpy.QtCore import Qt
89
+ if QT_API in ("pyqt5", "pyside2"):
90
+ return event.modifiers() & Qt.SHIFT
91
+ if QT_API in ("pyqt6", "pyside6"):
92
+ return event.modifiers() & Qt.KeyboardModifier.ShiftModifier
93
+
94
+
95
+ def isALTPressed(event):
96
+ """
97
+ Checks whether the Alt key is held down in the given event.
98
+
99
+ :param event: Qt's mouse or key event
100
+ :type event: ``QEvent``
101
+ :return: the Alt modifier of the event, truthy when the key is held
102
+ :rtype: ``int``
103
+ """
104
+ from qtpy.QtCore import Qt
105
+ if QT_API in ("pyqt5", "pyside2"):
106
+ return event.modifiers() & Qt.ALT
107
+ if QT_API in ("pyqt6", "pyside6"):
108
+ return event.modifiers() & Qt.KeyboardModifier.AltModifier
@@ -0,0 +1,21 @@
1
+ """
2
+ Module with helper functions which do not depend on Qt
3
+ """
4
+ import traceback
5
+ from pprint import PrettyPrinter
6
+
7
+
8
+ def dumpException(e=None) -> None:
9
+ """
10
+ Prints out an Exception message with a traceback to the console
11
+
12
+ :param e: Exception to print out
13
+ :type e: Exception
14
+ """
15
+ # print("%s EXCEPTION:" % e.__class__.__name__, e)
16
+ # traceback.print_tb(e.__traceback__)
17
+ traceback.print_exc()
18
+
19
+
20
+ pp = PrettyPrinter(indent=4).pprint
21
+
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: nodeeditor-qtpy
3
+ Version: 0.8.1
4
+ Summary: Framework for building node editors and graph UIs in Qt, independent of the Qt binding (PyQt5/PyQt6/PySide2/PySide6 via QtPy)
5
+ Keywords: node editor,nodeeditor,node graph,graph,visual programming,qt,qtpy,pyqt6,pyside6
6
+ Author: Azad Kshitij
7
+ Author-email: Azad Kshitij <azadkshitij08302001@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENCE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: User Interfaces
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: orjson>=3.12.0
21
+ Requires-Dist: qtpy>=2.4.3
22
+ Requires-Dist: pyqt6>=6.0 ; extra == 'pyqt6'
23
+ Requires-Dist: pyside6>=6.0 ; extra == 'pyside6'
24
+ Requires-Python: >=3.10
25
+ Project-URL: Homepage, https://github.com/AzadKshitij/qtpy-nodeeditor
26
+ Project-URL: Repository, https://github.com/AzadKshitij/qtpy-nodeeditor.git
27
+ Project-URL: Issues, https://github.com/AzadKshitij/qtpy-nodeeditor/issues
28
+ Project-URL: Changelog, https://github.com/AzadKshitij/qtpy-nodeeditor/blob/master/HISTORY.rst
29
+ Provides-Extra: pyqt6
30
+ Provides-Extra: pyside6
31
+ Description-Content-Type: text/markdown
32
+
33
+ # nodeeditor-qtpy
34
+
35
+ A node editor framework for Qt. Build interactive graph interfaces out of
36
+ draggable nodes, connectable sockets and edges, with undo/redo, JSON
37
+ serialization and support for your own evaluation logic.
38
+
39
+ The distribution is called `nodeeditor-qtpy`; the import package is `nodeeditor`.
40
+ The framework itself is binding-agnostic: it talks to Qt through
41
+ [QtPy](https://github.com/spyder-ide/qtpy), so it runs on PyQt5, PySide2,
42
+ PyQt6 and PySide6.
43
+
44
+ ![Calculator example](https://raw.githubusercontent.com/AzadKshitij/qtpy-nodeeditor/main/examples/calculator_ss_1.png)
45
+
46
+ ## Features
47
+
48
+ - full framework for customizable nodes, sockets and edges
49
+ - undo / redo and serialization into JSON files in a VCS friendly way
50
+ - support for implementing evaluation logic
51
+ - hovering effects, dragging edges, cutting lines, snapping and more
52
+ - node groups, multi-input nodes, custom socket shapes and color schemes
53
+ - ready-made examples you can copy from
54
+
55
+ ## Requirements
56
+
57
+ - Python 3.10+
58
+ - A Qt binding: PyQt6 or PySide6 (any Qt 5/6 binding QtPy supports works)
59
+
60
+ ## Installation
61
+
62
+ From PyPI, including a Qt binding:
63
+
64
+ ```
65
+ pip install nodeeditor-qtpy[pyqt6]
66
+ ```
67
+
68
+ or, for PySide6:
69
+
70
+ ```
71
+ pip install nodeeditor-qtpy[pyside6]
72
+ ```
73
+
74
+ Without an extra, the package installs no binding and uses whichever
75
+ PyQt5/PySide2/PyQt6/PySide6 you already have. When several are installed,
76
+ QtPy picks one from the `QT_API` environment variable.
77
+
78
+ Directly from source code to get the latest version:
79
+
80
+ ```
81
+ pip install "nodeeditor-qtpy[pyqt6] @ git+https://github.com/AzadKshitij/qtpy-nodeeditor.git"
82
+ ```
83
+
84
+ Or clone the repository:
85
+
86
+ ```
87
+ git clone https://github.com/AzadKshitij/qtpy-nodeeditor.git
88
+ ```
89
+
90
+ ## Quick start
91
+
92
+ A node is a `Node` belonging to a `Scene`. Give it sockets, then connect an
93
+ output socket to an input socket with an `Edge`:
94
+
95
+ ```python
96
+ import sys
97
+
98
+ from qtpy.QtWidgets import QApplication
99
+
100
+ from nodeeditor.node_editor_widget import NodeEditorWidget
101
+ from nodeeditor.node_node import Node
102
+ from nodeeditor.node_edge import Edge
103
+
104
+ app = QApplication(sys.argv)
105
+
106
+ editor = NodeEditorWidget()
107
+ scene = editor.scene
108
+
109
+ # a node with one output socket
110
+ source = Node(scene, title="Value", outputs=[1], output_text=["out"])
111
+
112
+ # a node with one input socket
113
+ sink = Node(scene, title="Print", inputs=[1], input_text=["in"])
114
+
115
+ # connect the output of the first node to the input of the second
116
+ Edge(scene, source.outputs[0], sink.inputs[0])
117
+
118
+ editor.show()
119
+ sys.exit(app.exec())
120
+ ```
121
+
122
+ `NodeEditorWidget` creates the `Scene` for you. Use `Scene` on its own when
123
+ you want to drive the graph without a widget:
124
+
125
+ ```python
126
+ from nodeeditor.node_scene import Scene
127
+ from nodeeditor.node_node import Node
128
+
129
+ scene = Scene()
130
+ node = Node(scene, title="Value", outputs=[1], output_text=["out"])
131
+ ```
132
+
133
+ The `inputs` and `outputs` lists hold socket types, and the matching
134
+ `input_text` / `output_text` lists hold the labels drawn next to them. Both
135
+ `Node` and `Edge` register themselves with the scene when created, so there is
136
+ nothing to add by hand.
137
+
138
+ ## Import names
139
+
140
+ ```python
141
+ from nodeeditor import NodeEditorColorScheme, get_socket_shape
142
+ from nodeeditor.node_scene import Scene
143
+ from nodeeditor.node_node import Node
144
+ from nodeeditor.node_edge import Edge
145
+ ```
146
+
147
+ ## Credits
148
+
149
+ This project is a fork of the original
150
+ [PyQt Node Editor](https://gitlab.com/pavel.krupala/pyqt-node-editor) by
151
+ [Pavel Křupala](https://gitlab.com/pavel.krupala), which is released under the
152
+ MIT licence. The original author is credited in [LICENCE](LICENCE), and the
153
+ history of the upstream project is kept in
154
+ [CHANGES.md](CHANGES.md) and [HISTORY.rst](HISTORY.rst).
155
+
156
+ ## Links
157
+
158
+ - [Documentation](https://azadkshitij.github.io/qtpy-nodeeditor/)
159
+ - [Examples](https://github.com/AzadKshitij/qtpy-nodeeditor/tree/main/examples)
160
+ - [Original project](https://gitlab.com/pavel.krupala/pyqt-node-editor)
161
+ - [Contribute](https://github.com/AzadKshitij/qtpy-nodeeditor/blob/main/CONTRIBUTING.md)
162
+ - [Issues](https://github.com/AzadKshitij/qtpy-nodeeditor/issues)
163
+ - [Pull requests](https://github.com/AzadKshitij/qtpy-nodeeditor/pulls)
164
+ - [Release history](https://github.com/AzadKshitij/qtpy-nodeeditor/blob/main/HISTORY.rst)
165
+ - [Upstream changelog (pre-fork)](https://github.com/AzadKshitij/qtpy-nodeeditor/blob/main/CHANGES.md)
@@ -0,0 +1,40 @@
1
+ nodeeditor/__init__.py,sha256=XTuvO4iP1WvzBtY9KDasTsppNlWKiY65WjBvtlaTF5s,2320
2
+ nodeeditor/node_colors_config.py,sha256=ww8I_UNU2WS8IxHJFmUEzQgG7f1HHVy6iT8mnCzaJ1A,10695
3
+ nodeeditor/node_content_widget.py,sha256=XuOWCpHB6R60vQQIqFv5aRP__ys0G7JYSBQMeH-fHsc,5119
4
+ nodeeditor/node_decorators.py,sha256=KqwWekiTL3LEz-HlvXBMsaNtGHjE_iUMrFk8Ekg8V00,2540
5
+ nodeeditor/node_edge.py,sha256=rxd5d_nd1DYjjTi0AzEHRePm8Y8zswEkHKN8z1YCkzM,20143
6
+ nodeeditor/node_edge_dragging.py,sha256=lhg4-Ycgux_Yt_m48M2J1rDA_Z_0cPHiST6ONU-oj4o,7368
7
+ nodeeditor/node_edge_intersect.py,sha256=2K5vc6O_7tBHf4m6vsyv5-PHvL_CUvC2teYrIqHyNr4,8205
8
+ nodeeditor/node_edge_rerouting.py,sha256=LyGVTFhEskFKuteKvACdl1hNsvLoanWx9g_QkvRMOUQ,8759
9
+ nodeeditor/node_edge_snapping.py,sha256=Van0hJJTJi6WedH4ktEgMsYXb6X9bEiwWBaYN2Ihztg,3784
10
+ nodeeditor/node_edge_validators.py,sha256=BXBeeZbFNgDYSu-5tBAbO-XivI1-cOeRso88Xshn2eM,2676
11
+ nodeeditor/node_editor_widget.py,sha256=BYzsxqEQwylo1t9xyEBxfUr91HKr4FFDa9JJZynSqsg,8873
12
+ nodeeditor/node_editor_window.py,sha256=snZ0_6HiFH7PJiXX9BjKP6e_hsQ_ZaWP85MnUveN5y0,17708
13
+ nodeeditor/node_graph.py,sha256=B3i64NFwwETbUZaDnxpazmQqBkuKatgK6Bs9L-BEa08,20039
14
+ nodeeditor/node_graphics_cutline.py,sha256=0-XTN9eYjV43eSAp4mdWU3Ms1_0a06V6oCstWdZ2Oxw,1784
15
+ nodeeditor/node_graphics_edge.py,sha256=Z_mTy3ySGdlH6HrQPUS1QZ7kGymYMS5TlpZtuFJZB1c,15356
16
+ nodeeditor/node_graphics_edge_path.py,sha256=2i0wvJVARA0wATOF1aTg2E1JQXMRwiGKEOTeZMEX7tQ,7456
17
+ nodeeditor/node_graphics_node.py,sha256=eKM29YyTA-9PxIFCOKW4iR5AlFH0_pEuij0MQStRmvY,27218
18
+ nodeeditor/node_graphics_scene.py,sha256=lsfi9DFHRtz4P7IaUxaSgRiuPDfyHavAC3t0Lq_EOyA,14731
19
+ nodeeditor/node_graphics_socket.py,sha256=k3PsdDjXDx6kN8Ka6_VZRzQsr1RFndwhxOIzuYYit00,8977
20
+ nodeeditor/node_graphics_view.py,sha256=HkrYcGH7PjmBnmwMmvLnvDQIUIRr7FZ1pSul__OtPkE,36214
21
+ nodeeditor/node_group.py,sha256=1bIX1Rc4UqDQAKT84z7a2tSmxMN2DHfBJO5AVAwaE54,50102
22
+ nodeeditor/node_group_node.py,sha256=TrV8P1vscZWF9aIPzPpXLe2oh_yX4eAGSSQLQIOJR2E,254
23
+ nodeeditor/node_icon_content_widget.py,sha256=hBY7ko3mCE_wI_qL1lnk3JXIBA-7jKmW_5uB8tYk8fw,6947
24
+ nodeeditor/node_icon_graphics_node.py,sha256=lsn6v4bFsziMDfDCYNaWdPnoquXXKjINeHIIyXe8KFY,21493
25
+ nodeeditor/node_multi_input_node.py,sha256=hDdZdKHvvOtgjx2684DGyNGAGY_I-QurbtGuqrrd0wo,17811
26
+ nodeeditor/node_node.py,sha256=bEgGdbuG5_5G3GkUPTfZWQZSD72NFZhB7NY3qBerDXQ,34813
27
+ nodeeditor/node_scene.py,sha256=1bzSoS61zjZCnqeqdZYmIXh6H4AeuxusW62NmvSeqro,42236
28
+ nodeeditor/node_scene_clipboard.py,sha256=vkIljNAi5EvkOUO_VJf4MI2d07rlpevkDUcZ7JOCfFs,11742
29
+ nodeeditor/node_scene_history.py,sha256=R8-azhzAuQ7tpJWpJ8IjUwrYpHIILQkgGoKWLQYzO20,20099
30
+ nodeeditor/node_serializable.py,sha256=Z4uFqS-JI4EDnRrhqG1CimY0HNBUMtrQa8FnitQvEMg,1666
31
+ nodeeditor/node_socket.py,sha256=SUvxIF5AyDvewPzq_9MKGyS5T28XchSH5AjKNoDd4kE,17231
32
+ nodeeditor/node_socket_shapes.py,sha256=Ff3m9my-INwfK6wvuMukTNhibNuAw4TSwYzX1UNFWNw,8735
33
+ nodeeditor/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ nodeeditor/qss/nodestyle.qss,sha256=eU8hdcmX_NuGux8KA-xdkwaMXqXQndZsoUqe-dgCFxw,164
35
+ nodeeditor/utils.py,sha256=Hts5U3m67rtXNMf8xnYZHnmfmtvD-oB02NNu2MfwiQ4,3342
36
+ nodeeditor/utils_no_qt.py,sha256=B8d5aX3TH-iolGhkSBZ3VT3BDd59XyLC9mLxKKnQyw8,457
37
+ nodeeditor_qtpy-0.8.1.dist-info/licenses/LICENCE,sha256=uOd7uoKZsgX55xpCk9EXhPIMKKEW8tDIAlkhl3RXeK0,1111
38
+ nodeeditor_qtpy-0.8.1.dist-info/WHEEL,sha256=1dD_GoEqEIxQ7VnrUExgdj_eExY4b4UgfR0iBlGKVac,80
39
+ nodeeditor_qtpy-0.8.1.dist-info/METADATA,sha256=FOZOG5He98EJWaM0w4obvh8_F0vLX_IJKcTf1_D-oiM,5699
40
+ nodeeditor_qtpy-0.8.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.13.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019, Pavel Křupala
4
+ Copyright (c) 2024-2026, Azad Kshitij
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+