Skip to content

Merge pull request #1 from jimkring/PySide2-to-PySide6-Upgrade #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 92 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
2e4f2e6
Merge pull request #1 from jimkring/PySide2-to-PySide6-Upgrade
bhowiebkr Aug 2, 2022
0f4258a
black formatting
bhowiebkr Mar 4, 2023
ca5e626
vscode settings
bhowiebkr Mar 4, 2023
1936a86
fixing bugs when moving to PySide6
bhowiebkr Mar 4, 2023
274d2d4
using qdarktheme if available instead
bhowiebkr Mar 4, 2023
91ad07d
restructuring left side, added a button for making new node types
bhowiebkr Mar 4, 2023
5a5fdb5
cleanup
bhowiebkr Mar 4, 2023
5f811c4
removing the context menu because it doesn't do anything
bhowiebkr Mar 4, 2023
121ce22
this would be a node editor for new node types
bhowiebkr Mar 4, 2023
ea4eec0
cleanup
bhowiebkr Mar 4, 2023
ff8cb96
update
bhowiebkr Mar 14, 2023
8cad057
markdown formatting lost and fixed
bhowiebkr Mar 14, 2023
87eabe1
Added doc strings
bhowiebkr Mar 14, 2023
93b0a1d
video embed markdown
bhowiebkr Mar 14, 2023
86069cc
Several code smells, improvements, and clean-up
BoboTiG Mar 18, 2023
879756c
Merge pull request #3 from BoboTiG/impr-code-smells
bhowiebkr Mar 19, 2023
845c1f7
fixed- mouse wheel must have gotten broken from when moving from pysi…
bhowiebkr Mar 19, 2023
d4ddbeb
example project
bhowiebkr Apr 4, 2023
c827e3e
Loading project, and saving scene description
bhowiebkr Apr 4, 2023
64b8184
scene updated
bhowiebkr Apr 5, 2023
4f45ddb
moded loading modules here and now reading first json file
bhowiebkr Apr 5, 2023
366297f
update
bhowiebkr Apr 5, 2023
7dac9be
removed importing modules. Just add to the list the class nodes now.
bhowiebkr Apr 5, 2023
ec9917f
loading scene by json description
bhowiebkr Apr 5, 2023
4b92b86
helper to get port. TODO should store ports in dict for faster lookup.
bhowiebkr Apr 5, 2023
9bbbb02
nodes will not be made in the GUI. They will be python classes in the…
bhowiebkr Apr 5, 2023
612b1d2
removing new node button
bhowiebkr Apr 5, 2023
1c32e7f
Merge pull request #5 from bhowiebkr/code-execution
bhowiebkr Apr 5, 2023
a2e11cd
update
bhowiebkr Apr 5, 2023
4505bee
Merge branch 'master' of https://github.com/bhowiebkr/simple-node-editor
bhowiebkr Apr 5, 2023
3df785e
update
bhowiebkr Apr 5, 2023
bf36bc6
removing hard codes
bhowiebkr Apr 5, 2023
3fbebb8
note
bhowiebkr Apr 5, 2023
c052f42
gui update
bhowiebkr Apr 5, 2023
794cc6d
update
bhowiebkr Apr 6, 2023
29e18d3
ports change solid when connected
bhowiebkr Apr 6, 2023
73499ae
Thinner connection line
bhowiebkr Apr 6, 2023
201f320
background title color, text alignment to left
bhowiebkr Apr 6, 2023
a128c94
gradient title background
bhowiebkr Apr 6, 2023
d0d2d89
changed highlight colors to just be lighter than the original color
bhowiebkr Apr 6, 2023
7fc5bc1
adding execution pins
bhowiebkr Apr 6, 2023
eb2bc1d
gui update
bhowiebkr Apr 6, 2023
c030700
execution connections are thick and white
bhowiebkr Apr 6, 2023
1be72bc
execution ports look and get positioned differently
bhowiebkr Apr 6, 2023
5b7d7f8
class change of node, and the start of supporting widgets in graphics…
bhowiebkr Apr 7, 2023
d4711b3
color change testing
bhowiebkr Apr 7, 2023
9b282b7
a button event node. Will change/remove later
bhowiebkr Apr 7, 2023
3d72646
different scene showing 2 node types
bhowiebkr Apr 7, 2023
dde8be5
widget setup/changing computers
bhowiebkr Apr 7, 2023
f2340c2
renaming button widget
bhowiebkr Apr 8, 2023
e6dda41
position widget at the bottom of the node
bhowiebkr Apr 8, 2023
efa9c34
moving widget for button out of add node
bhowiebkr Apr 8, 2023
ed499f5
connecting add node with button
bhowiebkr Apr 8, 2023
cf9103d
a scaler node
bhowiebkr Apr 8, 2023
59b0307
common widgets
bhowiebkr Apr 8, 2023
1444b38
skipping things that are not nodes
bhowiebkr Apr 8, 2023
68950b1
print node with only inputs
bhowiebkr Apr 8, 2023
630b812
removing margins
bhowiebkr Apr 8, 2023
e507426
hiding prints
bhowiebkr Apr 8, 2023
396fc6b
Getting height alignment correct for all staes of inputs ports
bhowiebkr Apr 8, 2023
f30995f
debug with a 4 node types
bhowiebkr Apr 8, 2023
cfae4af
node look changed
bhowiebkr Apr 8, 2023
a24a723
forcing github to update image
bhowiebkr Apr 8, 2023
0c09629
added a status to nodes
bhowiebkr Apr 8, 2023
0c520db
renamed port to pin
bhowiebkr Apr 8, 2023
787dc6b
refactor node into logic and graphics
bhowiebkr Apr 8, 2023
7aca786
changing param of title
bhowiebkr Apr 8, 2023
0df3729
readme update
bhowiebkr Apr 8, 2023
e30d57f
moving files around
bhowiebkr Apr 8, 2023
3f184d1
refactoring
bhowiebkr Apr 8, 2023
d8f94f2
refactoring
bhowiebkr Apr 9, 2023
fc9b015
compute engine for getting the pin data between nodes
bhowiebkr Apr 9, 2023
9cfff65
rename
bhowiebkr Apr 9, 2023
9bc19e8
rename
bhowiebkr Apr 9, 2023
d72b367
refactor
bhowiebkr Apr 9, 2023
45450c5
moving compute to pin
bhowiebkr Apr 9, 2023
4155d9b
cleanup
bhowiebkr Apr 9, 2023
7abdf5a
adding pre-commit with static type checking
bhowiebkr Sep 23, 2024
e8c6ad4
static typing
bhowiebkr Sep 24, 2024
aa39cf9
enabling the widgets on custom nodes
bhowiebkr Sep 24, 2024
624715c
fixing custom list widget items, datatypes for module/class to Any
bhowiebkr Sep 24, 2024
4aa01c4
helper method to get items by type (Node, Connection, etc)
bhowiebkr Sep 26, 2024
13e0a46
ignore project folders
bhowiebkr Sep 26, 2024
a4acc73
start imp of exec of graphs
bhowiebkr Sep 26, 2024
daa2482
using index instead of hash
bhowiebkr Sep 26, 2024
8e8a854
start of computing DAG graphs
bhowiebkr Sep 26, 2024
e551e53
replacing hashes with indexes for nodes. Should keep thing more simpl…
bhowiebkr Sep 26, 2024
ab45833
node id's are in order, not random, start at 0
bhowiebkr Sep 26, 2024
b4b5cfc
added index to request node creation signal
bhowiebkr Sep 26, 2024
9e82a0a
node index are the next of the total nodes
bhowiebkr Sep 26, 2024
5bf98f8
topological sorting order functions
bhowiebkr Sep 26, 2024
c9fd5d6
moved the deletion of nodes to be in the scene object. When deleting …
bhowiebkr Sep 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -103,4 +103,10 @@ venv.bak/
# mypy
.mypy_cache/

temp
temp

#vscode settings
settings.json


*_Project
40 changes: 40 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 # Use the ref you want to point at
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files

- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
- id: black
args: [--line-length=120]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
args: [--strict, --ignore-missing-imports]

- repo: https://github.com/pycqa/flake8
rev: 7.1.1
hooks:
- id: flake8
additional_dependencies: [flake8-typing-imports==1.12.0]
args: [--max-line-length=120]

- repo: https://github.com/asottile/reorder_python_imports
rev: v3.13.0
hooks:
- id: reorder-python-imports
args: [--py37-plus, --add-import, 'from __future__ import annotations']

- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.5.0
hooks:
- id: setup-cfg-fmt
38 changes: 38 additions & 0 deletions Example_Project/Add_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations

from typing import Optional
from typing import Tuple

from node_editor.node import Node


class Add_Node(Node):
def __init__(self) -> None:
super().__init__()

self.title_text: str = "Add"
self.type_text: str = "Logic Nodes"
self.set_color(title_color=(0, 128, 0))

self.add_pin(name="Ex In", is_output=False, execution=True)
self.add_pin(name="Ex Out", is_output=True, execution=True)

self.add_pin(name="input A", is_output=False)
self.add_pin(name="input B", is_output=False)
self.add_pin(name="output", is_output=True)
self.build()

def set_color(
self,
title_color: Tuple[int, int, int],
background_color: Optional[Tuple[int, int, int]] = None,
) -> None:
super().set_color(title_color, background_color)

def add_pin(self, name: str, is_output: bool, execution: bool = False) -> None:
# Assuming add_pin is defined in the parent class
super().add_pin(name=name, is_output=is_output, execution=execution)

def build(self) -> None:
# Assuming build is defined in the parent class
super().build()
38 changes: 38 additions & 0 deletions Example_Project/Button_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations

from PySide6 import QtWidgets

from node_editor.node import Node


class Button_Node(Node):
def __init__(self) -> None:
super().__init__()

self.title_text = "Button"
self.type_text = "Inputs"
self.set_color(title_color=(128, 0, 0))

self.add_pin(name="Ex Out", is_output=True, execution=True)

self.build()

def init_widget(self) -> None:
self.widget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)

btn = QtWidgets.QPushButton("Button test")
btn.clicked.connect(self.btn_cmd)
layout.addWidget(btn)
self.widget.setLayout(layout)

proxy = QtWidgets.QGraphicsProxyWidget()
proxy.setWidget(self.widget)
proxy.setParentItem(self)

super().init_widget()

def btn_cmd(self) -> None:
print("btn command")
self.execute()
35 changes: 35 additions & 0 deletions Example_Project/Print_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

from typing import Optional
from typing import Tuple

from node_editor.node import Node


class Print_Node(Node):
def __init__(self) -> None:
super().__init__()

self.title_text: str = "Print"
self.type_text: str = "Debug Nodes"
self.set_color(title_color=(160, 32, 240))

self.add_pin(name="Ex In", is_output=False, execution=True)

self.add_pin(name="input", is_output=False)
self.build()

def set_color(
self,
title_color: Tuple[int, int, int],
background_color: Optional[Tuple[int, int, int]] = None,
) -> None:
super().set_color(title_color, background_color)

def add_pin(self, name: str, is_output: bool, execution: bool = False) -> None:
# Assuming add_pin is defined in the parent class
super().add_pin(name=name, is_output=is_output, execution=execution)

def build(self) -> None:
# Assuming build is defined in the parent class
super().build()
34 changes: 34 additions & 0 deletions Example_Project/Scaler_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from __future__ import annotations

from PySide6 import QtWidgets

from Example_Project.common_widgets import FloatLineEdit
from node_editor.node import Node


class Scaler_Node(Node):
def __init__(self) -> None:
super().__init__()

self.title_text = "Scaler"
self.type_text = "Constants"
self.set_color(title_color=(255, 165, 0))

self.add_pin(name="value", is_output=True)

self.build()

def init_widget(self) -> None:
self.widget = QtWidgets.QWidget()
self.widget.setFixedWidth(100)
layout = QtWidgets.QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.scaler_line = FloatLineEdit()
layout.addWidget(self.scaler_line)
self.widget.setLayout(layout)

proxy = QtWidgets.QGraphicsProxyWidget()
proxy.setWidget(self.widget)
proxy.setParentItem(self)

super().init_widget()
Empty file added Example_Project/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions Example_Project/common_widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

from typing import Optional
from typing import Tuple

from PySide6 import QtGui
from PySide6 import QtWidgets
from PySide6.QtCore import Qt


class FloatLineEdit(QtWidgets.QLineEdit): # type: ignore
def __init__(self, parent: Optional[QtWidgets.QWidget] = None) -> None:
super().__init__(parent)
self.setValidator(FloatValidator())

def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
if event.key() == Qt.Key_Space:
event.ignore()
else:
super().keyPressEvent(event)


class FloatValidator(QtGui.QDoubleValidator): # type: ignore
def __init__(self, parent: Optional[QtGui.QObject] = None) -> None:
super().__init__(parent)

def validate(self, input_str: str, pos: int) -> Tuple[QtGui.QValidator.State, str, int]:
state, num, pos = super().validate(input_str, pos)
if state == QtGui.QValidator.Acceptable:
return QtGui.QValidator.Acceptable, num, pos
if str(num).count(".") > 1:
return QtGui.QValidator.Invalid, num, pos
if input_str[pos - 1] == ".":
return QtGui.QValidator.Acceptable, num, pos
return QtGui.QValidator.Invalid, num, pos
66 changes: 66 additions & 0 deletions Example_Project/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"nodes": [
{
"type": "Scaler_Node",
"x": 4765,
"y": 5098,
"index": "4"
},
{
"type": "Scaler_Node",
"x": 4752,
"y": 5000,
"index": "3"
},
{
"type": "Print_Node",
"x": 5121,
"y": 4905,
"index": "2"
},
{
"type": "Button_Node",
"x": 4777,
"y": 4894,
"index": "1"
},
{
"type": "Add_Node",
"x": 4971,
"y": 4955,
"index": "0"
}
],
"connections": [
{
"start_id": "4",
"end_id": "0",
"start_pin": "value",
"end_pin": "input B"
},
{
"start_id": "3",
"end_id": "0",
"start_pin": "value",
"end_pin": "input A"
},
{
"start_id": "0",
"end_id": "2",
"start_pin": "output",
"end_pin": "input"
},
{
"start_id": "0",
"end_id": "2",
"start_pin": "Ex Out",
"end_pin": "Ex In"
},
{
"start_id": "1",
"end_id": "0",
"start_pin": "Ex Out",
"end_pin": "Ex In"
}
]
}
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
# Logic Node Editor
# Python Node Editor

A very basic minimal code for implementing a node graph or editor using PySide2. In this case we are building with logic nodes. All nodes are built using QGraphics items.
This is a node based Python tool used for visual scripting that is designed to be used for composing high level Python code into reusable blocks. Nodes look and function similar to Unreal Engine blueprints. Each node consists of connection pins and a widget section enabling the developer to write a full custom PySide GUI for each node type.

Example video: https://www.youtube.com/watch?v=DOsFJ8lm9dU
The tool is designed to allow you to write Python code in individual files per class/node. This means that your code is self-contained, easily modifiable, and reusable across multiple projects. Additionally, the GUI is designed to be familiar to those who have used Unreal Engine's blueprinting system, making it easy to learn and use.

![nodes](https://github.com/bhowiebkr/simple-node-editor/blob/master/images/node_editor.jpg)
My goal with this project is to provide a new and innovative way of organizing and working with Python code. While the tool is still in the development phase, I am constantly working to improve its functionality and features.

Visual scripting using nodes does have some benefits and drawbacks and it’s up to the end developer to decide when such a system is beneficial or not.

![nodes](https://github.com/bhowiebkr/simple-node-editor/blob/master/images/node_editor2.jpg)

Use it for:
- high level composing/configurable code. If a given system consists of many similar components but have a unique set of steps or requirements on similar tasks. Example a VFX or game pipeline.
- readability for non programmers as a dependency graph with built-in functionality
- enabling non-programmers a simple system to assemble blocks of logic
- networks that require a high level of feedback throughout that network and not just the end result. Example shader building, sound synthesizing, machine learning, robotics and sensors. Each node can have a custom visual feedback such as images, graphs, sound timelines, spreadsheets etc.
- prototyping logic.
- Generator scripts. Taking an input or building up a result that gets saved for other uses. Example textures, images, sound, ML training data.

Don’t use it for
- Anything complex. 40 nodes or less. This is because the user not only needs to think of how nodes are logically connected, but also the visual composure of nodes in the graph. It’s always best to refactor code when a graph gets too complex to make sense of.
- code that needs to run fast. The overhead of node based tools will increase processing in almost all cases.
- Code that doesn’t need a GUI/human interface to use.

For minimal GUI code for creating a node network see [GUI-nodes-only](https://github.com/bhowiebkr/simple-node-editor/tree/GUI-nodes-only) branch.


[![Video](http://img.youtube.com/vi/DOsFJ8lm9dU/0.jpg)](http://www.youtube.com/watch?v=DOsFJ8lm9dU)
Binary file removed images/node_editor.jpg
Binary file not shown.
Binary file added images/node_editor2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions launch.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@echo off

:: Activate the virtual environment
call venv\Scripts\activate

:: Start the three Python programs in separate command windows
start cmd /k python .\main.py
Loading