Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Although this graph makes no sense, it shows the current look and feel:
## Code Example

```python
from PySide import QtGui
from PySide2 import QtWidgets
from qtnodes import (Header, Node, InputKnob,
OutputKnob, NodeGraphWidget)

Expand All @@ -28,7 +28,7 @@ class Multiply(Node):
self.addKnob(InputKnob(name="y"))
self.addKnob(OutputKnob(name="value"))

app = QtGui.QApplication([])
app = QtWidgets.QApplication([])
graph = NodeGraphWidget()
graph.registerNodeClass(Multiply)
graph.addNode(Multiply())
Expand Down
5 changes: 2 additions & 3 deletions qtnodes/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Manual tests."""

from PySide import QtGui

from .qtchooser import QtWidgets
from .knob import InputKnob, OutputKnob
from .header import Header
from .node import Node
Expand Down Expand Up @@ -120,7 +119,7 @@ def __init__(self):


def test():
app = QtGui.QApplication([])
app = QtWidgets.QApplication([])

graph = NodeGraphWidget()
graph.setGeometry(100, 100, 800, 600)
Expand Down
11 changes: 5 additions & 6 deletions qtnodes/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

import os

from PySide import QtGui
from PySide import QtCore
from .qtchooser import QtCore, QtGui, QtWidgets


windows = os.name == "nt"

DELETE_MODIFIER_KEY = QtCore.Qt.AltModifier if windows else QtCore.Qt.ControlModifier


class Edge(QtGui.QGraphicsPathItem):
class Edge(QtWidgets.QGraphicsPathItem):
"""A connection between two Knobs."""

def __init__(self, **kwargs):
Expand Down Expand Up @@ -66,14 +65,14 @@ def updatePath(self):
self.setPath(path)

def paint(self, painter, option, widget):
"""Paint Edge color depending on modifier key pressed or not."""
mod = QtGui.QApplication.keyboardModifiers() == DELETE_MODIFIER_KEY
"""Paint Edge color depending on modifier key pressed or not."""
mod = QtGui.QGuiApplication.keyboardModifiers() == DELETE_MODIFIER_KEY
if mod:
self.setPen(QtGui.QPen(self.removalColor, self.thickness))
else:
self.setPen(QtGui.QPen(self.lineColor, self.thickness))

self.setBrush(QtCore.Qt.NoBrush)
# self.setBrush(QtCore.Qt.NoBrush)
self.setZValue(-1)
super(Edge, self).paint(painter, option, widget)

Expand Down
7 changes: 3 additions & 4 deletions qtnodes/header.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""Node header."""

from PySide import QtGui
from PySide import QtCore
from .qtchooser import QtCore, QtGui, QtWidgets

from .helpers import getTextSize


class Header(QtGui.QGraphicsItem):
class Header(QtWidgets.QGraphicsItem):
"""A Header is a child of a Node and gives it a title.

Its width resizes automatically to match the Node's width.
Expand All @@ -21,7 +20,7 @@ def __init__(self, node, text, **kwargs):

def boundingRect(self):
nodebox = self.node.boundingRect()
rect = QtCore.QRect(self.x(),
rect = QtCore.QRectF(self.x(),
self.y(),
nodebox.width(),
self.h)
Expand Down
8 changes: 3 additions & 5 deletions qtnodes/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import json

from PySide import QtGui
from PySide import QtCore

from .qtchooser import QtCore, QtGui, QtWidgets

def readFileContent(filePath):
"""Return the content of the file."""
Expand All @@ -14,12 +12,12 @@ def readFileContent(filePath):

def toJson(serialized):
"""Return JSON string from given native Python datatypes."""
return json.dumps(serialized, encoding="utf-8", indent=4)
return json.dumps(serialized, indent=4)


def fromJson(jsonString):
"""Return native Python datatypes from JSON string."""
return json.loads(jsonString, encoding="utf-8")
return json.loads(jsonString)


def getTextSize(text, painter=None):
Expand Down
72 changes: 36 additions & 36 deletions qtnodes/knob.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Knob classes."""

from PySide import QtGui
from PySide import QtCore
from .qtchooser import QtCore, QtGui, QtWidgets

from .helpers import getTextSize
from .exceptions import KnobConnectionError, UnknownFlowError
Expand All @@ -13,10 +12,10 @@
FLOW_RIGHT_TO_LEFT = "flow_right_to_left"


class Knob(QtGui.QGraphicsItem):
class Knob(QtWidgets.QGraphicsItem):
"""A Knob is a socket of a Node and can be connected to other Knobs."""

def __init__(self, **kwargs):
def __init__(self, name = None, **kwargs):
super(Knob, self).__init__(**kwargs)
self.x = 0
self.y = 0
Expand Down Expand Up @@ -50,7 +49,7 @@ def connectTo(self, knob):
"""Convenience method to connect this to another Knob.

This creates an Edge and directly connects it, in contrast to the mouse
events that first create an Edge temporarily and only connect if the
events that first create an Edge temporarily and only connect if the
user releases on a valid target Knob.
"""
if knob is self:
Expand All @@ -70,8 +69,8 @@ def connectTo(self, knob):
def addEdge(self, edge):
"""Add the given Edge to the internal tracking list.

This is only one part of the Knob connection procedure. It enables us to
later traverse the whole graph and to see how many connections there
This is only one part of the Knob connection procedure. It enables us to
later traverse the whole graph and to see how many connections there
currently are.

Also make sure it is added to the QGraphicsScene, if not yet done.
Expand All @@ -93,15 +92,15 @@ def removeEdge(self, edge):

def boundingRect(self):
"""Return the bounding box of this Knob."""
rect = QtCore.QRect(self.x,
rect = QtCore.QRectF(self.x,
self.y,
self.w,
self.h)
return rect

def highlight(self, toggle):
"""Toggle the highlight color on/off.

Store the old color in a new attribute, so it can be restored.
"""
if toggle:
Expand Down Expand Up @@ -175,39 +174,40 @@ def mouseReleaseEvent(self, event):

node = self.parentItem()
scene = node.scene()
target = scene.itemAt(event.scenePos())
targets = scene.items(event.scenePos())

try:
if self.newEdge and target:

if self.newEdge.source is target:
raise KnobConnectionError(
"Can't connect a Knob to itself.")
if self.newEdge and targets:

if isinstance(target, Knob):

if type(self) == type(target):
for target in targets:
if self.newEdge.source is target:
raise KnobConnectionError(
"Can't connect Knobs of same type.")
"Can't connect a Knob to itself.")

if isinstance(target, Knob):

newConn = set([self, target])
for edge in self.edges:
existingConn = set([edge.source, edge.target])
diff = existingConn.difference(newConn)
if not diff:
if type(self) == type(target):
raise KnobConnectionError(
"Connection already exists.")
return

self.checkMaxConnections(target)

print("finish edge")
target.addEdge(self.newEdge)
self.newEdge.target = target
self.newEdge.updatePath()
self.finalizeEdge(self.newEdge)
self.newEdge = None
return
"Can't connect Knobs of same type.")

newConn = set([self, target])
for edge in self.edges:
existingConn = set([edge.source, edge.target])
diff = existingConn.difference(newConn)
if not diff:
raise KnobConnectionError(
"Connection already exists.")
return

self.checkMaxConnections(target)

print("finish edge")
target.addEdge(self.newEdge)
self.newEdge.target = target
self.newEdge.updatePath()
self.finalizeEdge(self.newEdge)
self.newEdge = None
return

raise KnobConnectionError(
"Edge creation cancelled by user.")
Expand Down
18 changes: 10 additions & 8 deletions qtnodes/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@

import uuid

from PySide import QtGui
from PySide import QtCore
from .qtchooser import QtCore, QtGui, QtWidgets

from .helpers import getTextSize
from .knob import Knob, InputKnob, OutputKnob
from .exceptions import DuplicateKnobNameError


class Node(QtGui.QGraphicsItem):
class Node(QtWidgets.QGraphicsItem):
"""A Node is a container for a header and 0-n Knobs.

It can be created, removed and modified by the user in the UI.
"""
def __init__(self, **kwargs):
def __init__(self, scene = None, **kwargs):
super(Node, self).__init__(**kwargs)

if (scene):
scene.addItem(self)

# This unique id is useful for serialization/reconstruction.
self.uuid = str(uuid.uuid4())

Expand All @@ -34,8 +36,8 @@ def __init__(self, **kwargs):
self.fillColor = QtGui.QColor(220, 220, 220)

# General configuration.
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)

self.setCursor(QtCore.Qt.SizeAllCursor)

Expand Down Expand Up @@ -73,7 +75,7 @@ def boundingRect(self):
active when hovering its Header, as otherwise there would be conflicts
with the hover events for the Node's Knobs.
"""
rect = QtCore.QRect(self.x,
rect = QtCore.QRectF(self.x,
self.y,
self.w,
self.header.h)
Expand Down Expand Up @@ -112,7 +114,7 @@ def addHeader(self, header):
def addKnob(self, knob):
"""Add the given Knob to this Node.

A Knob must have a unique name, meaning there can be no duplicates within
A Knob must have a unique name, meaning there can be no duplicates within
a Node (the displayNames are not constrained though).

Assign ourselves as the Knob's parent item (which also will put it onto
Expand Down
16 changes: 16 additions & 0 deletions qtnodes/qtchooser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import sys

try:
import PySide2
from PySide2 import QtGui, QtWidgets, QtCore
from PySide2.QtCore import Signal, Slot
print('Using PySide2 bindings')
except:

try:
import PyQt5
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
print('Using PyQt5 bindings')
except:
raise Exception('No Qt bindings found! Install PySide2 or PyQt5')
19 changes: 9 additions & 10 deletions qtnodes/view.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Custom QGraphicsView."""

from PySide import QtGui
from PySide import QtCore
from .qtchooser import QtCore, QtGui, QtWidgets

from .node import Node
from .edge import Edge
Expand All @@ -11,7 +10,7 @@
ALTERNATE_MODE_KEY = QtCore.Qt.Key.Key_Alt


class GridView(QtGui.QGraphicsView):
class GridView(QtWidgets.QGraphicsView):
"""This view will draw a grid in its background."""

def __init__(self, *args, **kwargs):
Expand All @@ -31,8 +30,8 @@ def __init__(self, *args, **kwargs):
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

self.setDragMode(QtGui.QGraphicsView.RubberBandDrag)
self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)

def nodes(self):
"""Return all Nodes in the scene."""
Expand Down Expand Up @@ -62,12 +61,12 @@ def keyReleaseEvent(self, event):
def mousePressEvent(self, event):
"""Initiate custom panning using middle mouse button."""
if event.button() == QtCore.Qt.MiddleButton:
self.setDragMode(QtGui.QGraphicsView.NoDrag)
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self.panning = True
self.prevPos = event.pos()
self.setCursor(QtCore.Qt.SizeAllCursor)
elif event.button() == QtCore.Qt.LeftButton:
self.setDragMode(QtGui.QGraphicsView.RubberBandDrag)
self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
super(GridView, self).mousePressEvent(event)

def mouseMoveEvent(self, event):
Expand All @@ -89,7 +88,7 @@ def mouseReleaseEvent(self, event):
super(GridView, self).mouseReleaseEvent(event)

def wheelEvent(self, event):
positive = event.delta() >= 0
positive = event.angleDelta().y() >= 0
zoom = self.zoomStep if positive else 1.0 / self.zoomStep
self.scale(zoom, zoom)

Expand All @@ -111,13 +110,13 @@ def drawBackground(self, painter, rect):

currentXPos = left
while currentXPos <= right:
line = QtCore.QLine(currentXPos, top, currentXPos, bottom)
line = QtCore.QLineF(currentXPos, top, currentXPos, bottom)
lines.append(line)
currentXPos += self.xStep

currentYPos = top
while currentYPos <= bottom:
line = QtCore.QLine(left, currentYPos, right, currentYPos)
line = QtCore.QLineF(left, currentYPos, right, currentYPos)
lines.append(line)
currentYPos += self.yStep

Expand Down
Loading