Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ CMakeLists.txt.user
build*/
.vscode/

qt-build

tags
22 changes: 16 additions & 6 deletions examples/calculator/DivisionModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,27 @@ class DivisionModel : public MathOperationDataModel
auto n1 = _number1.lock();
auto n2 = _number2.lock();

QtNodes::NodeValidationState state;
if (n2 && (n2->number() == 0.0)) {
//modelValidationState = NodeValidationState::Error;
//modelValidationError = QStringLiteral("Division by zero error");
state._state = QtNodes::NodeValidationState::State::Error;
state._stateMessage = QStringLiteral("Division by zero error");
setValidatonState(state);
_result.reset();
} else if ( n2 && (n2->number() < 1e-5)) {
state._state = QtNodes::NodeValidationState::State::Warning;
state._stateMessage = QStringLiteral("Very small divident. Result might overflow");
setValidatonState(state);
if (n1) {
_result = std::make_shared<DecimalData>(n1->number() / n2->number());
} else {
_result.reset();
}
} else if (n1 && n2) {
//modelValidationState = NodeValidationState::Valid;
//modelValidationError = QString();
setValidatonState(state);
_result = std::make_shared<DecimalData>(n1->number() / n2->number());
} else {
//modelValidationState = NodeValidationState::Warning;
//modelValidationError = QStringLiteral("Missing or incorrect inputs");
QtNodes::NodeValidationState state;
setValidatonState(state);
_result.reset();
}

Expand Down
6 changes: 6 additions & 0 deletions include/QtNodes/internal/DefaultNodePainter.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <QIcon>
#include <QtGui/QPainter>

#include "AbstractNodePainter.hpp"
Expand Down Expand Up @@ -30,5 +31,10 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter
void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const;

void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const;

void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const;

private:
QIcon _toolTipIcon{"://info-tooltip.svg"};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this one is not needed in the header file, as we instantiate the icon one more time in the rendering function in *.cpp. Or vice versa

};
} // namespace QtNodes
27 changes: 14 additions & 13 deletions include/QtNodes/internal/Definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@ NODE_EDITOR_PUBLIC Q_NAMESPACE
Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC)
#endif

/**
/**
* 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`
};
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`
ValidationState = 11, ///< Enum NodeValidationState of the node
};
Q_ENUM_NS(NodeRole)

/**
Expand Down
39 changes: 34 additions & 5 deletions include/QtNodes/internal/NodeDelegateModel.hpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
#pragma once

#include <memory>

#include <QMetaType>
#include <QtWidgets/QWidget>

#include "Definitions.hpp"
#include "Export.hpp"
#include "NodeData.hpp"
#include "NodeStyle.hpp"
#include "Serializable.hpp"

#include <QtWidgets/QWidget>

#include <memory>


namespace QtNodes {

/**
* Describes whether a node configuration is usable and defines a description message
*/
struct NodeValidationState
{
enum class State : int {
Valid = 0, ///< All required inputs are present and correct.
Warning = 1, ///< Some inputs are missing or questionable, processing may be unreliable.
Error = 2, ///< Inputs or settings are invalid, preventing successful computation.
};
bool isValid() { return _state == State::Valid; };
QString const message() { return _stateMessage; }
State state() { return _state; }

State _state{State::Valid};
QString _stateMessage{""};
};

class StyleCollection;

/**
Expand Down Expand Up @@ -45,9 +63,16 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable
/// It is possible to hide port caption in GUI
virtual bool portCaptionVisible(PortType, PortIndex) const { return false; }

/// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class
virtual NodeValidationState validationState() const { return _nodeValidationState; }

public:
QJsonObject save() const override;

void load(QJsonObject const &) override;

void setValidatonState(const NodeValidationState &validationState);

virtual unsigned int nPorts(PortType portType) const = 0;

virtual NodeDataType dataType(PortType portType, PortIndex portIndex) const = 0;
Expand Down Expand Up @@ -117,6 +142,10 @@ public Q_SLOTS:

private:
NodeStyle _nodeStyle;

NodeValidationState _nodeValidationState;
};

} // namespace QtNodes

Q_DECLARE_METATYPE(QtNodes::NodeValidationState)
1 change: 1 addition & 0 deletions include/QtNodes/internal/NodeStyle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style

QColor WarningColor;
QColor ErrorColor;
QColor ToolTipIconColor;

float PenWidth;
float HoveredPenWidth;
Expand Down
5 changes: 3 additions & 2 deletions resources/DefaultStyle.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
"FontColorFaded" : "gray",
"ConnectionPointColor": [169, 169, 169],
"FilledConnectionPointColor": "cyan",
"ErrorColor": "red",
"WarningColor": [128, 128, 0],
"ErrorColor": [211, 47, 47],
"WarningColor": [255, 179, 0],
"ToolTipIconColor": "white",

"PenWidth": 1.0,
"HoveredPenWidth": 1.5,
Expand Down
4 changes: 4 additions & 0 deletions resources/info-tooltip.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions resources/resources.qrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<RCC version="1.0">
<qresource>
<RCC>
<qresource prefix="/">
<file>DefaultStyle.json</file>
<file>info-tooltip.svg</file>
</qresource>
</RCC>
53 changes: 31 additions & 22 deletions src/DataFlowGraphModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <stack>
#include <stdexcept>


namespace QtNodes {

DataFlowGraphModel::DataFlowGraphModel(std::shared_ptr<NodeDelegateModelRegistry> registry)
Expand Down Expand Up @@ -117,12 +116,10 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con
// Check port bounds, i.e. that we do not connect non-existing port numbers
auto checkPortBounds = [&](PortType const portType) {
NodeId const nodeId = getNodeId(portType, connectionId);
auto portCountRole = (portType == PortType::Out) ?
NodeRole::OutPortCount :
NodeRole::InPortCount;
auto portCountRole = (portType == PortType::Out) ? NodeRole::OutPortCount
: NodeRole::InPortCount;

std::size_t const portCount =
nodeData(nodeId, portCountRole).toUInt();
std::size_t const portCount = nodeData(nodeId, portCountRole).toUInt();

return getPortIndex(portType, connectionId) < portCount;
};
Expand All @@ -146,12 +143,9 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con
return connected.empty() || (policy == ConnectionPolicy::Many);
};

bool const basicChecks =
getDataType(PortType::Out).id == getDataType(PortType::In).id
&& portVacant(PortType::Out)
&& portVacant(PortType::In)
&& checkPortBounds(PortType::Out)
&& checkPortBounds(PortType::In);
bool const basicChecks = getDataType(PortType::Out).id == getDataType(PortType::In).id
&& portVacant(PortType::Out) && portVacant(PortType::In)
&& checkPortBounds(PortType::Out) && checkPortBounds(PortType::In);

// In data-flow mode (this class) it's important to forbid graph loops.
// We perform depth-first graph traversal starting from the "Input" port of
Expand All @@ -161,17 +155,16 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con
std::stack<NodeId> filo;
filo.push(connectionId.inNodeId);

while (!filo.empty())
{
auto id = filo.top(); filo.pop();
while (!filo.empty()) {
auto id = filo.top();
filo.pop();

if (id == connectionId.outNodeId) { // LOOP!
return true;
return true;
}

// Add out-connections to continue interations
std::size_t const nOutPorts =
nodeData(id, NodeRole::OutPortCount).toUInt();
std::size_t const nOutPorts = nodeData(id, NodeRole::OutPortCount).toUInt();

for (PortIndex index = 0; index < nOutPorts; ++index) {
auto const &outConnectionIds = connections(id, PortType::Out, index);
Expand All @@ -188,7 +181,6 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con
return basicChecks && (loopsEnabled() || !hasLoops());
}


void DataFlowGraphModel::addConnection(ConnectionId const connectionId)
{
_connectivity.insert(connectionId);
Expand Down Expand Up @@ -294,9 +286,14 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const
break;

case NodeRole::Widget: {
auto w = model->embeddedWidget();
auto *w = model->embeddedWidget();
result = QVariant::fromValue(w);
} break;

case NodeRole::ValidationState: {
auto validationState = model->validationState();
result = QVariant::fromValue(validationState);
} break;
}

return result;
Expand Down Expand Up @@ -356,6 +353,16 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu

case NodeRole::Widget:
break;

case NodeRole::ValidationState: {
if (value.canConvert<NodeValidationState>()) {
auto state = value.value<NodeValidationState>();
if (auto node = delegateModel<NodeDelegateModel>(nodeId); node != nullptr) {
node->setValidatonState(state);
}
}
Q_EMIT nodeUpdated(nodeId);
} break;
}

return result;
Expand Down Expand Up @@ -538,7 +545,8 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson)
connect(model.get(),
&NodeDelegateModel::portsAboutToBeDeleted,
this,
[restoredNodeId, this](PortType const portType, PortIndex const first, PortIndex const last) {
[restoredNodeId,
this](PortType const portType, PortIndex const first, PortIndex const last) {
portsAboutToBeDeleted(restoredNodeId, portType, first, last);
});

Expand All @@ -550,7 +558,8 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson)
connect(model.get(),
&NodeDelegateModel::portsAboutToBeInserted,
this,
[restoredNodeId, this](PortType const portType, PortIndex const first, PortIndex const last) {
[restoredNodeId,
this](PortType const portType, PortIndex const first, PortIndex const last) {
portsAboutToBeInserted(restoredNodeId, portType, first, last);
});

Expand Down
Loading
Loading