From 4082e885a95483bd5eafbcc626887377b36cb8a1 Mon Sep 17 00:00:00 2001 From: QianMoth Date: Mon, 20 Feb 2023 12:48:47 +0800 Subject: [PATCH 1/5] Add the widget embeddable option and implement it in the `resizable_images` example. --- examples/resizable_images/ImageShowModel.cpp | 5 ++++ examples/resizable_images/ImageShowModel.hpp | 4 +++- examples/resizable_images/main.cpp | 20 ++++++++++++++++ include/QtNodes/internal/Definitions.hpp | 23 ++++++++++--------- .../QtNodes/internal/NodeDelegateModel.hpp | 6 ++++- src/DataFlowGraphModel.cpp | 11 +++++++-- src/DefaultHorizontalNodeGeometry.cpp | 12 +++++++--- src/DefaultVerticalNodeGeometry.cpp | 12 +++++++--- src/NodeGraphicsObject.cpp | 6 ++++- 9 files changed, 77 insertions(+), 22 deletions(-) diff --git a/examples/resizable_images/ImageShowModel.cpp b/examples/resizable_images/ImageShowModel.cpp index 1d62f9f3f..5e6e7844c 100644 --- a/examples/resizable_images/ImageShowModel.cpp +++ b/examples/resizable_images/ImageShowModel.cpp @@ -24,6 +24,11 @@ ImageShowModel::ImageShowModel() _label->installEventFilter(this); } +ImageShowModel::~ImageShowModel() +{ + delete _label; +} + unsigned int ImageShowModel::nPorts(PortType portType) const { unsigned int result = 1; diff --git a/examples/resizable_images/ImageShowModel.hpp b/examples/resizable_images/ImageShowModel.hpp index 7e4c8a06c..edf27280a 100644 --- a/examples/resizable_images/ImageShowModel.hpp +++ b/examples/resizable_images/ImageShowModel.hpp @@ -23,7 +23,7 @@ class ImageShowModel : public NodeDelegateModel public: ImageShowModel(); - ~ImageShowModel() = default; + virtual ~ImageShowModel(); public: QString caption() const override { return QString("Image Display"); } @@ -41,6 +41,8 @@ class ImageShowModel : public NodeDelegateModel void setInData(std::shared_ptr nodeData, PortIndex const port) override; + bool widgetEmbeddable() const override { return false; } + QWidget *embeddedWidget() override { return _label; } bool resizable() const override { return true; } diff --git a/examples/resizable_images/main.cpp b/examples/resizable_images/main.cpp index 2e0e91e5f..d994463ed 100644 --- a/examples/resizable_images/main.cpp +++ b/examples/resizable_images/main.cpp @@ -38,6 +38,26 @@ int main(int argc, char *argv[]) GraphicsView view(&scene); + QObject::connect(&scene, + &DataFlowGraphicsScene::nodeDoubleClicked, + &dataFlowGraphModel, + [&dataFlowGraphModel](QtNodes::NodeId nodeId) { + QString name = dataFlowGraphModel + .nodeData(nodeId, QtNodes::NodeRole::Caption) + .value(); + + bool isEmbeded = dataFlowGraphModel + .nodeData(nodeId, QtNodes::NodeRole::WidgetEmbeddable) + .value(); + auto w = dataFlowGraphModel.nodeData(nodeId, QtNodes::NodeRole::Widget) + .value(); + + if (!isEmbeded && w) { + w->setWindowTitle(name + "_" + QString::number(nodeId)); + w->show(); + } + }); + view.setWindowTitle("Data Flow: Resizable Images"); view.resize(800, 600); // Center window. diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 863fa40b4..53e165aaf 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Export.hpp" @@ -22,16 +22,17 @@ Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC) * Constants used for fetching QVariant data from GraphModel. */ enum class NodeRole { - Type = 0, ///< Type of the current node, usually a string. - Position = 1, ///< `QPointF` positon of the node on the scene. - Size = 2, ///< `QSize` for resizable nodes. - CaptionVisible = 3, ///< `bool` for caption visibility. - Caption = 4, ///< `QString` for node caption. - Style = 5, ///< Custom NodeStyle as QJsonDocument - InternalData = 6, ///< Node-stecific user data as QJsonObject - InPortCount = 7, ///< `unsigned int` - OutPortCount = 9, ///< `unsigned int` - Widget = 10, ///< Optional `QWidget*` or `nullptr` + Type = 0, ///< Type of the current node, usually a string. + Position, ///< `QPointF` positon of the node on the scene. + Size, ///< `QSize` for resizable nodes. + CaptionVisible, ///< `bool` for caption visibility. + Caption, ///< `QString` for node caption. + Style, ///< Custom NodeStyle as QJsonDocument + InternalData, ///< Node-stecific user data as QJsonObject + InPortCount, ///< `unsigned int` + OutPortCount, ///< `unsigned int` + WidgetEmbeddable, ///< `bool` for widget embeddability + Widget, ///< Optional `QWidget*` or `nullptr` }; Q_ENUM_NS(NodeRole) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 6301164db..a387350f2 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -20,7 +20,9 @@ class StyleCollection; * AbstractGraphModel. * This class is the same what has been called NodeDataModel before v3. */ -class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable +class NODE_EDITOR_PUBLIC NodeDelegateModel + : public QObject + , public Serializable { Q_OBJECT @@ -78,6 +80,8 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable */ virtual QWidget *embeddedWidget() = 0; + virtual bool widgetEmbeddable() const { return true; } + virtual bool resizable() const { return false; } public Q_SLOTS: diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index a5ba8734f..9bcfd4492 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -203,7 +203,7 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const break; case NodeRole::CaptionVisible: - result = model->captionVisible(); + result = model->widgetEmbeddable() ? model->captionVisible() : true; break; case NodeRole::Caption: @@ -232,6 +232,10 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const result = model->nPorts(PortType::Out); break; + case NodeRole::WidgetEmbeddable: + result = model->widgetEmbeddable(); + break; + case NodeRole::Widget: { auto w = model->embeddedWidget(); result = QVariant::fromValue(w); @@ -245,7 +249,7 @@ NodeFlags DataFlowGraphModel::nodeFlags(NodeId nodeId) const { auto it = _models.find(nodeId); - if (it != _models.end() && it->second->resizable()) + if (it != _models.end() && it->second->widgetEmbeddable() && it->second->resizable()) return NodeFlag::Resizable; return NodeFlag::NoFlags; @@ -293,6 +297,9 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu case NodeRole::OutPortCount: break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 466c5ef34..f05cff066 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -32,7 +32,10 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const { unsigned int height = maxVerticalPortsExtent(nodeId); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && w) { height = std::max(height, static_cast(w->height())); } @@ -48,7 +51,7 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int width = inPortWidth + outPortWidth + 4 * _portSpasing; - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + if (isEmbeded && w) { width += w->width(); } @@ -150,7 +153,10 @@ QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && w) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { diff --git a/src/DefaultVerticalNodeGeometry.cpp b/src/DefaultVerticalNodeGeometry.cpp index 0254028ed..f6aecf07c 100644 --- a/src/DefaultVerticalNodeGeometry.cpp +++ b/src/DefaultVerticalNodeGeometry.cpp @@ -32,7 +32,10 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const { unsigned int height = _portSpasing; // maxHorizontalPortsExtent(nodeId); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && w) { height = std::max(height, static_cast(w->height())); } @@ -64,7 +67,7 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int width = std::max(totalInPortsWidth, totalOutPortsWidth); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + if (isEmbeded && w) { width = std::max(width, static_cast(w->width())); } @@ -177,7 +180,10 @@ QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && w) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index aa727cdaa..e4aa4ff99 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -81,6 +81,9 @@ void NodeGraphicsObject::embedQWidget() AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); geometry.recomputeSize(_nodeId); + if (!_graphModel.nodeData(_nodeId, NodeRole::WidgetEmbeddable).value()) + return; + if (auto w = _graphModel.nodeData(_nodeId, NodeRole::Widget).value()) { _proxyWidget = new QGraphicsProxyWidget(this); @@ -245,7 +248,8 @@ void NodeGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) setSelected(true); } - if (_nodeState.resizing()) { + if (_nodeState.resizing() + && _graphModel.nodeData(_nodeId, NodeRole::WidgetEmbeddable).value()) { auto diff = event->pos() - event->lastPos(); if (auto w = _graphModel.nodeData(_nodeId, NodeRole::Widget)) { From 16f23b969b3eab5fed7170a5d631f4cdb5e4988d Mon Sep 17 00:00:00 2001 From: QianMoth Date: Mon, 20 Feb 2023 12:49:04 +0800 Subject: [PATCH 2/5] fix: enumeration value 'WidgetEmbeddable' not handled in switch --- src/DataFlowGraphModel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 9bcfd4492..2790257c5 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -240,6 +240,9 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const auto w = model->embeddedWidget(); result = QVariant::fromValue(w); } break; + + default: + break; } return result; @@ -302,6 +305,9 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu case NodeRole::Widget: break; + + default: + break; } return result; @@ -340,7 +346,9 @@ QVariant DataFlowGraphModel::portData(NodeId nodeId, case PortRole::Caption: result = model->portCaption(portType, portIndex); + break; + default: break; } From c6a4b824b7064b488d7405cd9aedcbaa84031c91 Mon Sep 17 00:00:00 2001 From: QianMoth Date: Mon, 20 Feb 2023 12:53:39 +0800 Subject: [PATCH 3/5] Add `NodeRole::WidgetEmbeddable` for all switches --- examples/dynamic_ports/DynamicPortsModel.cpp | 7 +++++++ examples/simple_graph_model/SimpleGraphModel.cpp | 7 +++++++ examples/vertical_layout/SimpleGraphModel.cpp | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/examples/dynamic_ports/DynamicPortsModel.cpp b/examples/dynamic_ports/DynamicPortsModel.cpp index 1d8d537e1..2af46b857 100644 --- a/examples/dynamic_ports/DynamicPortsModel.cpp +++ b/examples/dynamic_ports/DynamicPortsModel.cpp @@ -143,6 +143,10 @@ QVariant DynamicPortsModel::nodeData(NodeId nodeId, NodeRole role) const result = _nodePortCounts[nodeId].out; break; + case NodeRole::WidgetEmbeddable: + result = true; + break; + case NodeRole::Widget: { result = QVariant::fromValue(widget(nodeId)); break; @@ -194,6 +198,9 @@ bool DynamicPortsModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value widget(nodeId)->populateButtons(PortType::Out, value.toUInt()); break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } diff --git a/examples/simple_graph_model/SimpleGraphModel.cpp b/examples/simple_graph_model/SimpleGraphModel.cpp index 7c04440f0..249dfb0f5 100644 --- a/examples/simple_graph_model/SimpleGraphModel.cpp +++ b/examples/simple_graph_model/SimpleGraphModel.cpp @@ -121,6 +121,10 @@ QVariant SimpleGraphModel::nodeData(NodeId nodeId, NodeRole role) const result = 1u; break; + case NodeRole::WidgetEmbeddable: + result = true; + break; + case NodeRole::Widget: result = QVariant(); break; @@ -167,6 +171,9 @@ bool SimpleGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value) case NodeRole::OutPortCount: break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } diff --git a/examples/vertical_layout/SimpleGraphModel.cpp b/examples/vertical_layout/SimpleGraphModel.cpp index 5b5e3b8b1..1b68effc9 100644 --- a/examples/vertical_layout/SimpleGraphModel.cpp +++ b/examples/vertical_layout/SimpleGraphModel.cpp @@ -121,6 +121,10 @@ QVariant SimpleGraphModel::nodeData(NodeId nodeId, NodeRole role) const result = 3u; break; + case NodeRole::WidgetEmbeddable: + result = true; + break; + case NodeRole::Widget: result = QVariant(); break; @@ -167,6 +171,9 @@ bool SimpleGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value) case NodeRole::OutPortCount: break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } From 8a096a368a7017babce261892e60b733cd272648 Mon Sep 17 00:00:00 2001 From: QianMoth Date: Thu, 6 Apr 2023 13:09:54 +0800 Subject: [PATCH 4/5] override --- examples/resizable_images/ImageLoaderModel.hpp | 2 +- examples/resizable_images/ImageShowModel.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/resizable_images/ImageLoaderModel.hpp b/examples/resizable_images/ImageLoaderModel.hpp index 394cf28a9..96785bcd0 100644 --- a/examples/resizable_images/ImageLoaderModel.hpp +++ b/examples/resizable_images/ImageLoaderModel.hpp @@ -18,7 +18,7 @@ using QtNodes::PortType; /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. -class ImageLoaderModel : public NodeDelegateModel +class ImageLoaderModel final : public NodeDelegateModel { Q_OBJECT diff --git a/examples/resizable_images/ImageShowModel.hpp b/examples/resizable_images/ImageShowModel.hpp index edf27280a..2874d301e 100644 --- a/examples/resizable_images/ImageShowModel.hpp +++ b/examples/resizable_images/ImageShowModel.hpp @@ -16,14 +16,14 @@ using QtNodes::PortType; /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. -class ImageShowModel : public NodeDelegateModel +class ImageShowModel final : public NodeDelegateModel { Q_OBJECT public: ImageShowModel(); - virtual ~ImageShowModel(); + ~ImageShowModel() override; public: QString caption() const override { return QString("Image Display"); } From dfb8ae8ee732d780e6909c26624ffc7fdbcd2a86 Mon Sep 17 00:00:00 2001 From: zmoth Date: Sat, 17 Aug 2024 07:57:34 +0800 Subject: [PATCH 5/5] fix spelling error --- examples/resizable_images/main.cpp | 8 ++++---- src/DefaultHorizontalNodeGeometry.cpp | 10 +++++----- src/DefaultVerticalNodeGeometry.cpp | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/resizable_images/main.cpp b/examples/resizable_images/main.cpp index d994463ed..9283ba674 100644 --- a/examples/resizable_images/main.cpp +++ b/examples/resizable_images/main.cpp @@ -46,13 +46,13 @@ int main(int argc, char *argv[]) .nodeData(nodeId, QtNodes::NodeRole::Caption) .value(); - bool isEmbeded = dataFlowGraphModel - .nodeData(nodeId, QtNodes::NodeRole::WidgetEmbeddable) - .value(); + bool isEmbedded = dataFlowGraphModel + .nodeData(nodeId, QtNodes::NodeRole::WidgetEmbeddable) + .value(); auto w = dataFlowGraphModel.nodeData(nodeId, QtNodes::NodeRole::Widget) .value(); - if (!isEmbeded && w) { + if (!isEmbedded && w) { w->setWindowTitle(name + "_" + QString::number(nodeId)); w->show(); } diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index f05cff066..2f1fba7d9 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -32,10 +32,10 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const { unsigned int height = maxVerticalPortsExtent(nodeId); - bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); - if (isEmbeded && w) { + if (isEmbedded && w) { height = std::max(height, static_cast(w->height())); } @@ -51,7 +51,7 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int width = inPortWidth + outPortWidth + 4 * _portSpasing; - if (isEmbeded && w) { + if (isEmbedded && w) { width += w->width(); } @@ -153,10 +153,10 @@ QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); - bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); - if (isEmbeded && w) { + if (isEmbedded && w) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { diff --git a/src/DefaultVerticalNodeGeometry.cpp b/src/DefaultVerticalNodeGeometry.cpp index f6aecf07c..74b52292a 100644 --- a/src/DefaultVerticalNodeGeometry.cpp +++ b/src/DefaultVerticalNodeGeometry.cpp @@ -32,10 +32,10 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const { unsigned int height = _portSpasing; // maxHorizontalPortsExtent(nodeId); - bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); - if (isEmbeded && w) { + if (isEmbedded && w) { height = std::max(height, static_cast(w->height())); } @@ -67,7 +67,7 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int width = std::max(totalInPortsWidth, totalOutPortsWidth); - if (isEmbeded && w) { + if (isEmbedded && w) { width = std::max(width, static_cast(w->width())); } @@ -180,10 +180,10 @@ QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); - bool isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); - if (isEmbeded && w) { + if (isEmbedded && w) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) {