Skip to content

Commit afcbf28

Browse files
committed
fix: processing status icons and related bugs
ConnectionIdUtils: - Fixed "intNodeId" typo to "inNodeId" in toJson() - Added backward compatibility in fromJson() Processing status icons: - Initialize icons in NodeStyle constructor after Q_INIT_RESOURCE (fixes icons not loading due to static initialization order) - Fixed resource path format from "://" to ":/" - Skip drawing when status is NoStatus - Use const reference for ProcessingIconStyle to avoid copy - Auto-move icon to BottomLeft when node is resizable to avoid overlap with resize handle RandomNumberModel example: - Fixed memory leak by reusing timer instead of creating new ones
1 parent f953fca commit afcbf28

6 files changed

Lines changed: 61 additions & 29 deletions

File tree

examples/calculator/LongProcessingRandomNumber.hpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,19 @@
1515
class RandomNumberModel : public MathOperationDataModel
1616
{
1717
public:
18-
RandomNumberModel() {
18+
RandomNumberModel()
19+
: _timer(new QTimer(this))
20+
{
1921
this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty);
2022

21-
2223
QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() {
2324
if (_number1.lock() && _number2.lock()) {
24-
this->setNodeProcessingStatus(
25-
QtNodes::NodeProcessingStatus::Processing);
25+
this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Processing);
2626
}
27-
2827
emit requestNodeUpdate();
2928
});
3029
QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() {
31-
this->setNodeProcessingStatus(
32-
QtNodes::NodeProcessingStatus::Updated);
33-
30+
this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Updated);
3431
emit requestNodeUpdate();
3532
});
3633
}
@@ -44,31 +41,34 @@ class RandomNumberModel : public MathOperationDataModel
4441
private:
4542
void compute() override
4643
{
44+
// Stop any previous computation
45+
_timer->stop();
46+
_timer->disconnect();
47+
4748
Q_EMIT computingStarted();
4849
PortIndex const outPortIndex = 0;
4950

5051
auto n1 = _number1.lock();
5152
auto n2 = _number2.lock();
5253

53-
QTimer *timer = new QTimer(this);
54-
timer->start(1000);
55-
int secondsRemaining = 3;
56-
connect(timer, &QTimer::timeout, this, [=]() mutable {
57-
if (--secondsRemaining <= 0) {
58-
timer->stop();
54+
_secondsRemaining = 3;
55+
_timer->start(1000);
56+
connect(_timer, &QTimer::timeout, this, [this, n1, n2, outPortIndex]() {
57+
if (--_secondsRemaining <= 0) {
58+
_timer->stop();
5959
if (n1 && n2) {
6060
double a = n1->number();
6161
double b = n2->number();
6262

6363
if (a > b) {
6464
setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Failed);
65-
6665
emit requestNodeUpdate();
6766
return;
6867
}
6968

7069
double upper = std::nextafter(b, std::numeric_limits<double>::max());
71-
double randomValue = QRandomGenerator::global()->generateDouble() * (upper - a) + a;
70+
double randomValue = QRandomGenerator::global()->generateDouble() * (upper - a)
71+
+ a;
7272

7373
_result = std::make_shared<DecimalData>(randomValue);
7474
Q_EMIT computingFinished();
@@ -80,4 +80,8 @@ class RandomNumberModel : public MathOperationDataModel
8080
}
8181
});
8282
}
83+
84+
private:
85+
QTimer *_timer;
86+
int _secondsRemaining = 0;
8387
};

include/QtNodes/internal/ConnectionIdUtils.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,25 @@ inline QJsonObject toJson(ConnectionId const &connId)
132132

133133
connJson["outNodeId"] = static_cast<qint64>(connId.outNodeId);
134134
connJson["outPortIndex"] = static_cast<qint64>(connId.outPortIndex);
135-
connJson["intNodeId"] = static_cast<qint64>(connId.inNodeId);
135+
connJson["inNodeId"] = static_cast<qint64>(connId.inNodeId);
136136
connJson["inPortIndex"] = static_cast<qint64>(connId.inPortIndex);
137137

138138
return connJson;
139139
}
140140

141141
inline ConnectionId fromJson(QJsonObject const &connJson)
142142
{
143+
// Support both "inNodeId" (correct) and "intNodeId" (legacy typo) for backward compatibility
144+
NodeId inNodeId = InvalidNodeId;
145+
if (connJson.contains("inNodeId")) {
146+
inNodeId = static_cast<NodeId>(connJson["inNodeId"].toInt(InvalidNodeId));
147+
} else if (connJson.contains("intNodeId")) {
148+
inNodeId = static_cast<NodeId>(connJson["intNodeId"].toInt(InvalidNodeId));
149+
}
150+
143151
ConnectionId connId{static_cast<NodeId>(connJson["outNodeId"].toInt(InvalidNodeId)),
144152
static_cast<PortIndex>(connJson["outPortIndex"].toInt(InvalidPortIndex)),
145-
static_cast<NodeId>(connJson["intNodeId"].toInt(InvalidNodeId)),
153+
inNodeId,
146154
static_cast<PortIndex>(connJson["inPortIndex"].toInt(InvalidPortIndex))};
147155

148156
return connId;

include/QtNodes/internal/DefaultNodePainter.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter
3737
void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const;
3838

3939
private:
40-
QIcon _toolTipIcon{"://info-tooltip.svg"};
40+
QIcon _toolTipIcon{":/info-tooltip.svg"};
4141
};
4242
} // namespace QtNodes

include/QtNodes/internal/NodeStyle.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,13 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style
7878

7979
float Opacity;
8080

81-
QIcon statusUpdated{QStringLiteral("://status_icons/updated.svg")};
82-
QIcon statusProcessing{QStringLiteral("://status_icons/processing.svg")};
83-
QIcon statusPending{QStringLiteral("://status_icons/pending.svg")};
84-
QIcon statusInvalid{QStringLiteral("://status_icons/failed.svg")};
85-
QIcon statusEmpty{QStringLiteral("://status_icons/empty.svg")};
86-
QIcon statusPartial{QStringLiteral("://status_icons/partial.svg")};
81+
// Status icons - initialized in constructor after Q_INIT_RESOURCE
82+
QIcon statusUpdated;
83+
QIcon statusProcessing;
84+
QIcon statusPending;
85+
QIcon statusInvalid;
86+
QIcon statusEmpty;
87+
QIcon statusPartial;
8788

8889
ProcessingIconStyle processingIconStyle{};
8990
};

src/DefaultNodePainter.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,21 +306,32 @@ void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphics
306306
if (!delegate)
307307
return;
308308

309+
// Skip if status is NoStatus
310+
if (delegate->processingStatus() == NodeProcessingStatus::NoStatus)
311+
return;
312+
309313
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
310314

311315
QSize size = geometry.size(nodeId);
312316

313317
QPixmap pixmap = delegate->processingStatusIcon();
314-
NodeStyle nodeStyle = delegate->nodeStyle();
318+
if (pixmap.isNull())
319+
return;
315320

316-
ProcessingIconStyle iconStyle = nodeStyle.processingIconStyle;
321+
ProcessingIconStyle const &iconStyle = delegate->nodeStyle().processingIconStyle;
317322

318323
qreal iconSize = iconStyle._size;
319324
qreal margin = iconStyle._margin;
320325

321-
qreal x = margin;
326+
// Determine position, avoiding conflict with resize handle
327+
ProcessingIconPos pos = iconStyle._pos;
328+
bool isResizable = model.nodeFlags(nodeId) & NodeFlag::Resizable;
329+
if (isResizable && pos == ProcessingIconPos::BottomRight) {
330+
pos = ProcessingIconPos::BottomLeft;
331+
}
322332

323-
if (iconStyle._pos == ProcessingIconPos::BottomRight) {
333+
qreal x = margin;
334+
if (pos == ProcessingIconPos::BottomRight) {
324335
x = size.width() - iconSize - margin;
325336
}
326337

src/NodeStyle.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ NodeStyle::NodeStyle()
2121
// order fiasco: https://isocpp.org/wiki/faq/ctors#static-init-order
2222
initResources();
2323

24+
// Initialize status icons after resources are loaded
25+
statusUpdated = QIcon(":/status_icons/updated.svg");
26+
statusProcessing = QIcon(":/status_icons/processing.svg");
27+
statusPending = QIcon(":/status_icons/pending.svg");
28+
statusInvalid = QIcon(":/status_icons/failed.svg");
29+
statusEmpty = QIcon(":/status_icons/empty.svg");
30+
statusPartial = QIcon(":/status_icons/partial.svg");
31+
2432
// This configuration is stored inside the compiled unit and is loaded statically
2533
loadJsonFile(":DefaultStyle.json");
2634
}

0 commit comments

Comments
 (0)