nodeeditor-qtpy 0.8.1__tar.gz → 0.8.2__tar.gz

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 (133) hide show
  1. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/HISTORY.rst +87 -1
  2. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/PKG-INFO +3 -1
  3. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/README.md +2 -0
  4. nodeeditor_qtpy-0.8.2/docs/api/comment.md +8 -0
  5. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/index.md +1 -0
  6. nodeeditor_qtpy-0.8.2/docs/guides/comments.md +96 -0
  7. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/calc_window.py +30 -0
  8. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/mkdocs.yml +2 -0
  9. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/__init__.py +5 -1
  10. nodeeditor_qtpy-0.8.2/nodeeditor/node_comment.py +735 -0
  11. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_cutline.py +9 -2
  12. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_node.py +78 -7
  13. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_view.py +10 -0
  14. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_icon_graphics_node.py +52 -6
  15. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_scene.py +148 -0
  16. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_scene_history.py +27 -2
  17. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/pyproject.toml +1 -1
  18. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/pyproject.toml.orig +1 -1
  19. nodeeditor_qtpy-0.8.2/tests/test_130_comment.py +283 -0
  20. nodeeditor_qtpy-0.8.2/tests/test_210_label_geometry.py +120 -0
  21. nodeeditor_qtpy-0.8.2/tests/test_400_comment.py +94 -0
  22. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/CHANGES.md +0 -0
  23. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/LICENCE +0 -0
  24. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/CONTRIBUTING.md +0 -0
  25. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/color-scheme.md +0 -0
  26. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/content.md +0 -0
  27. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/edge.md +0 -0
  28. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/editor-widget.md +0 -0
  29. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/editor-window.md +0 -0
  30. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/graph.md +0 -0
  31. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/graphics.md +0 -0
  32. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/group.md +0 -0
  33. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/node.md +0 -0
  34. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/scene.md +0 -0
  35. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/serializable.md +0 -0
  36. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/socket-shapes.md +0 -0
  37. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/socket.md +0 -0
  38. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/api/utils.md +0 -0
  39. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/events.md +0 -0
  40. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/coding-standards.md +0 -0
  41. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/edge-labels.md +0 -0
  42. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/graph-algorithms.md +0 -0
  43. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/group-nodes.md +0 -0
  44. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/multi-input-nodes.md +0 -0
  45. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/node-grouping.md +0 -0
  46. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/node-labels.md +0 -0
  47. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/guides/socket-shapes.md +0 -0
  48. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/edge-labels.png +0 -0
  49. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/graph-algorithms.png +0 -0
  50. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/group-collapsed.png +0 -0
  51. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/group-nodes.png +0 -0
  52. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/multi-input.png +0 -0
  53. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/node-labels.png +0 -0
  54. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/quickstart.png +0 -0
  55. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/images/socket-shapes.png +0 -0
  56. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/index.md +0 -0
  57. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/installation.md +0 -0
  58. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/quickstart.md +0 -0
  59. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/screenshot-calculator.png +0 -0
  60. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/docs/screenshot-example.png +0 -0
  61. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/calculator_ss_1.png +0 -0
  62. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/calc_conf.py +0 -0
  63. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/calc_drag_listbox.py +0 -0
  64. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/calc_graph_inspector.py +0 -0
  65. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/calc_node_base.py +0 -0
  66. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/calc_sub_window.py +0 -0
  67. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/example.json +0 -0
  68. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/example2.json +0 -0
  69. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/example3.json +0 -0
  70. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/File Output.png +0 -0
  71. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/add.png +0 -0
  72. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/divide.png +0 -0
  73. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/dot.png +0 -0
  74. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/icons.psd +0 -0
  75. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/in.png +0 -0
  76. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/mul.png +0 -0
  77. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/out.png +0 -0
  78. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/status_icons.png +0 -0
  79. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/status_icons.psd +0 -0
  80. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/icons/sub.png +0 -0
  81. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/main.py +0 -0
  82. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/nodes/__init__.py +0 -0
  83. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/nodes/check.py +0 -0
  84. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/nodes/input.py +0 -0
  85. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/nodes/multi_input.py +0 -0
  86. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/nodes/operations.py +0 -0
  87. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/nodes/output.py +0 -0
  88. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/qss/nodeeditor-dark.qss +0 -0
  89. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/qss/nodeeditor.qss +0 -0
  90. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/qss/nodeeditor.styl +0 -0
  91. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_calculator/qss/nodeeditor_dark_resources.py +0 -0
  92. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_color_config.py +0 -0
  93. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_socket_shapes.py +0 -0
  94. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_test/graph.json +0 -0
  95. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_test/graph2.json +0 -0
  96. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/examples/example_test/main_ex2.py +0 -0
  97. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_colors_config.py +0 -0
  98. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_content_widget.py +0 -0
  99. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_decorators.py +0 -0
  100. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_edge.py +0 -0
  101. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_edge_dragging.py +0 -0
  102. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_edge_intersect.py +0 -0
  103. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_edge_rerouting.py +0 -0
  104. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_edge_snapping.py +0 -0
  105. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_edge_validators.py +0 -0
  106. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_editor_widget.py +0 -0
  107. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_editor_window.py +0 -0
  108. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graph.py +0 -0
  109. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_edge.py +0 -0
  110. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_edge_path.py +0 -0
  111. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_scene.py +0 -0
  112. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_graphics_socket.py +0 -0
  113. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_group.py +0 -0
  114. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_group_node.py +0 -0
  115. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_icon_content_widget.py +0 -0
  116. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_multi_input_node.py +0 -0
  117. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_node.py +0 -0
  118. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_scene_clipboard.py +0 -0
  119. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_serializable.py +0 -0
  120. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_socket.py +0 -0
  121. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/node_socket_shapes.py +0 -0
  122. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/py.typed +0 -0
  123. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/qss/nodestyle.qss +0 -0
  124. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/utils.py +0 -0
  125. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/nodeeditor/utils_no_qt.py +0 -0
  126. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/requirements_docs.txt +0 -0
  127. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/__init__.py +0 -0
  128. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/test_000_import.py +0 -0
  129. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/test_100_multi_input.py +0 -0
  130. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/test_110_socket_shapes.py +0 -0
  131. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/test_120_graph.py +0 -0
  132. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/test_200_detail_policy.py +0 -0
  133. {nodeeditor_qtpy-0.8.1 → nodeeditor_qtpy-0.8.2}/tests/test_300_hover_colors.py +0 -0
@@ -1,12 +1,98 @@
1
1
  History
2
2
  =======
3
3
 
4
- Release history for **qtpy-nodeeditor**.
4
+ Release history for **nodeeditor-qtpy**.
5
5
 
6
6
  This file tracks releases of this project. For the pre-fork upstream
7
7
  `PyNodeEditor <https://github.com/benbyjones/PyQtNodeEditor>`_ lineage that
8
8
  this code started from, see `CHANGES.md <CHANGES.md>`_.
9
9
 
10
+ 0.8.2 - 2026-10-25
11
+ ------------------
12
+
13
+ **Fixes**
14
+
15
+ - A dragged node no longer leaves a stack of label plates behind it. Two
16
+ declared rectangles were smaller than what their items painted:
17
+ ``QDMGraphicsNodeLabel`` declared the text while painting a rounded box
18
+ padded by 6x3 px, and ``QDMGraphicsNode`` / ``QDMIconGraphicsNode`` declared
19
+ their body while the label floats above it. Qt repaints a moved item by the
20
+ rectangle it declared at the old position and dirties its children only at
21
+ the new one, so the old plate was never cleared. The label now declares its
22
+ plate, ``textRect()`` keeps the text rectangle for callers that position it,
23
+ the nodes expose ``labelRect()`` and include it in their own rectangle, and a
24
+ label that moves, changes text or is hidden by the level of detail re-indexes
25
+ its node with ``prepareGeometryChange()``.
26
+ - ``QDMGraphicsCutline`` declared the bare polyline while stroking it with a
27
+ 2px pen, so a moving cut line left the outer half of the stroke behind. Its
28
+ rectangle now accounts for the pen.
29
+
30
+ **Tests**
31
+
32
+ - Added ``tests/test_210_label_geometry.py``: the label declares its plate, a
33
+ node declares the label above it, hiding the label leaves the body alone, and
34
+ a node move dirties the plate's old position.
35
+ - The suite now runs 121 tests.
36
+
37
+ 0.8.1 - 2026-10-21
38
+ ------------------
39
+
40
+ **Features**
41
+
42
+ - Added ``nodeeditor.node_graph``: graph algorithms over a ``Scene`` without a
43
+ third-party graph library. ``topological_order`` (Kahn's algorithm, scene
44
+ order as the tie-break, raising ``GraphCycleError``), ``descendants`` and
45
+ ``ancestors`` (breadth-first with a visited set), ``components`` (the
46
+ disconnected subgraphs, via union-find), ``cycles`` (one representative cycle
47
+ per cyclic region), and ``paths`` and ``shortest_path`` (parallel edges stay
48
+ distinct). Every algorithm is also a thin wrapper on ``Scene``, so
49
+ ``scene.shortest_path(a, b)`` works without importing the module, and
50
+ ``GraphCycleError`` is exported from the package.
51
+ - Added a graph inspector panel to the calculator example, which runs the
52
+ algorithms on the active graph and selects the matching node on the canvas.
53
+ - ``Node.markDescendantsDirty`` and ``markDescendantsInvalid`` now walk with
54
+ ``descendants()`` instead of recursing. The old walk raised
55
+ ``RecursionError`` on a cycle and on chains over 1000 nodes, and visited a
56
+ node once per path into it: a 42-node diamond graph took 4.5 s, now about
57
+ 0.0002 s.
58
+
59
+ **Packaging**
60
+
61
+ - First release on PyPI, as ``nodeeditor-qtpy``. The import package stays
62
+ ``nodeeditor``.
63
+ - Built with ``uv_build``; the ``hatchling`` configuration is kept as a
64
+ fallback and produces the same artifacts.
65
+ - Dependencies are ``qtpy`` and ``orjson`` only. The bindings are optional
66
+ extras, ``pyqt6`` and ``pyside6``, and any binding QtPy supports keeps
67
+ working. Python 3.10 and newer.
68
+ - The licence is an SPDX ``MIT`` expression and the licence file ships in the
69
+ wheel, with the upstream copyright retained.
70
+ - Uploads run from GitHub Actions with PEP 740 attestations, so PyPI records
71
+ provenance instead of showing "without provenance".
72
+ - ``nodeeditor.__version__`` now reports the version of the installed
73
+ distribution, and importing the package without a Qt binding raises an error
74
+ that names the extras to install.
75
+
76
+ **Docs**
77
+
78
+ - New documentation site at
79
+ https://azadkshitij.github.io/qtpy-nodeeditor/, built with MkDocs, the
80
+ Material theme and mkdocstrings. The API reference is generated from the
81
+ docstrings in the source, so it always matches the installed code.
82
+ - Added pages for the installation, a quick start, the event system and node
83
+ groups, and the guides now carry screenshots.
84
+ - Documented every public class, method and function in the package.
85
+ - The site is redeployed whenever a commit pushed to ``main`` carries
86
+ "publish" in its message.
87
+
88
+ **Tests**
89
+
90
+ - Added ``tests/test_120_graph.py`` for the algorithms, the ``Scene`` wrappers
91
+ and the dirty walk.
92
+ - Moved the hover colour test into ``tests/``, where it runs with the suite; it
93
+ was stranded in the repository root and never collected.
94
+ - The suite now runs 90 tests.
95
+
10
96
  0.8 - 2026-10-21
11
97
  ----------------
12
98
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nodeeditor-qtpy
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: Framework for building node editors and graph UIs in Qt, independent of the Qt binding (PyQt5/PyQt6/PySide2/PySide6 via QtPy)
5
5
  Keywords: node editor,nodeeditor,node graph,graph,visual programming,qt,qtpy,pyqt6,pyside6
6
6
  Author: Azad Kshitij
@@ -50,6 +50,7 @@ PyQt6 and PySide6.
50
50
  - support for implementing evaluation logic
51
51
  - hovering effects, dragging edges, cutting lines, snapping and more
52
52
  - node groups, multi-input nodes, custom socket shapes and color schemes
53
+ - canvas comments (sticky notes) with inline editing and serialization
53
54
  - ready-made examples you can copy from
54
55
 
55
56
  ## Requirements
@@ -142,6 +143,7 @@ from nodeeditor import NodeEditorColorScheme, get_socket_shape
142
143
  from nodeeditor.node_scene import Scene
143
144
  from nodeeditor.node_node import Node
144
145
  from nodeeditor.node_edge import Edge
146
+ from nodeeditor.node_comment import Comment
145
147
  ```
146
148
 
147
149
  ## Credits
@@ -18,6 +18,7 @@ PyQt6 and PySide6.
18
18
  - support for implementing evaluation logic
19
19
  - hovering effects, dragging edges, cutting lines, snapping and more
20
20
  - node groups, multi-input nodes, custom socket shapes and color schemes
21
+ - canvas comments (sticky notes) with inline editing and serialization
21
22
  - ready-made examples you can copy from
22
23
 
23
24
  ## Requirements
@@ -110,6 +111,7 @@ from nodeeditor import NodeEditorColorScheme, get_socket_shape
110
111
  from nodeeditor.node_scene import Scene
111
112
  from nodeeditor.node_node import Node
112
113
  from nodeeditor.node_edge import Edge
114
+ from nodeeditor.node_comment import Comment
113
115
  ```
114
116
 
115
117
  ## Credits
@@ -0,0 +1,8 @@
1
+ A `Comment` is a canvas note: a movable, selectable box that holds multi-line
2
+ plain text. It is a visual item like a `Group`, not a `Node`. It has no sockets,
3
+ never runs, and has no effect on evaluation.
4
+
5
+ See [Canvas comments](../guides/comments.md) for usage, editing semantics and
6
+ the serialized shape.
7
+
8
+ ::: nodeeditor.node_comment
@@ -16,6 +16,7 @@ The package re-exports the pieces most applications need:
16
16
  | [Scene](scene.md) | `node_scene`, `node_scene_clipboard`, `node_scene_history` |
17
17
  | [Node](node.md) | `node_node`, `node_multi_input_node` |
18
18
  | [Groups](group.md) | `node_group` |
19
+ | [Comments](comment.md) | `node_comment` |
19
20
  | [Edge](edge.md) | `node_edge` and the edge interaction helpers |
20
21
  | [Socket](socket.md) | `node_socket` |
21
22
  | [Socket shapes](socket-shapes.md) | `node_socket_shapes` |
@@ -0,0 +1,96 @@
1
+ # Canvas comments
2
+
3
+ A `Comment` is a canvas note: a movable, selectable box that holds multi-line
4
+ plain text. It is a visual item like a `Group`, not a `Node`. It has no sockets,
5
+ never runs, and has no effect on evaluation.
6
+
7
+ Use `Scene.addComment()` as the short form:
8
+
9
+ ```python
10
+ note = scene.addComment(
11
+ "Remember to tune this after the first run.",
12
+ x=40, y=25, width=260,
13
+ )
14
+ ```
15
+
16
+ Or construct one directly. Both forms register the note with the scene:
17
+
18
+ ```python
19
+ from nodeeditor.node_comment import Comment
20
+
21
+ note = Comment(scene, "text", x=40, y=25, width=260, height=120)
22
+ ```
23
+
24
+ The note auto-sizes: its width is the requested width (at least
25
+ `Comment.MIN_WIDTH`) and its height grows to fit the wrapped text, never below
26
+ `Comment.MIN_HEIGHT`. `setSize(w, h)` changes the requested size and
27
+ `setText()` replaces the text.
28
+
29
+ ## Editing
30
+
31
+ Double-click the note, or call `begin_edit()`, to open the inline editor.
32
+ Editing ends in one of these ways:
33
+
34
+ - Focus leaves the editor (clicking elsewhere): the text is committed and a
35
+ `"Edited comment"` history entry is stored.
36
+ - `Escape` is pressed: the edit is cancelled and the text from before the edit
37
+ is restored. No history entry is stored.
38
+ - `Ctrl+Enter` is pressed, or `finish_edit()` is called: the text is committed.
39
+ - `cancel_edit()` is called: the edit is cancelled explicitly.
40
+
41
+ `setText(text, store_history=True)` also stores a history entry. The default
42
+ `store_history=False` keeps generated text and scene loading out of the undo
43
+ stack. History stamps snapshot the whole scene, and comments are part of that
44
+ snapshot, so undo and redo restore comment text, position, size, colours and
45
+ z-order.
46
+
47
+ ## Colours and order
48
+
49
+ The defaults are theme-neutral, so the library does not read an application
50
+ theme manager:
51
+
52
+ ```python
53
+ note.setBackgroundColor("#fff8d6")
54
+ note.setBorderColor((190, 175, 120))
55
+ note.setTextColor("#222222")
56
+ note.setColors(background="#e8f0ff", border="#8aa4c8")
57
+ ```
58
+
59
+ Each setter accepts a `QColor`, a colour name or an RGB/RGBA tuple. Moving a
60
+ note updates its scene position. `bringToFront()`, `sendToBack()` and
61
+ `resetZValue()` change the stacking order; the z-value is saved with the note.
62
+
63
+ ## Selection and removal
64
+
65
+ Notes are ordinary selectable items, so rubber-band selection and `Shift`
66
+ selection work. `Scene.getSelectedComments()` returns the selected notes.
67
+ `Delete` removes them through `QDMGraphicsView.deleteSelected()`, and the
68
+ context menu offers edit, order and delete actions. `note.remove()` removes a
69
+ note programmatically; the caller decides whether to store a history entry.
70
+
71
+ ## Serialization
72
+
73
+ Comments are stored in the additive `Scene` key `"comments"`. Files written
74
+ before comments existed have no such key and load unchanged.
75
+
76
+ ```json
77
+ {
78
+ "id": 1234,
79
+ "type": "Comment",
80
+ "version": 1,
81
+ "text": "first line\nsecond line",
82
+ "x": 40.0,
83
+ "y": 25.0,
84
+ "w": 240.0,
85
+ "h": 110.0,
86
+ "z": 1.0,
87
+ "style": {
88
+ "background": [255, 248, 214, 232],
89
+ "border": [190, 175, 120, 255],
90
+ "text": [35, 35, 35, 255]
91
+ }
92
+ }
93
+ ```
94
+
95
+ `w` and `h` are the outer size after auto-sizing; `z` is the item z-value.
96
+ Missing `style` or `z` keys fall back to the neutral defaults.
@@ -135,6 +135,11 @@ class CalculatorWindow(NodeEditorWindow):
135
135
  triggered=self.showSelectedPath)
136
136
  self.actShortestPath.setShortcut(QKeySequence("Ctrl+Shift+P"))
137
137
 
138
+ self.actNewComment = QAction("&New Comment", self,
139
+ statusTip="Add a sticky note to the canvas",
140
+ triggered=self.onAddComment)
141
+ self.actNewComment.setShortcut(QKeySequence("Ctrl+Shift+N"))
142
+
138
143
  def getCurrentScene(self):
139
144
  """Shortcut for the `Scene` of the active tab, or ``None``."""
140
145
  active = self.getCurrentNodeEditorWidget()
@@ -214,6 +219,10 @@ class CalculatorWindow(NodeEditorWindow):
214
219
  def createMenus(self):
215
220
  super().createMenus()
216
221
 
222
+ # "New Comment" belongs next to the other Edit-menu editing actions.
223
+ self.editMenu.addSeparator()
224
+ self.editMenu.addAction(self.actNewComment)
225
+
217
226
  self.graphMenu = self.menuBar().addMenu("&Graph")
218
227
  self.graphMenu.addAction(self.actTopologicalOrder)
219
228
  self.graphMenu.addAction(self.actComponents)
@@ -255,6 +264,7 @@ class CalculatorWindow(NodeEditorWindow):
255
264
  self.actComponents.setEnabled(hasMdiChild)
256
265
  self.actCycles.setEnabled(hasMdiChild)
257
266
  self.actShortestPath.setEnabled(hasMdiChild)
267
+ self.actNewComment.setEnabled(hasMdiChild)
258
268
 
259
269
  self.updateEditMenu()
260
270
 
@@ -290,6 +300,26 @@ class CalculatorWindow(NodeEditorWindow):
290
300
  # gridAct.setChecked(self.scene.grScene.showGrid)
291
301
  # gridAct.triggered.connect(self.onShowGrid)
292
302
 
303
+ def onAddComment(self):
304
+ """Add a sticky note at the center of the active canvas and edit it."""
305
+ scene = self.getCurrentScene()
306
+ if scene is None:
307
+ self.statusBar().showMessage("Open a graph before adding a comment", 3000)
308
+ return
309
+
310
+ try:
311
+ view = self.getCurrentNodeEditorWidget().view
312
+ center = view.mapToScene(view.viewport().rect().center())
313
+ x, y = center.x(), center.y()
314
+ except Exception:
315
+ x, y = 0, 0
316
+
317
+ comment = scene.addComment("", x=x, y=y, width=240)
318
+ comment.begin_edit()
319
+ self.statusBar().showMessage(
320
+ "Comment added. Type your note, then click away or press Ctrl+Enter to save.",
321
+ 4000)
322
+
293
323
  def onShowGrid(self, checked: bool) -> None:
294
324
  """
295
325
  Toggle the grid visibility for all subwindows
@@ -87,6 +87,7 @@ nav:
87
87
  - Events: events.md
88
88
  - Guides:
89
89
  - Group nodes: guides/group-nodes.md
90
+ - Canvas comments: guides/comments.md
90
91
  - Node grouping (reference): guides/node-grouping.md
91
92
  - Multi-input nodes: guides/multi-input-nodes.md
92
93
  - Graph algorithms: guides/graph-algorithms.md
@@ -99,6 +100,7 @@ nav:
99
100
  - Scene: api/scene.md
100
101
  - Node: api/node.md
101
102
  - Groups: api/group.md
103
+ - Comments: api/comment.md
102
104
  - Edge: api/edge.md
103
105
  - Socket: api/socket.md
104
106
  - Socket shapes: api/socket-shapes.md
@@ -9,7 +9,7 @@ __author__ = 'Azad Kshitij'
9
9
  try:
10
10
  __version__ = _dist_version("nodeeditor-qtpy")
11
11
  except PackageNotFoundError: # running from a source checkout
12
- __version__ = "0.8.1"
12
+ __version__ = "0.8.2"
13
13
 
14
14
 
15
15
  _QT_API_NAME, _QT_API_VERSION = None, None
@@ -74,6 +74,9 @@ from nodeeditor.node_socket_shapes import (
74
74
  # Export the graph algorithm exception
75
75
  from nodeeditor.node_graph import GraphCycleError
76
76
 
77
+ # Export the canvas note item for convenient construction
78
+ from nodeeditor.node_comment import Comment
79
+
77
80
 
78
81
  __all__ = [
79
82
  "NodeEditorColorScheme",
@@ -90,4 +93,5 @@ __all__ = [
90
93
  "get_socket_shape",
91
94
  "SOCKET_SHAPES",
92
95
  "GraphCycleError",
96
+ "Comment",
93
97
  ]