KDStateMachineEditor API Documentation 2.1
Loading...
Searching...
No Matches
qsmdebuginterfacesource.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: 2015 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 "qsmwatcher_p.h"
23
24#include "objecthelper.h"
25
26#include <QAbstractTransition>
27#include <QFinalState>
28#include <QHistoryState>
29#include <QSignalTransition>
30#include <QStateMachine>
31
32using namespace KDSME;
33using namespace DebugInterface;
34using namespace std;
35
36namespace {
37
38StateId makeStateId(QAbstractState *state)
39{
40 return StateId { reinterpret_cast<quint64>(state) };
41}
42TransitionId makeTransitionId(QAbstractTransition *transition)
43{
44 return TransitionId { reinterpret_cast<quint64>(transition) };
45}
46
47QString labelForTransition(QAbstractTransition *transition)
48{
49 const QString objectName = transition->objectName();
50 if (!objectName.isEmpty()) {
51 return objectName;
52 }
53
54 // Try to get a label for the transition if it is a QSignalTransition.
55 QSignalTransition *signalTransition = qobject_cast<QSignalTransition *>(transition);
56 if (signalTransition) {
57 return QString::fromLatin1("%1::%2").arg(ObjectHelper::displayString(signalTransition->senderObject())).arg(QString::fromLatin1(signalTransition->signal().mid(1)));
58 }
59
60 return ObjectHelper::displayString(transition);
61}
62
63}
64
65class QsmDebugInterfaceSource::Private : public DebugInterfaceSource
66{
67 Q_OBJECT
68
69public:
70 explicit Private(QObject *parent = nullptr);
71
72 void addState(QAbstractState *state);
73 void addTransition(QAbstractTransition *transition);
74
75 QStateMachine *qStateMachine() const;
76 void setQStateMachine(QStateMachine *machine);
77
78private Q_SLOTS:
79 void stateEntered(QAbstractState *state);
80 void stateExited(QAbstractState *state);
81 void handleStateConfigurationChanged();
82 void handleTransitionTriggered(QAbstractTransition *);
83
84 void updateStartStop();
85 void toggleRunning();
86
87 void repopulateGraph() override;
88
89private:
90 void updateStateItems();
91
92 bool mayAddState(QAbstractState *state);
93
94 QSMWatcher *m_stateMachineWatcher;
95 QSet<QAbstractState *> m_recursionGuard;
96 QSet<QAbstractState *> m_lastStateConfig;
97};
98
100 : d(new Private)
101{
102}
103
107
109{
110 return d->qStateMachine();
111}
112
114{
115 d->setQStateMachine(machine);
116}
117
119{
120 return d.data();
121}
122
123QsmDebugInterfaceSource::Private::Private(QObject *parent)
124 : DebugInterfaceSource(parent)
125 , m_stateMachineWatcher(new QSMWatcher(this))
126{
128
129 connect(m_stateMachineWatcher, SIGNAL(stateEntered(QAbstractState *)),
130 SLOT(stateEntered(QAbstractState *)));
131 connect(m_stateMachineWatcher, SIGNAL(stateExited(QAbstractState *)),
132 SLOT(stateExited(QAbstractState *)));
133 connect(m_stateMachineWatcher, SIGNAL(transitionTriggered(QAbstractTransition *)),
134 SLOT(handleTransitionTriggered(QAbstractTransition *)));
135
136 updateStartStop();
137}
138
139void QsmDebugInterfaceSource::Private::repopulateGraph()
140{
141 Q_EMIT aboutToRepopulateGraph();
142
143 updateStartStop();
144
145 addState(qStateMachine());
146 m_recursionGuard.clear();
147
148 Q_EMIT graphRepopulated();
149
150 // make sure to pass the current config to the listener
151 handleStateConfigurationChanged();
152}
153
154QStateMachine *QsmDebugInterfaceSource::Private::qStateMachine() const
155{
156 return m_stateMachineWatcher->watchedStateMachine();
157}
158
159bool QsmDebugInterfaceSource::Private::mayAddState(QAbstractState *state)
160{
161 if (!state) {
162 return false;
163 }
164
165 if (m_recursionGuard.contains(state)) {
166 return false;
167 }
168 return true;
169}
170
171void QsmDebugInterfaceSource::Private::setQStateMachine(QStateMachine *machine)
172{
173 QStateMachine *oldMachine = qStateMachine();
174 if (oldMachine) {
175 disconnect(oldMachine, SIGNAL(started()), this, SLOT(updateStartStop()));
176 disconnect(oldMachine, SIGNAL(stopped()), this, SLOT(updateStartStop()));
177 disconnect(oldMachine, SIGNAL(finished()), this, SLOT(updateStartStop()));
178 }
179
180 // m_stateModel->setStateMachine(machine);
181 handleStateConfigurationChanged();
182
183 m_stateMachineWatcher->setWatchedStateMachine(machine);
184 repopulateGraph();
185
186 if (machine) {
187 connect(machine, SIGNAL(started()), this, SLOT(updateStartStop()));
188 connect(machine, SIGNAL(stopped()), this, SLOT(updateStartStop()));
189 connect(machine, SIGNAL(finished()), this, SLOT(updateStartStop()));
190 }
191 updateStartStop();
192}
193
194void QsmDebugInterfaceSource::Private::handleTransitionTriggered(QAbstractTransition *transition)
195{
196 Q_EMIT transitionTriggered(makeTransitionId(transition), ObjectHelper::displayString(transition));
197}
198
199void QsmDebugInterfaceSource::Private::stateEntered(QAbstractState *state)
200{
201 Q_EMIT message(tr("State entered: %1").arg(ObjectHelper::displayString(state)));
202 handleStateConfigurationChanged();
203}
204
205void QsmDebugInterfaceSource::Private::stateExited(QAbstractState *state)
206{
207 Q_EMIT message(tr("State exited: %1").arg(ObjectHelper::displayString(state)));
208 handleStateConfigurationChanged();
209}
210
211void QsmDebugInterfaceSource::Private::handleStateConfigurationChanged()
212{
213 QSet<QAbstractState *> newConfig;
214 if (qStateMachine()) {
215 newConfig = qStateMachine()->configuration();
216 }
217
218 if (newConfig == m_lastStateConfig) {
219 return;
220 }
221 m_lastStateConfig = newConfig;
222
224 config.reserve(newConfig.size());
225 for (QAbstractState *state : qAsConst(newConfig)) {
226 config << makeStateId(state);
227 }
228
229 Q_EMIT stateConfigurationChanged(config);
230}
231
232void QsmDebugInterfaceSource::Private::addState(QAbstractState *state)
233{
234 if (!state)
235 return;
236
237 if (!mayAddState(state)) {
238 return;
239 }
240
241 Q_ASSERT(!m_recursionGuard.contains(state));
242 m_recursionGuard.insert(state);
243
244 QState *parentState = state->parentState();
245 if (parentState) {
246 addState(parentState); // be sure that parent is added first
247 }
248
249 const bool hasChildren = state->findChild<QAbstractState *>();
250 const QString &label = ObjectHelper::displayString(state);
251 // add a connection from parent state to initial state if
252 // parent state is valid and parent state has an initial state
253 const bool connectToInitial = parentState && parentState->initialState() == state;
254 StateType type = OtherState;
255 if (qobject_cast<QFinalState *>(state)) {
256 type = FinalState;
257 } else if (auto historyState = qobject_cast<QHistoryState *>(state)) {
258 type = historyState->historyType() == QHistoryState::ShallowHistory ? ShallowHistoryState : DeepHistoryState;
259 } else if (qobject_cast<QStateMachine *>(state)) {
260 type = StateMachineState;
261 }
262
263 Q_EMIT stateAdded(makeStateId(state), makeStateId(parentState),
264 hasChildren, label, type, connectToInitial);
265
266 // add outgoing transitions
267 Q_FOREACH (auto transition, state->findChildren<QAbstractTransition *>(QString(), Qt::FindDirectChildrenOnly)) {
268 addTransition(transition);
269 }
270
271 // add sub-states
272 Q_FOREACH (auto child, state->findChildren<QAbstractState *>(QString(), Qt::FindDirectChildrenOnly)) {
273 addState(child);
274 }
275}
276
277void QsmDebugInterfaceSource::Private::addTransition(QAbstractTransition *transition)
278{
279 QState *sourceState = transition->sourceState();
280 QAbstractState *targetState = transition->targetState();
281 addState(sourceState);
282 addState(targetState);
283
284 const QString label = labelForTransition(transition);
285 Q_EMIT transitionAdded(makeTransitionId(transition), makeStateId(sourceState),
286 makeStateId(targetState), label);
287}
288
289void QsmDebugInterfaceSource::Private::updateStartStop()
290{
291 Q_EMIT statusChanged(qStateMachine() != nullptr, qStateMachine() && qStateMachine()->isRunning());
292}
293
294void QsmDebugInterfaceSource::Private::toggleRunning()
295{
296 if (!qStateMachine()) {
297 return;
298 }
299
300 if (qStateMachine()->isRunning()) {
301 qStateMachine()->stop();
302 } else {
303 qStateMachine()->start();
304 }
305}
306
307#include "qsmdebuginterfacesource.moc"
void setQStateMachine(QStateMachine *machine)
QList< StateId > StateMachineConfiguration
KDSME_CORE_EXPORT QString displayString(const QObject *object, DisplayOption option=NoStrip)

© 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