From eeea4dd931b022908f7bf4071e76254566f4d041 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Mon, 20 May 2019 21:41:04 +1200 Subject: [PATCH 1/4] refactored property bin signals. --- NodeGraphQt/__init__.py | 9 +++-- NodeGraphQt/base/commands.py | 24 +++++--------- NodeGraphQt/base/graph.py | 36 +++++++------------- NodeGraphQt/base/node.py | 2 ++ NodeGraphQt/widgets/properties.py | 2 +- NodeGraphQt/widgets/properties_bin.py | 48 +++++++++++++++++++++++++-- docs/widgets.rst | 6 ++-- example.py | 4 +-- 8 files changed, 79 insertions(+), 52 deletions(-) diff --git a/NodeGraphQt/__init__.py b/NodeGraphQt/__init__.py index 541e7292..23ea8a9b 100644 --- a/NodeGraphQt/__init__.py +++ b/NodeGraphQt/__init__.py @@ -41,11 +41,11 @@ __url__ = 'https://github.com/jchanvfx/NodeGraphQt' __all__ = [ - 'NodeGraph', 'NodeObject', 'BaseNode', 'BackdropNode', 'Port', - 'Menu', 'MenuCommand', 'setup_context_menu' + 'BackdropNode', 'BaseNode', 'Menu', 'MenuCommand', 'NodeGraph', + 'NodeObject', 'Port', 'PropertiesBinWidget', 'constants', + 'setup_context_menu' ] - try: from Qt import QtWidgets, QtGui, QtCore, QtCompat except ImportError as ie: @@ -61,3 +61,6 @@ # functions from .base.actions import setup_context_menu + +# widgets +from .widgets.properties_bin import PropertiesBinWidget diff --git a/NodeGraphQt/base/commands.py b/NodeGraphQt/base/commands.py index aadb7b95..7b311009 100644 --- a/NodeGraphQt/base/commands.py +++ b/NodeGraphQt/base/commands.py @@ -50,29 +50,21 @@ def set_node_prop(self, name, value): name = 'xy_pos' setattr(view, name, value) - def update_prop_bin(self, name, value): - """ - updates the property bin widget. - """ - graph = self.node.graph - prop_bin = graph.properties_bin() - properties_wgt = prop_bin.prop_widget(self.node) - if properties_wgt: - prop_wgt = properties_wgt.get_widget(name) - # check if previous value is identical to current value, - # prevent signals from causing a infinite loop. - if prop_wgt and prop_wgt.get_value() != value: - prop_wgt.set_value(value) - def undo(self): if self.old_val != self.new_val: self.set_node_prop(self.name, self.old_val) - self.update_prop_bin(self.name, self.old_val) + + # emit property changed signal. + graph = self.node.graph + graph.property_changed.emit(self.node, self.name, self.old_val) def redo(self): if self.old_val != self.new_val: self.set_node_prop(self.name, self.new_val) - self.update_prop_bin(self.name, self.new_val) + + # emit property changed signal. + graph = self.node.graph + graph.property_changed.emit(self.node, self.name, self.new_val) class NodeMovedCmd(QtWidgets.QUndoCommand): diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index be611bfa..9930de45 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -16,16 +16,12 @@ from NodeGraphQt.base.port import Port from NodeGraphQt.constants import DRAG_DROP_ID from NodeGraphQt.widgets.node_tree import NodeTreeWidget -from NodeGraphQt.widgets.properties_bin import PropertiesBinWidget from NodeGraphQt.widgets.viewer import NodeViewer class NodeGraph(QtCore.QObject): """ base node graph controller. - - Args: - tab_search_key(str): hotkey for the tab search widget (default: "tab"). """ #: signal for when a node has been created in the node graph. @@ -49,7 +45,6 @@ def __init__(self, parent=None): self._node_factory = NodeFactory() self._undo_stack = QtWidgets.QUndoStack(self) - self._properties_bin = None self._nodes_tree = None tab = QtWidgets.QAction('Search Nodes', self) @@ -80,9 +75,9 @@ def _toggle_tab_search(self): self._viewer.tab_search_set_nodes(self._node_factory.names) self._viewer.tab_search_toggle() - def _on_property_changed(self, node_id, prop_name, prop_value): + def _on_property_bin_changed(self, node_id, prop_name, prop_value): """ - called when a property widget has changed in the properties bin. + called when a property widget has changed in a properties bin. (emits the node object, property name, property value) Args: @@ -91,8 +86,10 @@ def _on_property_changed(self, node_id, prop_name, prop_value): prop_value (object): python object. """ node = self.get_node_by_id(node_id) - node.set_property(prop_name, prop_value) - self.property_changed.emit(node, prop_name, prop_value) + + # prevent signals from causing a infinite loop. + if node.get_property(prop_name) != prop_value: + node.set_property(prop_name, prop_value) def _on_node_double_clicked(self, node_id): """ @@ -103,9 +100,6 @@ 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) - if self._properties_bin: - self._properties_bin.add_node(node) - self.node_double_clicked.emit(node) def _on_node_selected(self, node_id): @@ -237,20 +231,14 @@ def scene(self): """ return self._viewer.scene() - def properties_bin(self): + def add_properties_bin(self, prop_bin): """ - Initializes the node properties bin widget when first called. + Wire up a properties bin widget to the node graph. - Returns: - 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 + Args: + prop_bin (NodeGraphQt.PropertiesBinWidget): properties widget. + """ + prop_bin.property_changed.connect(self._on_property_bin_changed) def nodes_tree(self): """ diff --git a/NodeGraphQt/base/node.py b/NodeGraphQt/base/node.py index 682f3a84..58949cbd 100644 --- a/NodeGraphQt/base/node.py +++ b/NodeGraphQt/base/node.py @@ -264,6 +264,8 @@ def set_property(self, name, value): name (str): name of the property. value (object): property data. """ + + # prevent signals from causing a infinite loop. if self.get_property(name) == value: return diff --git a/NodeGraphQt/widgets/properties.py b/NodeGraphQt/widgets/properties.py index d5aa3366..121d8982 100644 --- a/NodeGraphQt/widgets/properties.py +++ b/NodeGraphQt/widgets/properties.py @@ -330,7 +330,7 @@ class NodePropWidget(QtWidgets.QWidget): Node properties widget for display a Node object. Args: - parent: + parent (QtWidgets.QWidget): parent object. node (NodeGraphQt.BaseNode): node. """ diff --git a/NodeGraphQt/widgets/properties_bin.py b/NodeGraphQt/widgets/properties_bin.py index 034d6e99..7b49ef03 100644 --- a/NodeGraphQt/widgets/properties_bin.py +++ b/NodeGraphQt/widgets/properties_bin.py @@ -52,23 +52,29 @@ def __init__(self, parent=None): class PropertiesBinWidget(QtWidgets.QWidget): """ Node properties bin for displaying properties. + + Args: + parent (QtWidgets.QWidget): parent object. + node_graph (NodeGraphQt.NodeGraph): node graph. """ #: Signal emitted (node_id, prop_name, prop_value) property_changed = QtCore.Signal(str, str, object) - def __init__(self, parent=None): + def __init__(self, parent=None, node_graph=None): super(PropertiesBinWidget, self).__init__(parent) self.setWindowTitle('Properties Bin') self._prop_list = PropertiesList() self._limit = QtWidgets.QSpinBox() - self._limit.setToolTip('Set node limit to display.') + self._limit.setToolTip('Set display nodes limit.') self._limit.setMaximum(10) self._limit.setMinimum(0) self._limit.setValue(5) self._limit.valueChanged.connect(self.__on_limit_changed) self.resize(400, 400) + self._block_signal = False + btn_clr = QtWidgets.QPushButton('clear') btn_clr.setToolTip('Clear the properties bin.') btn_clr.clicked.connect(self.clear_bin) @@ -82,6 +88,11 @@ def __init__(self, parent=None): layout.addLayout(top_layout) layout.addWidget(self._prop_list, 1) + # wire up node graph. + node_graph.add_properties_bin(self) + node_graph.node_double_clicked.connect(self.add_node) + node_graph.property_changed.connect(self.__on_graph_property_changed) + def __repr__(self): return '<{} object at {}>'.format(self.__class__.__name__, hex(id(self))) @@ -94,6 +105,37 @@ def __on_limit_changed(self, value): if rows > value: self._prop_list.removeRow(rows - 1) + def __on_graph_property_changed(self, node, prop_name, prop_value): + """ + Slot function that updates the property bin from the node graph signal. + + Args: + node (NodeGraphQt.NodeObject): + prop_name (str): + prop_value (object): + """ + properties_widget = self.prop_widget(node) + if not properties_widget: + return + + property_window = properties_widget.get_widget(prop_name) + if prop_value != property_window.get_value(): + self._block_signal = True + property_window.set_value(prop_value) + self._block_signal = False + + def __on_property_widget_changed(self, node_id, prop_name, prop_value): + """ + Slot function triggered when a property widget value has changed. + + Args: + node_id (str): + prop_name (str): + prop_value (object): + """ + if not self._block_signal: + self.property_changed.emit(node_id, prop_name, prop_value) + def limit(self): """ Returns the limit for how many nodes can be loaded into the bin. @@ -132,7 +174,7 @@ def add_node(self, node): self._prop_list.insertRow(0) prop_widget = NodePropWidget(node=node) - prop_widget.property_changed.connect(self.property_changed.emit) + prop_widget.property_changed.connect(self.__on_property_widget_changed) prop_widget.property_closed.connect(self.__on_prop_close) self._prop_list.setCellWidget(0, 0, prop_widget) diff --git a/docs/widgets.rst b/docs/widgets.rst index 05e9ef1f..38f257f9 100644 --- a/docs/widgets.rst +++ b/docs/widgets.rst @@ -11,17 +11,17 @@ PropertiesBinWidget .. code-block:: python :linenos: - from NodeGraphQt import NodeGraph + from NodeGraphQt import NodeGraph, PropertiesBinWidget graph = NodeGraph() # create properties bin widget. - properties_bin = graph.properties_bin() + properties_bin = PropertiesBinWidget(node_graph=graph) properties_bin.show() ---- -.. autoclass:: NodeGraphQt.widgets.properties_bin.PropertiesBinWidget +.. autoclass:: NodeGraphQt.PropertiesBinWidget :members: :exclude-members: property_changed diff --git a/example.py b/example.py index abe0c20c..5b6ffa07 100644 --- a/example.py +++ b/example.py @@ -7,7 +7,7 @@ BaseNode, BackdropNode, setup_context_menu) -from NodeGraphQt import QtWidgets, QtCore +from NodeGraphQt import QtWidgets, QtCore, PropertiesBinWidget # import example nodes from the "example_nodes" package from example_nodes import basic_nodes, widget_nodes @@ -49,7 +49,7 @@ def __init__(self): # show the properties bin when a node is "double clicked" in the graph. - properties_bin = graph.properties_bin() + properties_bin = PropertiesBinWidget(node_graph=graph) properties_bin.setWindowFlags(QtCore.Qt.Tool) def show_prop_bin(node): if not properties_bin.isVisible(): From 6df6dc27cf2cada01c480f6b666ee1f6c7936efd Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Tue, 21 May 2019 22:00:37 +1200 Subject: [PATCH 2/4] node tree widget clean up and doc strings. --- NodeGraphQt/__init__.py | 5 +- NodeGraphQt/base/graph.py | 68 ++++++++++++++++++++------- NodeGraphQt/base/node.py | 16 +++++-- NodeGraphQt/base/port.py | 4 +- NodeGraphQt/constants.py | 1 - NodeGraphQt/widgets/node_tree.py | 14 ++++-- NodeGraphQt/widgets/properties_bin.py | 2 +- docs/classes.rst | 3 +- docs/index.rst | 3 -- docs/widgets.rst | 6 ++- example.py | 4 +- 11 files changed, 87 insertions(+), 39 deletions(-) diff --git a/NodeGraphQt/__init__.py b/NodeGraphQt/__init__.py index 23ea8a9b..f0a08ec3 100644 --- a/NodeGraphQt/__init__.py +++ b/NodeGraphQt/__init__.py @@ -42,8 +42,8 @@ __all__ = [ 'BackdropNode', 'BaseNode', 'Menu', 'MenuCommand', 'NodeGraph', - 'NodeObject', 'Port', 'PropertiesBinWidget', 'constants', - 'setup_context_menu' + 'NodeObject', 'NodeTreeWidget', 'Port', 'PropertiesBinWidget', + 'constants', 'setup_context_menu' ] try: @@ -63,4 +63,5 @@ from .base.actions import setup_context_menu # widgets +from .widgets.node_tree import NodeTreeWidget from .widgets.properties_bin import PropertiesBinWidget diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index 9930de45..0a5bfcb1 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -15,7 +15,6 @@ from NodeGraphQt.base.node import NodeObject from NodeGraphQt.base.port import Port from NodeGraphQt.constants import DRAG_DROP_ID -from NodeGraphQt.widgets.node_tree import NodeTreeWidget from NodeGraphQt.widgets.viewer import NodeViewer @@ -45,8 +44,6 @@ def __init__(self, parent=None): self._node_factory = NodeFactory() self._undo_stack = QtWidgets.QUndoStack(self) - self._nodes_tree = None - tab = QtWidgets.QAction('Search Nodes', self) tab.setShortcut('tab') tab.triggered.connect(self._toggle_tab_search) @@ -231,26 +228,63 @@ def scene(self): """ return self._viewer.scene() - def add_properties_bin(self, prop_bin): + def background_color(self): """ - Wire up a properties bin widget to the node graph. + Return the node graph background color. + + Returns: + tuple: r, g ,b + """ + return self.scene().background_color + + def set_background_color(self, r, g, b): + """ + Set node graph background color. Args: - prop_bin (NodeGraphQt.PropertiesBinWidget): properties widget. + r (int): red value. + g (int): green value. + b (int): blue value. """ - prop_bin.property_changed.connect(self._on_property_bin_changed) + self.scene().background_color = (r, g, b) - def nodes_tree(self): + def grid_color(self): """ - Initializes the nodes list widget when first called. + Return the node graph grid color. Returns: - NodeTreeWidget: the initialized widget. + tuple: r, g ,b + """ + return self.scene().grid_color + + def set_grid_color(self, r, g, b): + """ + Set node graph grid color. + + Args: + r (int): red value. + g (int): green value. + b (int): blue value. """ - if self._nodes_tree is None: - self._nodes_tree = NodeTreeWidget() - self._nodes_tree.set_node_factory(self._node_factory) - return self._nodes_tree + self.scene().grid_color = (r, g, b) + + def display_grid(self, display=True): + """ + Display node graph background grid. + + Args: + display: False to not draw the background grid. + """ + self.scene().grid = display + + def add_properties_bin(self, prop_bin): + """ + Wire up a properties bin widget to the node graph. + + Args: + prop_bin (NodeGraphQt.PropertiesBinWidget): properties widget. + """ + prop_bin.property_changed.connect(self._on_property_bin_changed) def undo_stack(self): """ @@ -303,12 +337,12 @@ def acyclic(self): """ return self._model.acyclic - def set_acyclic(self, mode=True): + def set_acyclic(self, mode=False): """ - Set the node graph to be acyclic or not. (default=True) + Enable the node graph to be a acyclic graph. (default=False) Args: - mode (bool): false to disable acyclic. + mode (bool): true to enable acyclic. """ self._model.acyclic = mode self._viewer.acyclic = mode diff --git a/NodeGraphQt/base/node.py b/NodeGraphQt/base/node.py index 58949cbd..913205dd 100644 --- a/NodeGraphQt/base/node.py +++ b/NodeGraphQt/base/node.py @@ -91,16 +91,16 @@ def view(self): Returns the :class:`QtWidgets.QGraphicsItem` used in the scene. Returns: - AbstractNodeItem: node item. + NodeGraphQt.qgraphics.node_abstract.AbstractNodeItem: node item. """ return self._view def set_view(self, item): """ - Sets the graphic item to use for the scene. + Sets the qgraphics item to use for the scene. Args: - item (AbstractNodeItem): node view item. + item (NodeGraphQt.qgraphics.node_abstract.AbstractNodeItem): node item. """ self._view = item self._view.id = self.model.id @@ -109,14 +109,20 @@ def set_view(self, item): @property def model(self): """ - Returns the node model. + Return the node model. Returns: - NodeModel: node model object. + NodeGraphQt.base.model.NodeModel: node model object. """ return self._model def set_model(self, model): + """ + Set the node model. + + Args: + model (NodeGraphQt.base.model.NodeModel): node model object. + """ self._model = model self._model.type_ = self.type_ self._model.id = self.view.id diff --git a/NodeGraphQt/base/port.py b/NodeGraphQt/base/port.py index e5b49c51..2af0b0af 100644 --- a/NodeGraphQt/base/port.py +++ b/NodeGraphQt/base/port.py @@ -28,7 +28,7 @@ def view(self): returns the :class:`QtWidgets.QGraphicsItem` used in the scene. Returns: - PortItem: port item. + NodeGraphQt.qgraphics.port.PortItem: port item. """ return self.__view @@ -38,7 +38,7 @@ def model(self): returns the port model. Returns: - PortModel: port model. + NodeGraphQt.base.model.PortModel: port model. """ return self.__model diff --git a/NodeGraphQt/constants.py b/NodeGraphQt/constants.py index 88d4bb33..3a5b5f07 100644 --- a/NodeGraphQt/constants.py +++ b/NodeGraphQt/constants.py @@ -60,7 +60,6 @@ # === NODE VIEWER === -#: Node graph background color. VIEWER_BG_COLOR = (35, 35, 35) VIEWER_GRID_COLOR = (45, 45, 45) VIEWER_GRID_OVERLAY = True diff --git a/NodeGraphQt/widgets/node_tree.py b/NodeGraphQt/widgets/node_tree.py index a3d5a84c..6c052ed2 100644 --- a/NodeGraphQt/widgets/node_tree.py +++ b/NodeGraphQt/widgets/node_tree.py @@ -15,13 +15,21 @@ def __eq__(self, other): class NodeTreeWidget(QtWidgets.QTreeWidget): + """ + Node tree for displaying node types. - def __init__(self, parent=None): + Args: + parent (QtWidgets.QWidget): parent of the new widget. + node_graph (NodeGraphQt.NodeGraph): node graph. + """ + + def __init__(self, parent=None, node_graph=None): super(NodeTreeWidget, self).__init__(parent) self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly) self.setHeaderHidden(True) self._factory = None self._custom_labels = {} + self._set_node_factory(node_graph._node_factory) def __repr__(self): return '<{} object at {}>'.format(self.__class__.__name__, hex(id(self))) @@ -66,7 +74,7 @@ def _build_tree(self): category_item.addChild(item) - def set_node_factory(self, factory): + def _set_node_factory(self, factory): """ Set current node factory. @@ -87,6 +95,6 @@ def set_category_label(self, category, label): def update(self): """ - Update and refresh the node list widget. + Update and refresh the node tree widget. """ self._build_tree() diff --git a/NodeGraphQt/widgets/properties_bin.py b/NodeGraphQt/widgets/properties_bin.py index 7b49ef03..276b66c3 100644 --- a/NodeGraphQt/widgets/properties_bin.py +++ b/NodeGraphQt/widgets/properties_bin.py @@ -54,7 +54,7 @@ class PropertiesBinWidget(QtWidgets.QWidget): Node properties bin for displaying properties. Args: - parent (QtWidgets.QWidget): parent object. + parent (QtWidgets.QWidget): parent of the new widget. node_graph (NodeGraphQt.NodeGraph): node graph. """ diff --git a/docs/classes.rst b/docs/classes.rst index 60c72d32..f2adc905 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -10,4 +10,5 @@ Classes graph nodes - connections \ No newline at end of file + connections + menu \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 6bf83820..1e54e6ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,6 +17,3 @@ Contents constants classes widgets - menu - - diff --git a/docs/widgets.rst b/docs/widgets.rst index 38f257f9..bc2c1d26 100644 --- a/docs/widgets.rst +++ b/docs/widgets.rst @@ -13,6 +13,7 @@ PropertiesBinWidget from NodeGraphQt import NodeGraph, PropertiesBinWidget + # create node graph. graph = NodeGraph() # create properties bin widget. @@ -34,12 +35,13 @@ NodeTreeWidget .. code-block:: python :linenos: - from NodeGraphQt import NodeGraph + from NodeGraphQt import NodeGraph, NodeTreeWidget + # create node graph. graph = NodeGraph() # create node tree widget. - nodes_tree = graph.nodes_tree() + nodes_tree = NodeTreeWidget(node_graph=graph) nodes_tree.show() ---- diff --git a/example.py b/example.py index 5b6ffa07..302c643a 100644 --- a/example.py +++ b/example.py @@ -7,7 +7,7 @@ BaseNode, BackdropNode, setup_context_menu) -from NodeGraphQt import QtWidgets, QtCore, PropertiesBinWidget +from NodeGraphQt import QtWidgets, QtCore, PropertiesBinWidget, NodeTreeWidget # import example nodes from the "example_nodes" package from example_nodes import basic_nodes, widget_nodes @@ -58,7 +58,7 @@ def show_prop_bin(node): # show the nodes list when a node is "double clicked" in the graph. - node_tree = graph.nodes_tree() + node_tree = NodeTreeWidget(node_graph=graph) def show_nodes_list(node): if not node_tree.isVisible(): node_tree.update() From ca586d069ab579f39a8820c9be0ab3b49fd501bf Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Tue, 21 May 2019 22:02:31 +1200 Subject: [PATCH 3/4] version bump --- NodeGraphQt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NodeGraphQt/__init__.py b/NodeGraphQt/__init__.py index f0a08ec3..0a545b39 100644 --- a/NodeGraphQt/__init__.py +++ b/NodeGraphQt/__init__.py @@ -30,7 +30,7 @@ # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = '0.0.3' +__version__ = '0.0.4' __status__ = 'Work in Progress' __license__ = 'MIT' From e29b428635e0b9383e081ee3fda43eafef297490 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Tue, 21 May 2019 22:03:29 +1200 Subject: [PATCH 4/4] reverted version bump --- NodeGraphQt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NodeGraphQt/__init__.py b/NodeGraphQt/__init__.py index 0a545b39..f0a08ec3 100644 --- a/NodeGraphQt/__init__.py +++ b/NodeGraphQt/__init__.py @@ -30,7 +30,7 @@ # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = '0.0.4' +__version__ = '0.0.3' __status__ = 'Work in Progress' __license__ = 'MIT'