Line data Source code
1 : // Copyright (c) 2016-2020 The Bitcoin Core developers 2 : // Distributed under the MIT software license, see the accompanying 3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : 5 : #include <qt/modaloverlay.h> 6 : #include <qt/forms/ui_modaloverlay.h> 7 : 8 : #include <chainparams.h> 9 : #include <qt/guiutil.h> 10 : 11 : #include <QEasingCurve> 12 : #include <QPropertyAnimation> 13 : #include <QResizeEvent> 14 : 15 0 : ModalOverlay::ModalOverlay(bool enable_wallet, QWidget *parent) : 16 0 : QWidget(parent), 17 0 : ui(new Ui::ModalOverlay), 18 0 : bestHeaderHeight(0), 19 0 : bestHeaderDate(QDateTime()), 20 0 : layerIsVisible(false), 21 0 : userClosed(false) 22 0 : { 23 0 : ui->setupUi(this); 24 0 : connect(ui->closeButton, &QPushButton::clicked, this, &ModalOverlay::closeClicked); 25 0 : if (parent) { 26 0 : parent->installEventFilter(this); 27 0 : raise(); 28 : } 29 : 30 0 : blockProcessTime.clear(); 31 0 : setVisible(false); 32 0 : if (!enable_wallet) { 33 0 : ui->infoText->setVisible(false); 34 0 : ui->infoTextStrong->setText(tr("%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.").arg(PACKAGE_NAME)); 35 0 : } 36 : 37 0 : m_animation.setTargetObject(this); 38 0 : m_animation.setPropertyName("pos"); 39 0 : m_animation.setDuration(300 /* ms */); 40 0 : m_animation.setEasingCurve(QEasingCurve::OutQuad); 41 0 : } 42 : 43 0 : ModalOverlay::~ModalOverlay() 44 0 : { 45 0 : delete ui; 46 0 : } 47 : 48 0 : bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) { 49 0 : if (obj == parent()) { 50 0 : if (ev->type() == QEvent::Resize) { 51 0 : QResizeEvent * rev = static_cast<QResizeEvent*>(ev); 52 0 : resize(rev->size()); 53 0 : if (!layerIsVisible) 54 0 : setGeometry(0, height(), width(), height()); 55 : 56 0 : if (m_animation.endValue().toPoint().y() > 0) { 57 0 : m_animation.setEndValue(QPoint(0, height())); 58 0 : } 59 0 : } 60 0 : else if (ev->type() == QEvent::ChildAdded) { 61 0 : raise(); 62 0 : } 63 : } 64 0 : return QWidget::eventFilter(obj, ev); 65 0 : } 66 : 67 : //! Tracks parent widget changes 68 0 : bool ModalOverlay::event(QEvent* ev) { 69 0 : if (ev->type() == QEvent::ParentAboutToChange) { 70 0 : if (parent()) parent()->removeEventFilter(this); 71 : } 72 0 : else if (ev->type() == QEvent::ParentChange) { 73 0 : if (parent()) { 74 0 : parent()->installEventFilter(this); 75 0 : raise(); 76 0 : } 77 : } 78 0 : return QWidget::event(ev); 79 : } 80 : 81 0 : void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate) 82 : { 83 0 : if (count > bestHeaderHeight) { 84 0 : bestHeaderHeight = count; 85 0 : bestHeaderDate = blockDate; 86 0 : UpdateHeaderSyncLabel(); 87 0 : } 88 0 : } 89 : 90 0 : void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress) 91 : { 92 0 : QDateTime currentDate = QDateTime::currentDateTime(); 93 : 94 : // keep a vector of samples of verification progress at height 95 0 : blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress)); 96 : 97 : // show progress speed if we have more than one sample 98 0 : if (blockProcessTime.size() >= 2) { 99 : double progressDelta = 0; 100 : double progressPerHour = 0; 101 : qint64 timeDelta = 0; 102 : qint64 remainingMSecs = 0; 103 0 : double remainingProgress = 1.0 - nVerificationProgress; 104 0 : for (int i = 1; i < blockProcessTime.size(); i++) { 105 0 : QPair<qint64, double> sample = blockProcessTime[i]; 106 : 107 : // take first sample after 500 seconds or last available one 108 0 : if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) { 109 0 : progressDelta = blockProcessTime[0].second - sample.second; 110 0 : timeDelta = blockProcessTime[0].first - sample.first; 111 0 : progressPerHour = progressDelta / (double) timeDelta * 1000 * 3600; 112 0 : remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; 113 0 : break; 114 : } 115 0 : } 116 : // show progress increase per hour 117 0 : ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%"); 118 : 119 : // show expected remaining time 120 0 : if(remainingMSecs >= 0) { 121 0 : ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); 122 0 : } else { 123 0 : ui->expectedTimeLeft->setText(QObject::tr("unknown")); 124 : } 125 : 126 : static const int MAX_SAMPLES = 5000; 127 0 : if (blockProcessTime.count() > MAX_SAMPLES) { 128 0 : blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES); 129 : } 130 0 : } 131 : 132 : // show the last block date 133 0 : ui->newestBlockDate->setText(blockDate.toString()); 134 : 135 : // show the percentage done according to nVerificationProgress 136 0 : ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%"); 137 0 : ui->progressBar->setValue(nVerificationProgress*100); 138 : 139 0 : if (!bestHeaderDate.isValid()) 140 : // not syncing 141 0 : return; 142 : 143 : // estimate the number of headers left based on nPowTargetSpacing 144 : // and check if the gui is not aware of the best header (happens rarely) 145 0 : int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing; 146 0 : bool hasBestHeader = bestHeaderHeight >= count; 147 : 148 : // show remaining number of blocks 149 0 : if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) { 150 0 : ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count)); 151 0 : } else { 152 0 : UpdateHeaderSyncLabel(); 153 0 : ui->expectedTimeLeft->setText(tr("Unknown...")); 154 : } 155 0 : } 156 : 157 0 : void ModalOverlay::UpdateHeaderSyncLabel() { 158 0 : int est_headers_left = bestHeaderDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing; 159 0 : ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)...").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1))); 160 0 : } 161 : 162 0 : void ModalOverlay::toggleVisibility() 163 : { 164 0 : showHide(layerIsVisible, true); 165 0 : if (!layerIsVisible) 166 0 : userClosed = true; 167 0 : } 168 : 169 0 : void ModalOverlay::showHide(bool hide, bool userRequested) 170 : { 171 0 : if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested)) 172 : return; 173 : 174 0 : Q_EMIT triggered(hide); 175 : 176 0 : if (!isVisible() && !hide) 177 0 : setVisible(true); 178 : 179 0 : m_animation.setStartValue(QPoint(0, hide ? 0 : height())); 180 : // The eventFilter() updates the endValue if it is required for QEvent::Resize. 181 0 : m_animation.setEndValue(QPoint(0, hide ? height() : 0)); 182 0 : m_animation.start(QAbstractAnimation::KeepWhenStopped); 183 0 : layerIsVisible = !hide; 184 0 : } 185 : 186 0 : void ModalOverlay::closeClicked() 187 : { 188 0 : showHide(true); 189 0 : userClosed = true; 190 0 : }