FreeCAD

Форк
0
/
ProgressBar.cpp 
636 строк · 18.4 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <QApplication>
26
# include <QElapsedTimer>
27
# include <QKeyEvent>
28
# include <QMessageBox>
29
# include <QMetaObject>
30
# include <QThread>
31
# include <QTime>
32
# include <QTimer>
33
# include <QWindow>
34
#endif
35

36
#include "ProgressBar.h"
37
#include "MainWindow.h"
38
#include "ProgressDialog.h"
39
#include "WaitCursor.h"
40

41

42
using namespace Gui;
43

44

45
namespace Gui {
46
struct SequencerBarPrivate
47
{
48
    ProgressBar* bar;
49
    WaitCursor* waitCursor;
50
    QElapsedTimer measureTime;
51
    QElapsedTimer progressTime;
52
    QElapsedTimer checkAbortTime;
53
    QString text;
54
    bool guiThread;
55
};
56

57
struct ProgressBarPrivate
58
{
59
    QTimer* delayShowTimer;
60
    int minimumDuration;
61
    int observeEventFilter;
62

63
    bool isModalDialog(QObject* o) const
64
    {
65
        QWidget* parent = qobject_cast<QWidget*>(o);
66
        if (!parent) {
67
            QWindow* window = qobject_cast<QWindow*>(o);
68
            if (window)
69
                parent = QWidget::find(window->winId());
70
        }
71
        while (parent) {
72
            auto* dlg = qobject_cast<QMessageBox*>(parent);
73
            if (dlg && dlg->isModal())
74
                return true;
75
            auto* pd = qobject_cast<QProgressDialog*>(parent);
76
            if (pd)
77
                return true;
78
            parent = parent->parentWidget();
79
        }
80

81
        return false;
82
    }
83
};
84
}
85

86
SequencerBar* SequencerBar::_pclSingleton = nullptr;
87

88
SequencerBar* SequencerBar::instance()
89
{
90
    // not initialized?
91
    if (!_pclSingleton)
92
    {
93
        _pclSingleton = new SequencerBar();
94
    }
95

96
    return _pclSingleton;
97
}
98

99
SequencerBar::SequencerBar()
100
{
101
    d = new SequencerBarPrivate;
102
    d->bar = nullptr;
103
    d->waitCursor = nullptr;
104
    d->guiThread = true;
105
}
106

107
SequencerBar::~SequencerBar()
108
{
109
    delete d;
110
}
111

112
void SequencerBar::pause()
113
{
114
    QThread *currentThread = QThread::currentThread();
115
    QThread *thr = d->bar->thread(); // this is the main thread
116
    d->bar->leaveControlEvents(d->guiThread);
117
    if (thr != currentThread)
118
        return;
119

120
    // allow key handling of dialog and restore cursor
121
    d->waitCursor->restoreCursor();
122
    QApplication::setOverrideCursor(Qt::ArrowCursor);
123
}
124

125
void SequencerBar::resume()
126
{
127
    QThread *currentThread = QThread::currentThread();
128
    QThread *thr = d->bar->thread(); // this is the main thread
129
    if (thr == currentThread) {
130
        QApplication::restoreOverrideCursor();
131
        d->waitCursor->setWaitCursor();
132
    }
133

134
    // must be called as last to get control before WaitCursor
135
    d->bar->enterControlEvents(d->guiThread); // grab again
136
}
137

138
void SequencerBar::startStep()
139
{
140
    QThread *currentThread = QThread::currentThread();
141
    QThread *thr = d->bar->thread(); // this is the main thread
142
    if (thr != currentThread) {
143
        d->guiThread = false;
144
        QMetaObject::invokeMethod(d->bar, "setRangeEx", Qt::QueuedConnection,
145
            Q_ARG(int, 0), Q_ARG(int, (int)nTotalSteps));
146
        d->progressTime.start();
147
        d->checkAbortTime.start();
148
        d->measureTime.start();
149
        QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
150
        d->bar->enterControlEvents(d->guiThread);
151
    }
152
    else {
153
        d->guiThread = true;
154
        d->bar->setRangeEx(0, (int)nTotalSteps);
155
        d->progressTime.start();
156
        d->checkAbortTime.start();
157
        d->measureTime.start();
158
        d->waitCursor = new Gui::WaitCursor;
159
        d->bar->enterControlEvents(d->guiThread);
160
        d->bar->aboutToShow();
161
    }
162
}
163

164
void SequencerBar::checkAbort()
165
{
166
    if (d->bar->thread() != QThread::currentThread())
167
        return;
168
    if (!wasCanceled()) {
169
        if(d->checkAbortTime.elapsed() < 500)
170
            return;
171
        d->checkAbortTime.restart();
172
        qApp->processEvents();
173
        return;
174
    }
175
    // restore cursor
176
    pause();
177
    bool ok = d->bar->canAbort();
178
    // continue and show up wait cursor if needed
179
    resume();
180

181
    // force to abort the operation
182
    if ( ok ) {
183
        abort();
184
    } else {
185
        rejectCancel();
186
    }
187
}
188

189
void SequencerBar::nextStep(bool canAbort)
190
{
191
    QThread *currentThread = QThread::currentThread();
192
    QThread *thr = d->bar->thread(); // this is the main thread
193
    if (thr != currentThread) {
194
        if (wasCanceled() && canAbort) {
195
            abort();
196
        }
197
        else {
198
            setValue((int)nProgress + 1);
199
        }
200
    }
201
    else {
202
        if (wasCanceled() && canAbort) {
203
            // restore cursor
204
            pause();
205
            bool ok = d->bar->canAbort();
206
            // continue and show up wait cursor if needed
207
            resume();
208

209
            // force to abort the operation
210
            if ( ok ) {
211
                abort();
212
            } else {
213
                rejectCancel();
214
                setValue((int)nProgress+1);
215
            }
216
        }
217
        else {
218
            setValue((int)nProgress+1);
219
        }
220
    }
221
}
222

223
void SequencerBar::setProgress(size_t step)
224
{
225
    QThread* currentThread = QThread::currentThread();
226
    QThread* thr = d->bar->thread(); // this is the main thread
227
    if (thr != currentThread) {
228
        QMetaObject::invokeMethod(d->bar, "show", Qt::QueuedConnection);
229
    }
230
    else {
231
        d->bar->show();
232
    }
233

234
    setValue((int)step);
235
}
236

237
void SequencerBar::setValue(int step)
238
{
239
    QThread *currentThread = QThread::currentThread();
240
    QThread *thr = d->bar->thread(); // this is the main thread
241
    // if number of total steps is unknown then increment only by one
242
    if (nTotalSteps == 0) {
243
        int elapsed = d->progressTime.elapsed();
244
        // allow an update every 100 milliseconds only
245
        if (elapsed > 100) {
246
            d->progressTime.restart();
247
            if (thr != currentThread) {
248
                QMetaObject::invokeMethod(d->bar, "setValueEx", Qt::/*Blocking*/QueuedConnection,
249
                    Q_ARG(int,d->bar->value()+1));
250
            }
251
            else {
252
                d->bar->setValueEx(d->bar->value()+1);
253
                qApp->processEvents();
254
            }
255
        }
256
    }
257
    else {
258
        int elapsed = d->progressTime.elapsed();
259
        // allow an update every 100 milliseconds only
260
        if (elapsed > 100) {
261
            d->progressTime.restart();
262
            if (thr != currentThread) {
263
                QMetaObject::invokeMethod(d->bar, "setValueEx", Qt::/*Blocking*/QueuedConnection,
264
                Q_ARG(int,step));
265
                if (d->bar->isVisible())
266
                    showRemainingTime();
267
            }
268
            else {
269
                d->bar->setValueEx(step);
270
                if (d->bar->isVisible())
271
                    showRemainingTime();
272
                d->bar->resetObserveEventFilter();
273
                qApp->processEvents();
274
            }
275
        }
276
    }
277
}
278

279
void SequencerBar::showRemainingTime()
280
{
281
    QThread *currentThread = QThread::currentThread();
282
    QThread *thr = d->bar->thread(); // this is the main thread
283

284
    int elapsed = d->measureTime.elapsed();
285
    int progress = d->bar->value();
286
    int totalSteps = d->bar->maximum() - d->bar->minimum();
287

288
    QString txt = d->text;
289
    // More than 5 percent complete or more than 5 secs have elapsed.
290
    if (progress * 20 > totalSteps || elapsed > 5000) {
291
        int rest = (int) ( (double) totalSteps/progress * elapsed ) - elapsed;
292

293
        // more than 1 secs have elapsed and at least 100 ms are remaining
294
        if (elapsed > 1000 && rest > 100) {
295
            QTime time( 0,0, 0);
296
            time = time.addSecs( rest/1000 );
297
            QString remain = Gui::ProgressBar::tr("Remaining: %1").arg(time.toString());
298
            QString status = QString::fromLatin1("%1\t[%2]").arg(txt, remain);
299

300
            if (thr != currentThread) {
301
                QMetaObject::invokeMethod(getMainWindow(), "showMessage",
302
                    Qt::/*Blocking*/QueuedConnection,
303
                    Q_ARG(QString,status));
304
            }
305
            else {
306
                getMainWindow()->showMessage(status);
307
            }
308
        }
309
    }
310
}
311

312
void SequencerBar::resetData()
313
{
314
    QThread *currentThread = QThread::currentThread();
315
    QThread *thr = d->bar->thread(); // this is the main thread
316
    if (thr != currentThread) {
317
        QMetaObject::invokeMethod(d->bar, "resetEx", Qt::QueuedConnection);
318
        QMetaObject::invokeMethod(d->bar, "aboutToHide", Qt::QueuedConnection);
319
        QMetaObject::invokeMethod(getMainWindow(), "showMessage",
320
            Qt::/*Blocking*/QueuedConnection,
321
            Q_ARG(QString,QString()));
322
        QMetaObject::invokeMethod(getMainWindow(), "setPaneText",
323
            Qt::/*Blocking*/QueuedConnection,
324
            Q_ARG(int,1),
325
            Q_ARG(QString,QString()));
326
        d->bar->leaveControlEvents(d->guiThread);
327
    }
328
    else {
329
        d->bar->resetEx();
330
        // Note: Under Qt 4.1.4 this forces to run QWindowsStyle::eventFilter() twice
331
        // handling the same event thus a warning is printed. Possibly, this is a bug
332
        // in Qt. The message is QEventDispatcherUNIX::unregisterTimer: invalid argument.
333
        d->bar->aboutToHide();
334
        delete d->waitCursor;
335
        d->waitCursor = nullptr;
336
        d->bar->leaveControlEvents(d->guiThread);
337
        getMainWindow()->setPaneText(1, QString());
338
        getMainWindow()->showMessage(QString());
339
    }
340

341
    SequencerBase::resetData();
342
}
343

344
void SequencerBar::abort()
345
{
346
    //resets
347
    resetData();
348
    Base::AbortException exc("User aborted");
349
    throw exc;
350
}
351

352
void SequencerBar::setText (const char* pszTxt)
353
{
354
    QThread *currentThread = QThread::currentThread();
355
    QThread *thr = d->bar->thread(); // this is the main thread
356

357
    // print message to the statusbar
358
    d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
359
    if (thr != currentThread) {
360
        QMetaObject::invokeMethod(getMainWindow(), "showMessage",
361
            Qt::/*Blocking*/QueuedConnection,
362
            Q_ARG(QString,d->text));
363
    }
364
    else {
365
        getMainWindow()->showMessage(d->text);
366
    }
367
}
368

369
bool SequencerBar::isBlocking() const
370
{
371
    return d->guiThread;
372
}
373

374
QProgressBar* SequencerBar::getProgressBar(QWidget* parent)
375
{
376
    if (!d->bar)
377
        d->bar = new ProgressBar(this, parent);
378
    return d->bar;
379
}
380

381
// -------------------------------------------------------
382

383
/* TRANSLATOR Gui::ProgressBar */
384

385
ProgressBar::ProgressBar (SequencerBar* s, QWidget * parent)
386
    : QProgressBar(parent), sequencer(s)
387
{
388
#ifdef QT_WINEXTRAS_LIB
389
  m_taskbarButton = nullptr;
390
  m_taskbarButton = nullptr;
391
#endif
392
    d = new Gui::ProgressBarPrivate;
393
    d->minimumDuration = 2000; // 2 seconds
394
    d->delayShowTimer = new QTimer(this);
395
    d->delayShowTimer->setSingleShot(true);
396
    connect(d->delayShowTimer, &QTimer::timeout, this, &ProgressBar::delayedShow);
397
    d->observeEventFilter = 0;
398

399
    setFixedWidth(120);
400

401
    // write percentage to the center
402
    setAlignment(Qt::AlignHCenter);
403
    hide();
404
}
405

406
ProgressBar::~ProgressBar ()
407
{
408
    disconnect(d->delayShowTimer, &QTimer::timeout, this, &ProgressBar::delayedShow);
409
    delete d->delayShowTimer;
410
    delete d;
411
}
412

413
int ProgressBar::minimumDuration() const
414
{
415
    return d->minimumDuration;
416
}
417

418
void ProgressBar::resetEx()
419
{
420
  QProgressBar::reset();
421
#ifdef QT_WINEXTRAS_LIB
422
  setupTaskBarProgress();
423
  m_taskbarProgress->reset();
424
#endif
425
}
426

427
void ProgressBar::setRangeEx(int minimum, int maximum)
428
{
429
  QProgressBar::setRange(minimum, maximum);
430
#ifdef QT_WINEXTRAS_LIB
431
  setupTaskBarProgress();
432
  m_taskbarProgress->setRange(minimum, maximum);
433
#endif
434
}
435

436
void ProgressBar::setValueEx(int value)
437
{
438
  QProgressBar::setValue(value);
439
#ifdef QT_WINEXTRAS_LIB
440
  setupTaskBarProgress();
441
  m_taskbarProgress->setValue(value);
442
#endif
443
}
444

445
void ProgressBar::setMinimumDuration (int ms)
446
{
447
    if (value() == 0)
448
    {
449
        d->delayShowTimer->stop();
450
        d->delayShowTimer->start(ms);
451
    }
452

453
    d->minimumDuration = ms;
454
}
455

456
void ProgressBar::aboutToShow()
457
{
458
    // delay showing the bar
459
    d->delayShowTimer->start(d->minimumDuration);
460
#ifdef QT_WINEXTRAS_LIB
461
    setupTaskBarProgress();
462
    m_taskbarProgress->show();
463
#endif
464
}
465

466
void ProgressBar::delayedShow()
467
{
468
    if (!isVisible() && !sequencer->wasCanceled() && sequencer->isRunning()) {
469
        show();
470
    }
471
}
472

473
void ProgressBar::aboutToHide()
474
{
475
    hide();
476
#ifdef QT_WINEXTRAS_LIB
477
    setupTaskBarProgress();
478
    m_taskbarProgress->hide();
479
#endif
480
}
481

482
bool ProgressBar::canAbort() const
483
{
484
    auto ret = QMessageBox::question(getMainWindow(),tr("Aborting"),
485
    tr("Do you really want to abort the operation?"),  QMessageBox::Yes | QMessageBox::No,
486
    QMessageBox::No);
487

488
    return (ret == QMessageBox::Yes) ? true : false;
489
}
490

491
void ProgressBar::showEvent(QShowEvent* e)
492
{
493
    QProgressBar::showEvent(e);
494
    d->delayShowTimer->stop();
495
}
496

497
void ProgressBar::hideEvent(QHideEvent* e)
498
{
499
    QProgressBar::hideEvent(e);
500
    d->delayShowTimer->stop();
501
}
502

503
void ProgressBar::resetObserveEventFilter()
504
{
505
    d->observeEventFilter = 0;
506
}
507

508
void ProgressBar::enterControlEvents(bool grab)
509
{
510
    qApp->installEventFilter(this);
511

512
    // Make sure that we get the key events, otherwise the Inventor viewer usurps the key events
513
    // This also disables accelerators.
514
#if defined(Q_OS_LINUX)
515
    Q_UNUSED(grab)
516
#else
517
    if (grab)
518
        grabKeyboard();
519
#endif
520
}
521

522
void ProgressBar::leaveControlEvents(bool release)
523
{
524
    qApp->removeEventFilter(this);
525

526
#if defined(Q_OS_LINUX)
527
    Q_UNUSED(release)
528
#else
529
    // release the keyboard again
530
    if (release)
531
        releaseKeyboard();
532
#endif
533
}
534

535
#ifdef QT_WINEXTRAS_LIB
536
void ProgressBar::setupTaskBarProgress()
537
{
538
  if (!m_taskbarButton || !m_taskbarProgress)
539
  {
540
    m_taskbarButton = new QWinTaskbarButton(this);
541
    m_taskbarButton->setWindow(MainWindow::getInstance()->windowHandle());
542
    //m_myButton->setOverlayIcon(QIcon(""));
543

544
    m_taskbarProgress = m_taskbarButton->progress();
545
  }
546
}
547
#endif
548

549
bool ProgressBar::eventFilter(QObject* o, QEvent* e)
550
{
551
    if (sequencer->isRunning() && e) {
552
        QThread* currentThread = QThread::currentThread();
553
        QThread* thr = this->thread(); // this is the main thread
554
        if (thr != currentThread) {
555
            if (e->type() == QEvent::KeyPress) {
556
                auto ke = static_cast<QKeyEvent*>(e);
557
                if (ke->key() == Qt::Key_Escape) {
558
                    // cancel the operation
559
                    sequencer->tryToCancel();
560
                    return true;
561
                }
562
            }
563
            return QProgressBar::eventFilter(o, e);
564
        }
565

566
        // main thread
567
        switch ( e->type() )
568
        {
569
        // check for ESC
570
        case QEvent::KeyPress:
571
            {
572
                auto ke = static_cast<QKeyEvent*>(e);
573
                if (ke->key() == Qt::Key_Escape) {
574
                    // eventFilter() was called from the application 50 times without performing a new step (app could hang)
575
                    if (d->observeEventFilter > 50) {
576
                        // tries to unlock the application if it hangs (probably due to incorrect usage of Base::Sequencer)
577
                        if (ke->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) {
578
                            sequencer->resetData();
579
                            return true;
580
                        }
581
                    }
582

583
                    // cancel the operation
584
                    sequencer->tryToCancel();
585
                }
586

587
                return true;
588
            }   break;
589

590
        // ignore all these events
591
        case QEvent::KeyRelease:
592
        case QEvent::Enter:
593
        case QEvent::Leave:
594
        case QEvent::MouseButtonDblClick:
595
        case QEvent::MouseButtonRelease:
596
        case QEvent::MouseMove:
597
        case QEvent::NativeGesture:
598
        case QEvent::ContextMenu:
599
            {
600
                if (!d->isModalDialog(o))
601
                    return true;
602
            }   break;
603

604
        // special case if the main window's close button was pressed
605
        case QEvent::Close:
606
            {
607
                // avoid to exit while app is working
608
                // note: all other widget types are allowed to be closed anyway
609
                if (o == getMainWindow()) {
610
                    e->ignore();
611
                    return true;
612
                }
613
            }   break;
614

615
        // do a system beep and ignore the event
616
        case QEvent::MouseButtonPress:
617
            {
618
                if (!d->isModalDialog(o)) {
619
                    QApplication::beep();
620
                    return true;
621
                }
622
            }   break;
623

624
        default:
625
            {
626
            }   break;
627
        }
628

629
        d->observeEventFilter++;
630
    }
631

632
    return QProgressBar::eventFilter(o, e);
633
}
634

635

636
#include "moc_ProgressBar.cpp"
637

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.