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