From 86fee520c6b86acd5162b8693b546b8ff7996d67 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Mon, 29 Apr 2019 23:30:50 +1200 Subject: [PATCH 1/4] port connection type indicators. --- NodeGraphQt/qgraphics/port.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/NodeGraphQt/qgraphics/port.py b/NodeGraphQt/qgraphics/port.py index d5d7e741..bb9e5002 100644 --- a/NodeGraphQt/qgraphics/port.py +++ b/NodeGraphQt/qgraphics/port.py @@ -55,11 +55,8 @@ def paint(self, painter, option, widget): painter.save() rect = QtCore.QRectF(0.0, 0.8, self._width, self._height) - painter.setBrush(QtGui.QColor(0, 0, 0, 200)) - painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0, 255), 1.8)) path = QtGui.QPainterPath() path.addEllipse(rect) - painter.drawPath(path) if self._hovered: color = QtGui.QColor(*PORT_HOVER_COLOR) @@ -76,6 +73,28 @@ def paint(self, painter, option, widget): painter.setPen(pen) painter.drawEllipse(self.boundingRect()) + if self.connected_pipes and not self._hovered: + painter.setBrush(border_color) + w = self._width / 2.5 + h = self._height / 2.5 + rect = QtCore.QRectF(self.boundingRect().center().x() - w / 2, + self.boundingRect().center().y() - h / 2, + w, h) + painter.drawEllipse(rect) + elif self._hovered: + if self.multi_connection: + painter.setBrush(color) + w = self._width / 1.8 + h = self._height / 1.8 + else: + painter.setBrush(border_color) + w = self._width / 3.5 + h = self._height / 3.5 + rect = QtCore.QRectF(self.boundingRect().center().x() - w / 2, + self.boundingRect().center().y() - h / 2, + w, h) + painter.drawEllipse(rect) + painter.restore() def itemChange(self, change, value): From 3da5b5c61fd11333fbcea345a91081211e04d2ea Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Sat, 4 May 2019 18:31:30 +1200 Subject: [PATCH 2/4] wip implemented node list widget. --- NodeGraphQt/base/graph.py | 36 +++++++++++---- NodeGraphQt/widgets/node_list.py | 75 ++++++++++++++++++++++++++++++++ example.py | 13 +++++- 3 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 NodeGraphQt/widgets/node_list.py diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index 9210da31..4c43122d 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -13,6 +13,7 @@ from NodeGraphQt.base.model import NodeGraphModel from NodeGraphQt.base.node import NodeObject from NodeGraphQt.base.port import Port +from NodeGraphQt.widgets.node_list import NodeListWidget from NodeGraphQt.widgets.properties_bin import PropertiesBinWidget from NodeGraphQt.widgets.viewer import NodeViewer @@ -45,7 +46,9 @@ def __init__(self, parent=None, tab_search_key='tab'): self._viewer = NodeViewer(parent) self._node_factory = NodeFactory() self._undo_stack = QtWidgets.QUndoStack(self) - self._properties_bin = PropertiesBinWidget() + + self._properties_bin = None + self._nodes_list = None tab = QtWidgets.QAction('Search Nodes', self) tab.setShortcut(tab_search_key) @@ -65,10 +68,6 @@ def _wire_signals(self): self._viewer.node_selected.connect(self._on_node_selected) self._viewer.data_dropped.connect(self._on_node_data_dropped) - # wire up properties bin widget. - self._properties_bin.property_changed.connect( - self._on_property_changed) - def _toggle_tab_search(self): """ toggle the tab search widget. @@ -99,7 +98,8 @@ def _on_node_double_clicked(self, node_id): node_id (str): node id emitted by the viewer. """ node = self.get_node_by_id(node_id) - self._properties_bin.add_node(node) + if self._properties_bin: + self._properties_bin.add_node(node) self.node_double_clicked.emit(node) @@ -222,13 +222,31 @@ def scene(self): def properties_bin(self): """ - Return the node properties bin widget. + Initializes the node properties bin widget when first called. Returns: - PropBinWidget: widget. - """ + PropBinWidget: the initialized widget. + """ + if self._properties_bin is None: + self._properties_bin = PropertiesBinWidget() + # wire up widget. + self._properties_bin.property_changed.connect( + self._on_property_changed + ) return self._properties_bin + def nodes_list(self): + """ + Initializes the nodes list widget when first called. + + Returns: + NodeListWidget: the initialized widget. + """ + if self._nodes_list is None: + self._nodes_list = NodeListWidget() + self._nodes_list.set_node_factory(self._node_factory) + return self._nodes_list + def undo_stack(self): """ Returns the undo stack used in the node graph diff --git a/NodeGraphQt/widgets/node_list.py b/NodeGraphQt/widgets/node_list.py new file mode 100644 index 00000000..9550d436 --- /dev/null +++ b/NodeGraphQt/widgets/node_list.py @@ -0,0 +1,75 @@ +from NodeGraphQt import QtWidgets, QtCore + + +class BaseListWidgetItem(QtWidgets.QListWidgetItem): + + def __eq__(self, other): + return id(self) == id(other) + + +class NodeListWidget(QtWidgets.QTreeWidget): + + def __init__(self, parent=None): + super(NodeListWidget, self).__init__(parent) + self.setHeaderHidden(True) + self._factory = None + self._custom_labels = {} + + def _build_tree(self): + """ + Populate the node tree. + """ + self.clear() + categories = set() + node_types = {} + for name, node_ids in self._factory.names.items(): + for nid in node_ids: + categories.add('.'.join(nid.split('.')[:-1])) + node_types[nid] = name + + category_items = {} + for category in sorted(categories): + if category in self._custom_labels.keys(): + label = self._custom_labels[category] + else: + label = '- {}'.format(category) + item = QtWidgets.QTreeWidgetItem(self, [label]) + item.setFirstColumnSpanned(True) + item.setFlags(QtCore.Qt.ItemIsEnabled) + self.addTopLevelItem(item) + item.setExpanded(True) + category_items[category] = item + + for node_id, node_name in node_types.items(): + category = '.'.join(node_id.split('.')[:-1]) + category_item = category_items[category] + + item = QtWidgets.QTreeWidgetItem(category_item, [node_name]) + item.setToolTip(0, node_id) + + category_item.addChild(item) + + def set_node_factory(self, factory): + """ + Set current node factory. + + Args: + factory (NodeFactory): node factory. + """ + self._factory = factory + + def set_category_label(self, category, label): + """ + Set custom display label for a node category. + + Args: + category (str): node identifier category eg. "nodeGraphQt.nodes" + label (str): custom display label. + """ + self._custom_labels[category] = label + + def update(self): + """ + Update and refresh the node list widget. + """ + self._build_tree() diff --git a/example.py b/example.py index 8e47ef9f..94e5478c 100644 --- a/example.py +++ b/example.py @@ -44,16 +44,25 @@ def __init__(self): viewer.resize(1100, 800) viewer.show() + # show the properties bin when a node is "double clicked" in the graph. properties_bin = graph.properties_bin() properties_bin.setWindowFlags(QtCore.Qt.Tool) - def show_prop_bin(node): if not properties_bin.isVisible(): properties_bin.show() - graph.node_double_clicked.connect(show_prop_bin) + + # show the nodes list when a node is "double clicked" in the graph. + node_list = graph.nodes_list() + def show_nodes_list(node): + if not node_list.isVisible(): + node_list.update() + node_list.show() + graph.node_double_clicked.connect(show_nodes_list) + + # registered nodes. reg_nodes = [ Backdrop, MyNode, From 7fb39285b27a21756f49e6e67b96b9603701867f Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Mon, 6 May 2019 21:53:22 +1200 Subject: [PATCH 3/4] updated requirements --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 012dbebf..d69a9e51 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ PySide2>=5.12 + +python>=3.6 \ No newline at end of file From 821e84d8cc6673bf5d990a3e923ebc7eae516e7c Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Tue, 7 May 2019 22:50:24 +1200 Subject: [PATCH 4/4] updated node list widget. --- NodeGraphQt/base/graph.py | 14 ++++++++++++++ NodeGraphQt/constants.py | 2 ++ NodeGraphQt/widgets/node_list.py | 33 +++++++++++++++++++++++++------- NodeGraphQt/widgets/viewer.py | 1 + requirements.txt | 2 +- 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index 4c43122d..d945be43 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- import json import os import re @@ -13,6 +14,7 @@ from NodeGraphQt.base.model import NodeGraphModel from NodeGraphQt.base.node import NodeObject from NodeGraphQt.base.port import Port +from NodeGraphQt.constants import DRAG_DROP_ID from NodeGraphQt.widgets.node_list import NodeListWidget from NodeGraphQt.widgets.properties_bin import PropertiesBinWidget from NodeGraphQt.widgets.viewer import NodeViewer @@ -122,6 +124,18 @@ def _on_node_data_dropped(self, data, pos): data (QtCore.QMimeData): mime data. pos (QtCore.QPoint): scene position relative to the drop. """ + + # don't emit signal for internal widget drops. + if data.hasFormat('text/plain'): + if data.text().startswith('<${}>:'.format(DRAG_DROP_ID)): + node_ids = data.text()[len('<${}>:'.format(DRAG_DROP_ID)):] + x, y = pos.x(), pos.y() + for node_id in node_ids.split(','): + self.create_node(node_id, pos=[x, y]) + x += 20 + y += 20 + return + self.data_dropped.emit(data, pos) def _on_nodes_moved(self, node_data): diff --git a/NodeGraphQt/constants.py b/NodeGraphQt/constants.py index 5664c5c0..f76d31ed 100644 --- a/NodeGraphQt/constants.py +++ b/NodeGraphQt/constants.py @@ -65,6 +65,8 @@ SCENE_AREA = 8000.0 +DRAG_DROP_ID = 'n0deGraphQT' + # === PATHS === BASE_PATH = os.path.dirname(os.path.abspath(__file__)) diff --git a/NodeGraphQt/widgets/node_list.py b/NodeGraphQt/widgets/node_list.py index 9550d436..50aad007 100644 --- a/NodeGraphQt/widgets/node_list.py +++ b/NodeGraphQt/widgets/node_list.py @@ -1,4 +1,11 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- from NodeGraphQt import QtWidgets, QtCore +from NodeGraphQt.constants import DRAG_DROP_ID + + +TYPE_NODE = QtWidgets.QTreeWidgetItem.UserType + 1 +TYPE_CATEGORY = QtWidgets.QTreeWidgetItem.UserType + 2 class BaseListWidgetItem(QtWidgets.QListWidgetItem): @@ -11,10 +18,17 @@ class NodeListWidget(QtWidgets.QTreeWidget): def __init__(self, parent=None): super(NodeListWidget, self).__init__(parent) + self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly) self.setHeaderHidden(True) self._factory = None self._custom_labels = {} + def mimeData(self, items): + node_ids = ','.join(i.toolTip(0) for i in items) + mime_data = super(NodeListWidget, self).mimeData(items) + mime_data.setText('<${}>:{}'.format(DRAG_DROP_ID, node_ids)) + return mime_data + def _build_tree(self): """ Populate the node tree. @@ -33,22 +47,27 @@ def _build_tree(self): label = self._custom_labels[category] else: label = '- {}'.format(category) - item = QtWidgets.QTreeWidgetItem(self, [label]) - item.setFirstColumnSpanned(True) - item.setFlags(QtCore.Qt.ItemIsEnabled) - self.addTopLevelItem(item) - item.setExpanded(True) - category_items[category] = item + cat_item = QtWidgets.QTreeWidgetItem( + self, [label], type=TYPE_CATEGORY + ) + cat_item.setFirstColumnSpanned(True) + cat_item.setFlags(QtCore.Qt.ItemIsEnabled) + self.addTopLevelItem(cat_item) + cat_item.setExpanded(True) + category_items[category] = cat_item for node_id, node_name in node_types.items(): category = '.'.join(node_id.split('.')[:-1]) category_item = category_items[category] - item = QtWidgets.QTreeWidgetItem(category_item, [node_name]) + item = QtWidgets.QTreeWidgetItem( + category_item, [node_name], type=TYPE_NODE + ) item.setToolTip(0, node_id) category_item.addChild(item) + def set_node_factory(self, factory): """ Set current node factory. diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index b0e00160..f4a335bb 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- import os from NodeGraphQt import QtGui, QtCore, QtWidgets diff --git a/requirements.txt b/requirements.txt index d69a9e51..86e097c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ PySide2>=5.12 - +Qt.py>=1.2.0.b2 python>=3.6 \ No newline at end of file