Bitcoin Core  29.1.0
P2P Digital Currency
modaloverlay.cpp
Go to the documentation of this file.
1 // Copyright (c) 2016-2022 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 <bitcoin-build-config.h> // IWYU pragma: keep
6 
7 #include <qt/modaloverlay.h>
8 #include <qt/forms/ui_modaloverlay.h>
9 
10 #include <chainparams.h>
11 #include <qt/guiutil.h>
12 
13 #include <QEasingCurve>
14 #include <QPropertyAnimation>
15 #include <QResizeEvent>
16 
17 ModalOverlay::ModalOverlay(bool enable_wallet, QWidget* parent)
18  : QWidget(parent),
19  ui(new Ui::ModalOverlay),
20  bestHeaderDate(QDateTime())
21 {
22  ui->setupUi(this);
23  connect(ui->closeButton, &QPushButton::clicked, this, &ModalOverlay::closeClicked);
24  if (parent) {
25  parent->installEventFilter(this);
26  raise();
27  }
28  ui->closeButton->installEventFilter(this);
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(CLIENT_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 
65  if (obj == ui->closeButton && ev->type() == QEvent::FocusOut && layerIsVisible) {
66  ui->closeButton->setFocus(Qt::OtherFocusReason);
67  }
68 
69  return QWidget::eventFilter(obj, ev);
70 }
71 
73 bool ModalOverlay::event(QEvent* ev) {
74  if (ev->type() == QEvent::ParentAboutToChange) {
75  if (parent()) parent()->removeEventFilter(this);
76  }
77  else if (ev->type() == QEvent::ParentChange) {
78  if (parent()) {
79  parent()->installEventFilter(this);
80  raise();
81  }
82  }
83  return QWidget::event(ev);
84 }
85 
86 void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate, bool presync)
87 {
88  if (!presync && count > bestHeaderHeight) {
90  bestHeaderDate = blockDate;
92  }
93  if (presync) {
94  UpdateHeaderPresyncLabel(count, blockDate);
95  }
96 }
97 
98 void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress)
99 {
100  QDateTime currentDate = QDateTime::currentDateTime();
101 
102  // keep a vector of samples of verification progress at height
103  blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress));
104 
105  // show progress speed if we have more than one sample
106  if (blockProcessTime.size() >= 2) {
107  double progressDelta = 0;
108  double progressPerHour = 0;
109  qint64 timeDelta = 0;
110  qint64 remainingMSecs = 0;
111  double remainingProgress = 1.0 - nVerificationProgress;
112  for (int i = 1; i < blockProcessTime.size(); i++) {
113  QPair<qint64, double> sample = blockProcessTime[i];
114 
115  // take first sample after 500 seconds or last available one
116  if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) {
117  progressDelta = blockProcessTime[0].second - sample.second;
118  timeDelta = blockProcessTime[0].first - sample.first;
119  progressPerHour = (progressDelta > 0) ? progressDelta / (double)timeDelta * 1000 * 3600 : 0;
120  remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1;
121  break;
122  }
123  }
124  // show progress increase per hour
125  ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%");
126 
127  // show expected remaining time
128  if(remainingMSecs >= 0) {
129  ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0));
130  } else {
131  ui->expectedTimeLeft->setText(QObject::tr("unknown"));
132  }
133 
134  static const int MAX_SAMPLES = 5000;
135  if (blockProcessTime.count() > MAX_SAMPLES) {
136  blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES);
137  }
138  }
139 
140  // show the last block date
141  ui->newestBlockDate->setText(blockDate.toString());
142 
143  // show the percentage done according to nVerificationProgress
144  ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%");
145 
146  if (!bestHeaderDate.isValid())
147  // not syncing
148  return;
149 
150  // estimate the number of headers left based on nPowTargetSpacing
151  // and check if the gui is not aware of the best header (happens rarely)
152  int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing;
153  bool hasBestHeader = bestHeaderHeight >= count;
154 
155  // show remaining number of blocks
156  if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) {
157  ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count));
158  } else {
160  ui->expectedTimeLeft->setText(tr("Unknown…"));
161  }
162 }
163 
165  int est_headers_left = bestHeaderDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
166  ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)…").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1)));
167 }
168 
169 void ModalOverlay::UpdateHeaderPresyncLabel(int height, const QDateTime& blockDate) {
170  int est_headers_left = blockDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
171  ui->numberOfBlocksLeft->setText(tr("Unknown. Pre-syncing Headers (%1, %2%)…").arg(height).arg(QString::number(100.0 / (height + est_headers_left) * height, 'f', 1)));
172 }
173 
175 {
176  showHide(layerIsVisible, true);
177  if (!layerIsVisible)
178  userClosed = true;
179 }
180 
181 void ModalOverlay::showHide(bool hide, bool userRequested)
182 {
183  if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested))
184  return;
185 
186  Q_EMIT triggered(hide);
187 
188  if (!isVisible() && !hide)
189  setVisible(true);
190 
191  m_animation.setStartValue(QPoint(0, hide ? 0 : height()));
192  // The eventFilter() updates the endValue if it is required for QEvent::Resize.
193  m_animation.setEndValue(QPoint(0, hide ? height() : 0));
194  m_animation.start(QAbstractAnimation::KeepWhenStopped);
195  layerIsVisible = !hide;
196 
197  if (layerIsVisible) {
198  ui->closeButton->setFocus(Qt::OtherFocusReason);
199  }
200 }
201 
203 {
204  showHide(true);
205  userClosed = true;
206 }
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)
void UpdateHeaderSyncLabel()
Modal overlay to display information about the chain-sync state.
Definition: modaloverlay.h:20
Ui::ModalOverlay * ui
Definition: modaloverlay.h:47
int64_t nPowTargetSpacing
Definition: params.h:117
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.
bool layerIsVisible
Definition: modaloverlay.h:51
int bestHeaderHeight
Definition: modaloverlay.h:48
void showHide(bool hide=false, bool userRequested=false)
bool eventFilter(QObject *obj, QEvent *ev) override
QPropertyAnimation m_animation
Definition: modaloverlay.h:53
const CChainParams & Params()
Return the currently selected parameters.
static int count
QVector< QPair< qint64, double > > blockProcessTime
Definition: modaloverlay.h:50
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:787
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:93
void UpdateHeaderPresyncLabel(int height, const QDateTime &blockDate)
void closeClicked()