KDStateMachineEditor API Documentation 2.1
Loading...
Searching...
No Matches
qscxmldebuginterfacesource.cpp
Go to the documentation of this file.
1/*
2 This file is part of the KDAB State Machine Editor Library.
3
4 SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: Kevin Funk <kevin.funk@kdab.com>
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LicenseRef-KDAB-KDStateMachineEditor
8
9 Licensees holding valid commercial KDAB State Machine Editor Library
10 licenses may use this file in accordance with the KDAB State Machine Editor
11 Library License Agreement provided with the Software.
12
13 Contact info@kdab.com if any conditions of this licensing are not clear to you.
14*/
15
17
18#include <config-kdsme.h>
19
20#include "rep_debuginterface_source.h"
21
22#include "objecthelper.h"
23
24#include <QScxmlStateMachine>
25#include <private/qscxmlstatemachineinfo_p.h>
26
27using namespace KDSME;
28using namespace DebugInterface;
29
30namespace {
31
32StateId makeStateId(QScxmlStateMachineInfo::StateId stateId)
33{
34 return StateId { static_cast<quint64>(stateId) };
35}
36TransitionId makeTransitionId(QScxmlStateMachineInfo::TransitionId transitionId)
37{
38 return TransitionId { static_cast<quint64>(transitionId) };
39}
40StateType makeStateType(QScxmlStateMachineInfo::StateType stateType)
41{
42 switch (stateType) {
43 case QScxmlStateMachineInfo::InvalidState:
44 return StateMachineState;
45 case QScxmlStateMachineInfo::NormalState:
46 return OtherState;
47 case QScxmlStateMachineInfo::ParallelState:
48 return OtherState; // FIXME: No ParallelState. Bug.
49 case QScxmlStateMachineInfo::FinalState:
50 return FinalState;
51 case QScxmlStateMachineInfo::ShallowHistoryState:
53 case QScxmlStateMachineInfo::DeepHistoryState:
54 return DeepHistoryState;
55 }
56 Q_UNREACHABLE();
57 return OtherState;
58}
59
60}
61
62class QScxmlDebugInterfaceSource::Private : public DebugInterfaceSource
63{
64 Q_OBJECT
65
66public:
67 explicit Private(QObject *parent = nullptr);
68
69 QScxmlStateMachine *qScxmlStateMachine() const;
70 void setQScxmlStateMachine(QScxmlStateMachine *machine);
71
72private Q_SLOTS:
73 void stateEntered(QScxmlStateMachineInfo::StateId state);
74 void stateExited(QScxmlStateMachineInfo::StateId state);
75 void handleStateConfigurationChanged();
76 void handleTransitionTriggered(QScxmlStateMachineInfo::TransitionId);
77
78 void updateStartStop();
79 void toggleRunning();
80
81 void repopulateGraph() override;
82
83private:
84 void addState(QScxmlStateMachineInfo::StateId state);
85 void addTransition(QScxmlStateMachineInfo::TransitionId transition);
86
87 QString labelForState(QScxmlStateMachineInfo::StateId state) const
88 {
89 // hide the state id for the root state + invalid states
90 if (state == QScxmlStateMachineInfo::InvalidStateId) {
91 return m_info->stateMachine()->name();
92 }
93
94 return QStringLiteral("%1 (%2)").arg(m_info->stateName(state)).arg(state);
95 }
96
97 QString labelForTransition(QScxmlStateMachineInfo::TransitionId transition) const
98 {
99 if (transition == QScxmlStateMachineInfo::InvalidTransitionId) {
100 return QString();
101 }
102
103 auto events = m_info->transitionEvents(transition);
104 if (events.empty()) {
105 return QString();
106 }
107
108 // TODO return all events combined instead of just first one
109 return QStringLiteral("%1 (%2)").arg(events.first()).arg(transition);
110 }
111
112 QScopedPointer<QScxmlStateMachineInfo> m_info;
113 QSet<QScxmlStateMachineInfo::StateId> m_recursionGuard;
114 QSet<QScxmlStateMachineInfo::TransitionId> m_recursionGuardForTransition;
115 QVector<QScxmlStateMachineInfo::StateId> m_lastStateConfig;
116};
117
122
126
128{
129 return d->qScxmlStateMachine();
130}
131
133{
134 d->setQScxmlStateMachine(machine);
135}
136
138{
139 return d.data();
140}
141
142QScxmlDebugInterfaceSource::Private::Private(QObject *parent)
143 : DebugInterfaceSource(parent)
144{
146
147 updateStartStop();
148}
149
150void QScxmlDebugInterfaceSource::Private::repopulateGraph()
151{
152 Q_EMIT aboutToRepopulateGraph();
153
154 updateStartStop();
155
156 if (m_info) {
157 // root state is not part of 'allStates', add it manually
158 addState(QScxmlStateMachineInfo::InvalidStateId);
159
160 const auto states = m_info->allStates();
161 for (auto stateId : states) {
162 addState(stateId);
163 }
164
165 const auto transitions = m_info->allTransitions();
166 for (auto transition : transitions) {
167 addTransition(transition);
168 }
169
170 m_recursionGuard.clear();
171 m_recursionGuardForTransition.clear();
172 }
173
174 Q_EMIT graphRepopulated();
175
176 // make sure to pass the current config to the listener
177 handleStateConfigurationChanged();
178}
179
180QScxmlStateMachine *QScxmlDebugInterfaceSource::Private::qScxmlStateMachine() const
181{
182 return m_info ? m_info->stateMachine() : nullptr;
183}
184
185void QScxmlDebugInterfaceSource::Private::setQScxmlStateMachine(QScxmlStateMachine *machine)
186{
187 repopulateGraph();
188
189 m_info.reset();
190
191 handleStateConfigurationChanged();
192
193 m_info.reset(machine ? new QScxmlStateMachineInfo(machine) : nullptr);
194 repopulateGraph();
195
196 if (m_info) {
197 connect(m_info.data(), &QScxmlStateMachineInfo::statesEntered, this,
198 [this](const QVector<QScxmlStateMachineInfo::StateId> &states) {
199 // TODO: Don't just use the first
200 if (!states.isEmpty()) {
201 stateEntered(states.first());
202 }
203 });
204 connect(m_info.data(), &QScxmlStateMachineInfo::statesExited, this,
205 [this](const QVector<QScxmlStateMachineInfo::StateId> &states) {
206 // TODO: Don't just use the first
207 if (!states.isEmpty()) {
208 stateExited(states.first());
209 }
210 });
211 connect(m_info.data(), &QScxmlStateMachineInfo::transitionsTriggered,
212 this, [this](const QVector<QScxmlStateMachineInfo::TransitionId> &transitions) {
213 // TODO: Don't just use the first
214 if (!transitions.isEmpty()) {
215 handleTransitionTriggered(transitions.first());
216 }
217 });
218 }
219
220 updateStartStop();
221}
222
223void QScxmlDebugInterfaceSource::Private::handleTransitionTriggered(QScxmlStateMachineInfo::TransitionId transition)
224{
225 Q_EMIT transitionTriggered(makeTransitionId(transition), labelForTransition(transition));
226}
227
228void QScxmlDebugInterfaceSource::Private::stateEntered(QScxmlStateMachineInfo::StateId state)
229{
230 Q_EMIT message(tr("State entered: %1").arg(labelForState(state)));
231 handleStateConfigurationChanged();
232}
233
234void QScxmlDebugInterfaceSource::Private::stateExited(QScxmlStateMachineInfo::StateId state)
235{
236 Q_EMIT message(tr("State exited: %1").arg(labelForState(state)));
237 handleStateConfigurationChanged();
238}
239
240void QScxmlDebugInterfaceSource::Private::handleStateConfigurationChanged()
241{
242 QVector<QScxmlStateMachineInfo::StateId> newConfig;
243 if (m_info) {
244 newConfig = m_info->configuration();
245 }
246
247 if (newConfig == m_lastStateConfig) {
248 return;
249 }
250 m_lastStateConfig = newConfig;
251
253 config.reserve(newConfig.size());
254 for (auto state : qAsConst(newConfig)) {
255 config << makeStateId(state);
256 }
257
258 Q_EMIT stateConfigurationChanged(config);
259}
260
261void QScxmlDebugInterfaceSource::Private::addState(QScxmlStateMachineInfo::StateId state)
262{
263 if (m_recursionGuard.contains(state)) {
264 return;
265 }
266
267 m_recursionGuard.insert(state);
268
269 auto parentState = m_info->stateParent(state);
270 addState(parentState); // be sure that parent is added first
271
272 const auto children = m_info->stateChildren(state);
273 const bool hasChildren = !children.isEmpty();
274
275 // add a connection from parent state to initial state if
276 // parent state is valid and parent state has an initial state
277 const auto parentInitialTransition = m_info->initialTransition(parentState);
278 if (parentInitialTransition != QScxmlStateMachineInfo::InvalidTransitionId) {
279 m_recursionGuardForTransition.insert(parentInitialTransition);
280 }
281
282 const auto parentInitialTransitionTargets = m_info->transitionTargets(parentInitialTransition);
283 Q_ASSERT(parentInitialTransitionTargets.size() <= 1); // assume there can only be at most one 'initial state'
284 const auto parentInitialState = parentInitialTransitionTargets.value(0);
285 const bool connectToInitial = parentInitialState == state; // TODO ?
286 Q_EMIT stateAdded(makeStateId(state), makeStateId(parentState),
287 hasChildren, labelForState(state),
288 makeStateType(m_info->stateType(state)), connectToInitial);
289
290 // add sub-states
291 Q_FOREACH (int child, m_info->stateChildren(state)) {
292 addState(child);
293 }
294}
295
296void QScxmlDebugInterfaceSource::Private::addTransition(QScxmlStateMachineInfo::TransitionId transition)
297{
298 if (m_recursionGuardForTransition.contains(transition)) {
299 return;
300 }
301
302 m_recursionGuardForTransition.insert(transition);
303
304 const auto sourceState = m_info->transitionSource(transition);
305 const auto targetStates = m_info->transitionTargets(transition);
306 addState(sourceState);
307 for (int targetState : targetStates) {
308 addState(targetState);
309 }
310
311 for (int targetState : targetStates) {
312 Q_EMIT transitionAdded(makeTransitionId(transition), makeStateId(sourceState),
313 makeStateId(targetState), labelForTransition(transition));
314 }
315}
316
317void QScxmlDebugInterfaceSource::Private::updateStartStop()
318{
319 Q_EMIT statusChanged(qScxmlStateMachine() != nullptr, qScxmlStateMachine() && qScxmlStateMachine()->isRunning());
320}
321
322void QScxmlDebugInterfaceSource::Private::toggleRunning()
323{
324 if (!qScxmlStateMachine()) {
325 return;
326 }
327
328 if (qScxmlStateMachine()->isRunning()) {
329 qScxmlStateMachine()->stop();
330 } else {
331 qScxmlStateMachine()->start();
332 }
333}
334
335#include "qscxmldebuginterfacesource.moc"
void setQScxmlStateMachine(QScxmlStateMachine *machine)
QScxmlStateMachine * qScxmlStateMachine() const
QList< StateId > StateMachineConfiguration

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDStateMachineEditor
Create Qt State Machine metacode using a graphical user interface
https://github.com/KDAB/KDStateMachineEditor
Generated on Tue Jul 15 2025 15:21:47 for KDStateMachineEditor API Documentation by doxygen 1.9.8