-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSequencer.h
More file actions
410 lines (384 loc) · 13.4 KB
/
Sequencer.h
File metadata and controls
410 lines (384 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/***************************************************************************
* Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef BASE_SEQUENCER_H
#define BASE_SEQUENCER_H
#include "compatible.h"
#include <vector>
#include <memory>
//#include <CXX/Extensions.hxx>
//#include "Exception.h"
namespace Base
{
class AbortException;
class SequencerLauncher;
/**
* \brief This class gives the user an indication of the progress of an operation and
* it is used to reassure him that the application is still running.
*
* Here are some code snippets of how to use the sequencer:
* \code
*
* #include <Base/Sequencer.h>
*
* //first example
* Base::SequencerLauncher seq("my text", 10)
* for (int i=0; i<10; i++)
* {
* // do something
* seq.next ();
* }
*
* //second example
* Base::SequencerLauncher seq("my text", 10)
* do
* {
* // do something
* }
* while (seq.next());
*
* \endcode
*
* The implementation of this class also supports several nested instances
* at a time. But note, that only the first instance has an effect. Any further
* sequencer instance doesn't influence the total number of iteration steps. This
* is simply because it's impossible to get the exact number of iteration steps
* for nested instances and thus we have either too few steps estimated then the
* sequencer may indicate 100% but the algorithm still running or we have too many
* steps estimated so that the an algorithm may stop long before the sequencer
* reaches 100%.
*
* \code
* try {
* //start the first operation
* Base::SequencerLauncher seq1("my text", 10)
* for (int i=0; i<10, i++)
* {
* // do something
*
* // start the second operation while the first one is still running
* Base::SequencerLauncher seq2("another text", 10);
* for (int j=0; j<10; j++)
* {
* // do something different
* seq2.next ();
* }
*
* seq1.next ( true ); // allow to cancel
* }
* }
* catch(const Base::AbortException&){
* // cleanup your data if needed
* }
*
* \endcode
*
* \note If using the sequencer with SequencerLauncher.next(\a true) then you must
* take into account that the exception \a AbortException could be thrown, e.g. in
* case the ESC button was pressed. So in this case it's always a good idea to use
* the sequencer within a try-catch block.
*
* \note Instances of SequencerLauncher should always be created on the stack.
* This is because if an exception somewhere is thrown the destructor is auto-
* matically called to clean-up internal data.
*
* \note It's not supported to create an instance of SequencerBase or a sub-class
* in another thread than the main thread. But you can create SequencerLauncher
* instances in other threads.
*
* \author Werner Mayer
*/
class BaseExport SequencerBase
{
friend class SequencerLauncher;
public:
/**
* Returns the last created sequencer instance.
* If you create an instance of a class inheriting SequencerBase
* this object is retrieved instead.
*
* This mechanism is very useful to have an own sequencer for each layer of FreeCAD.
* For example, if FreeCAD is running in server mode you have/need no GUI layer
* and therewith no (graphical) progress bar; in this case ConsoleSequencer is taken.
* But in cases FreeCAD is running with GUI the @ref Gui::ProgressBar is taken instead.
* @see Sequencer
*/
static SequencerBase& Instance();
/**
* Returns true if the running sequencer is blocking any user input.
* This might be only of interest of the GUI where the progress bar or dialog
* is used from a thread. If started from a thread this method should return
* false, otherwise true. The default implementation always returns true.
*/
virtual bool isBlocking() const;
/** If \a bLock is true then the sequencer gets locked. startStep() and nextStep()
* don't get invoked any more until the sequencer gets unlocked again.
* This method returns the previous lock state.
*/
bool setLocked(bool bLock);
/** Returns true if the sequencer was locked, false otherwise. */
bool isLocked() const;
/** Returns true if the sequencer is running, otherwise returns false. */
bool isRunning() const;
/**
* Returns true if the pending operation was canceled.
*/
bool wasCanceled() const;
/// Check if the operation is aborted by user
virtual void checkAbort() {}
protected:
/**
* Starts a new operation, returns false if there is already a pending operation,
* otherwise it returns true.
* In this method startStep() gets invoked that can be reimplemented in sub-classes.
*/
bool start(const char* pszStr, size_t steps);
/** Returns the number of steps. */
size_t numberOfSteps() const;
/** Returns the current state of progress in percent. */
int progressInPercent() const;
/**
* Performs the next step and returns true if the operation is not yet finished.
* But note, when 0 was passed to start() as the number of total steps this method
* always returns false.
*
* In this method nextStep() gets invoked that can be reimplemented in sub-classes.
* If \a canAbort is true then the operations can be aborted, otherwise (the default)
* the operation cannot be aborted. In case it gets aborted an exception AbortException
* is thrown.
*/
bool next(bool canAbort = false);
/**
* Stops the sequencer if all operations are finished. It returns false if
* there are still pending operations, otherwise it returns true.
*/
bool stop();
/**
* Breaks the sequencer if needed. The default implementation does nothing.
* Every pause() must eventually be followed by a corresponding @ref resume().
* @see Gui::ProgressBar.
*/
virtual void pause();
/**
* Continues with progress. The default implementation does nothing.
* @see pause(), @see Gui::ProgressBar.
*/
virtual void resume();
/**
* Try to cancel the pending operation(s).
* E.g. @ref Gui::ProgressBar calls this method after the ESC button was pressed.
*/
void tryToCancel();
/**
* If you tried to cancel but then decided to continue the operation.
* E.g. in @ref Gui::ProgressBar a dialog appears asking if you really want to
* cancel. If you decide to continue this method must be called.
*/
void rejectCancel();
protected:
/** construction */
SequencerBase();
/** Destruction */
virtual ~SequencerBase();
/**
* Sets a text what the pending operation is doing. The default implementation
* does nothing.
*/
virtual void setText (const char* pszTxt);
/**
* This method can be reimplemented in sub-classes to give the user a feedback
* when a new sequence starts. The default implementation does nothing.
*/
virtual void startStep();
/**
* This method can be reimplemented in sub-classes to give the user a feedback
* when the next is performed. The default implementation does nothing. If \a canAbort
* is true then the pending operation can aborted, otherwise not. Depending on the
* re-implementation this method can throw an AbortException if canAbort is true.
*/
virtual void nextStep(bool canAbort);
/**
* Sets the progress indicator to a certain position.
*/
virtual void setProgress(size_t);
/**
* Resets internal data.
* If you want to reimplement this method, it is very important to call it in
* the re-implemented method.
*/
virtual void resetData();
protected:
size_t nProgress; /**< Stores the current amount of progress.*/
size_t nTotalSteps; /**< Stores the total number of steps */
private:
bool _bLocked; /**< Lock/unlock sequencer. */
bool _bCanceled; /**< Is set to true if the last pending operation was canceled */
int _nLastPercentage; /**< Progress in percent. */
};
/** This special sequencer might be useful if you want to suppress any indication
* of the progress to the user.
*/
class BaseExport EmptySequencer : public Base::SequencerBase
{
public:
/** construction */
EmptySequencer();
/** Destruction */
~EmptySequencer();
};
/**
* \brief This class writes the progress to the console window.
*/
class BaseExport ConsoleSequencer : public SequencerBase
{
public:
/** construction */
ConsoleSequencer ();
/** Destruction */
~ConsoleSequencer ();
protected:
/** Starts the sequencer */
void startStep();
/** Writes the current progress to the console window. */
void nextStep(bool canAbort);
private:
/** Puts text to the console window */
void setText (const char* pszTxt);
/** Resets the sequencer */
void resetData();
};
/** The SequencerLauncher class is provided for convenience. It allows you to run an instance of the
* sequencer by instantiating an object of this class -- most suitable on the stack. So this mechanism
* can be used for try-catch-blocks to destroy the object automatically if the C++ exception mechanism
* cleans up the stack.
*
* This class has been introduced to simplify the use with the sequencer. In the FreeCAD Gui layer there
* is a subclass of SequencerBase called ProgressBar that grabs the keyboard and filters most of the incoming
* events. If the programmer uses the API of SequencerBase directly to start an instance without due diligence
* with exceptions then a not handled exception could block the whole application -- the user has to kill the
* application then.
*
* Below is an example of a not correctly used sequencer.
*
* \code
*
* #include <Base/Sequencer.h>
*
* void runOperation();
*
* void myTest()
* {
* try{
* runOperation();
* } catch(...) {
* // the programmer forgot to stop the sequencer here
* // Under circumstances the sequencer never gets stopped so the keyboard never gets ungrabbed and
* // all Gui events still gets filtered.
* }
* }
*
* void runOperation()
* {
* Base::Sequencer().start ("my text", 10);
*
* for (int i=0; i<10; i++)
* {
* // do something where an exception be thrown
* ...
* Base::Sequencer().next ();
* }
*
* Base::Sequencer().stop ();
* }
*
* \endcode
*
* To avoid such problems the SequencerLauncher class can be used as follows:
*
* \code
*
* #include <Base/Sequencer.h>
*
* void runOperation();
*
* void myTest()
* {
* try{
* runOperation();
* } catch(...) {
* // the programmer forgot to halt the sequencer here
* // If SequencerLauncher leaves its scope the object gets destructed automatically and
* // stops the running sequencer.
* }
* }
*
* void runOperation()
* {
* // create an instance on the stack (not on any terms on the heap)
* SequencerLauncher seq("my text", 10);
*
* for (int i=0; i<10; i++)
* {
* // do something (e.g. here can be thrown an exception)
* ...
* seq.next ();
* }
* }
*
* \endcode
*
* @author Werner Mayer
*/
class BaseExport SequencerLauncher
{
public:
SequencerLauncher(const char* pszStr, size_t steps);
~SequencerLauncher();
size_t numberOfSteps() const;
void setText (const char* pszTxt);
bool next(bool canAbort = false);
void setProgress(size_t);
bool wasCanceled() const;
};
/** Access to the only SequencerBase instance */
inline SequencerBase& Sequencer ()
{
return SequencerBase::Instance();
}
/*
class BaseExport ProgressIndicatorPy : public Py::PythonExtension<ProgressIndicatorPy>
{
public:
static void init_type(void); // announce properties and methods
ProgressIndicatorPy();
~ProgressIndicatorPy();
Py::Object repr();
Py::Object start(const Py::Tuple&);
Py::Object next(const Py::Tuple&);
Py::Object stop(const Py::Tuple&);
private:
static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *);
private:
std::unique_ptr<SequencerLauncher> _seq;
};
*/
} // namespace Base
#endif // BASE_SEQUENCER_H