PIVX Core  5.6.99
P2P Digital Currency
dashboardwidget.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2022 The PIVX 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 "dashboardwidget.h"
6 #include "ui_dashboardwidget.h"
7 
8 #include "clientmodel.h"
9 #include "guiutil.h"
10 #include "optionsmodel.h"
11 #include "qtutils.h"
12 #include "sendconfirmdialog.h"
13 #include "txrow.h"
14 #include "utiltime.h"
15 #include <QGraphicsLayout>
16 #include <QList>
17 #include <QModelIndex>
18 #include <QPainter>
19 
20 #define DECORATION_SIZE 65
21 #define NUM_ITEMS 3
22 #define SHOW_EMPTY_CHART_VIEW_THRESHOLD 4000
23 #define REQUEST_LOAD_TASK 1
24 #define CHART_LOAD_MIN_TIME_INTERVAL 15
25 
27  PWidget(parent),
28  ui(new Ui::DashboardWidget)
29 {
30  ui->setupUi(this);
31 
35  txHolder,
36  this
37  );
38 
39  this->setStyleSheet(parent->styleSheet());
40  this->setContentsMargins(0,0,0,0);
41 
42  // Containers
43  setCssProperty({this, ui->left}, "container");
44  ui->left->setContentsMargins(0,0,0,0);
45 
46  // Title
47  setCssTitleScreen(ui->labelTitle);
48  setCssTitleScreen(ui->labelTitle2);
49 
50  /* Subtitle */
51  setCssSubtitleScreen(ui->labelSubtitle);
52 
53  // Staking Information
54  setCssSubtitleScreen(ui->labelMessage);
55  setCssProperty(ui->labelSquarePiv, "square-chart-piv");
56  setCssProperty(ui->labelSquareMN, "square-chart-mn");
57  setCssProperty(ui->labelPiv, "text-chart-piv");
58  setCssProperty(ui->labelMN, "text-chart-mn");
59 
60  // Staking Amount
61  QFont fontBold;
62  fontBold.setWeight(QFont::Bold);
63 
64  setCssProperty(ui->labelChart, "legend-chart");
65  setCssProperty(ui->labelAmountPiv, "text-stake-piv-disable");
66  setCssProperty(ui->labelAmountMN, "text-stake-mn-disable");
67 
68  setCssProperty({ui->pushButtonAll, ui->pushButtonMonth, ui->pushButtonYear}, "btn-check-time");
69  setCssProperty({ui->comboBoxMonths, ui->comboBoxYears}, "btn-combo-chart-selected");
70 
71  ui->comboBoxMonths->setView(new QListView());
72  ui->comboBoxMonths->setStyleSheet("selection-background-color:transparent;");
73  ui->comboBoxYears->setView(new QListView());
74  ui->comboBoxYears->setStyleSheet("selection-background-color:transparent;");
75  ui->pushButtonYear->setChecked(true);
76 
77  setCssProperty(ui->pushButtonChartArrow, "btn-chart-arrow");
78  setCssProperty(ui->pushButtonChartRight, "btn-chart-arrow-right");
79 
80 #ifdef USE_QTCHARTS
81  setCssProperty(ui->right, "container-right");
82  ui->right->setContentsMargins(20,20,20,0);
83  connect(ui->comboBoxYears, static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentTextChanged),
84  this, &DashboardWidget::onChartYearChanged);
85 #else
86  // hide charts container if not USE_QTCHARTS
87  ui->right->setVisible(false);
88 #endif // USE_QTCHARTS
89 
90  // Sort Transactions
91  SortEdit* lineEdit = new SortEdit(ui->comboBoxSort);
92  connect(lineEdit, &SortEdit::Mouse_Pressed, [this](){ui->comboBoxSort->showPopup();});
93  setSortTx(ui->comboBoxSort, lineEdit);
94  connect(ui->comboBoxSort, static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentTextChanged), this, &DashboardWidget::onSortChanged);
95 
96  // Sort type
97  SortEdit* lineEditType = new SortEdit(ui->comboBoxSortType);
98  connect(lineEditType, &SortEdit::Mouse_Pressed, [this](){ui->comboBoxSortType->showPopup();});
99  setSortTxTypeFilter(ui->comboBoxSortType, lineEditType);
100  ui->comboBoxSortType->setCurrentIndex(0);
101  connect(ui->comboBoxSortType, static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentTextChanged),
103 
104  // Transactions
105  setCssProperty(ui->listTransactions, "container");
106  ui->listTransactions->setItemDelegate(txViewDelegate);
107  ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
108  ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
109  ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);
110  ui->listTransactions->setSelectionBehavior(QAbstractItemView::SelectRows);
111  ui->listTransactions->setUniformItemSizes(true);
112 
113  // Sync Warning
114  ui->layoutWarning->setVisible(true);
115  ui->lblWarning->setText(tr("Please wait until the wallet is fully synced to see your correct balance"));
116  setCssProperty(ui->lblWarning, "text-warning");
117  setCssProperty(ui->imgWarning, "ic-warning");
118 
119  //Empty List
120  ui->emptyContainer->setVisible(false);
121  setCssProperty(ui->pushImgEmpty, "img-empty-transactions");
122  setCssProperty(ui->labelEmpty, "text-empty");
123  setCssProperty(ui->chartContainer, "container-chart");
124  setCssProperty(ui->pushImgEmptyChart, "img-empty-staking-on");
125 
126  setCssBtnSecondary(ui->btnHowTo);
127 
128  setCssProperty(ui->labelEmptyChart, "text-empty");
129  setCssSubtitleScreen(ui->labelMessageEmpty);
130 
131  // Chart State
132  ui->layoutChart->setVisible(false);
133  ui->emptyContainerChart->setVisible(true);
134  setShadow(ui->layoutShadow);
135 
136  connect(ui->listTransactions, &QListView::clicked, this, &DashboardWidget::handleTransactionClicked);
137 
138 bool hasCharts = false;
139 #ifdef USE_QTCHARTS
140  hasCharts = true;
141  isLoading = false;
142  setChartShow(YEAR);
143  connect(ui->pushButtonYear, &QPushButton::clicked, [this](){setChartShow(YEAR);});
144  connect(ui->pushButtonMonth, &QPushButton::clicked, [this](){setChartShow(MONTH);});
145  connect(ui->pushButtonAll, &QPushButton::clicked, [this](){setChartShow(ALL);});
146  if (window)
147  connect(window, &PIVXGUI::windowResizeEvent, this, &DashboardWidget::windowResizeEvent);
148 #endif
149 
150  if (hasCharts) {
151  ui->labelEmptyChart->setText(tr("You have no staking rewards"));
152  } else {
153  ui->labelEmptyChart->setText(tr("No charts library"));
154  }
155 }
156 
157 void DashboardWidget::handleTransactionClicked(const QModelIndex &index)
158 {
159  ui->listTransactions->setCurrentIndex(index);
160  QModelIndex rIndex = filter->mapToSource(index);
161 
162  window->showHide(true);
163  TxDetailDialog *dialog = new TxDetailDialog(window, false);
164  dialog->setData(walletModel, rIndex);
165  openDialogWithOpaqueBackgroundY(dialog, window, 3, 17);
166 
167  // Back to regular status
168  ui->listTransactions->scrollTo(index);
169  ui->listTransactions->clearSelection();
170  ui->listTransactions->setFocus();
171  dialog->deleteLater();
172 }
173 
175 {
178  // Set up transaction list
180  filter->setDynamicSortFilter(true);
181  filter->setSortCaseSensitivity(Qt::CaseInsensitive);
182  filter->setFilterCaseSensitivity(Qt::CaseInsensitive);
183  filter->setSortRole(Qt::EditRole);
184 
185  // Read filter settings
186  QSettings settings;
187  quint32 filterByType = settings.value("transactionType", TransactionFilterProxy::ALL_TYPES).toInt();
188  int filterIndex = ui->comboBoxSortType->findData(filterByType); // Find index
189  filterByType = (filterIndex == -1) ? TransactionFilterProxy::ALL_TYPES : filterByType;
190  filter->setTypeFilter(filterByType); // Set filter
191  ui->comboBoxSortType->setCurrentIndex(filterIndex); // Set item in ComboBox
192  // Read sort settings
193  changeSort(settings.value("transactionSort", SortTx::DATE_DESC).toInt());
194 
195  filter->setSourceModel(txModel);
197  ui->listTransactions->setModel(filter);
198  ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
199 
200  if (txModel->size() == 0) {
201  ui->emptyContainer->setVisible(true);
202  ui->listTransactions->setVisible(false);
203  ui->comboBoxSortType->setVisible(false);
204  ui->comboBoxSort->setVisible(false);
205  }
206 
207  connect(ui->pushImgEmpty, &QPushButton::clicked, [this](){window->openFAQ();});
208  connect(ui->btnHowTo, &QPushButton::clicked, [this](){window->openFAQ();});
210 
211  // Notification pop-up for new transaction
212  connect(txModel, &TransactionTableModel::rowsInserted, this, &DashboardWidget::processNewTransaction);
213 #ifdef USE_QTCHARTS
214  onHideChartsChanged(walletModel->getOptionsModel()->isHideCharts());
216  &DashboardWidget::onHideChartsChanged);
217 #endif
218  }
219  // update the display unit, to not use the default ("PIV")
221 }
222 
223 void DashboardWidget::onTxArrived(const QString& hash, const bool isCoinStake, const bool isMNReward, const bool isCSAnyType)
224 {
225  showList();
226  if (!isVisible()) return;
227 #ifdef USE_QTCHARTS
228  if (isCoinStake || isMNReward) {
229  // Update value if this is our first stake/reward
230  if (!hasStakes && stakesFilter)
231  hasStakes = stakesFilter->rowCount() > 0;
232  tryChartRefresh();
233  }
234 #endif
235 }
236 
238 {
239  if (txModel->size() == 0) {
240  ui->emptyContainer->setVisible(true);
241  ui->listTransactions->setVisible(false);
242  ui->comboBoxSortType->setVisible(false);
243  ui->comboBoxSort->setVisible(false);
244  } else {
245  ui->emptyContainer->setVisible(false);
246  ui->listTransactions->setVisible(true);
247  ui->comboBoxSortType->setVisible(true);
248  ui->comboBoxSort->setVisible(true);
249  }
250 }
251 
253 {
257  ui->listTransactions->update();
258  }
259 }
260 
261 void DashboardWidget::onSortChanged(const QString& value)
262 {
263  if (!filter) return;
264 
265  if (!value.isNull()) {
266  changeSort(ui->comboBoxSort->currentIndex());
267  } else {
269  }
270 }
271 
272 void DashboardWidget::changeSort(int nSortIndex)
273 {
274  int nColumnIndex = TransactionTableModel::Date;
275  Qt::SortOrder order = Qt::DescendingOrder;
276 
277  switch (nSortIndex) {
278  case SortTx::DATE_DESC:
279  {
280  nColumnIndex = TransactionTableModel::Date;
281  break;
282  }
283  case SortTx::DATE_ASC:
284  {
285  nColumnIndex = TransactionTableModel::Date;
286  order = Qt::AscendingOrder;
287  break;
288  }
289  case SortTx::AMOUNT_DESC:
290  {
291  nColumnIndex = TransactionTableModel::Amount;
292  break;
293  }
294  case SortTx::AMOUNT_ASC:
295  {
296  nColumnIndex = TransactionTableModel::Amount;
297  order = Qt::AscendingOrder;
298  break;
299  }
300  }
301 
302  ui->comboBoxSort->setCurrentIndex(nSortIndex);
303  filter->sort(nColumnIndex, order);
304 
305  // Store settings
306  QSettings settings;
307  settings.setValue("transactionSort", nSortIndex);
308 }
309 
310 void DashboardWidget::onSortTypeChanged(const QString& value)
311 {
312  if (!filter) return;
313  int filterIndex = ui->comboBoxSortType->currentIndex();
314  int filterByType = ui->comboBoxSortType->itemData(filterIndex).toInt();
315 
316  filter->setTypeFilter(filterByType);
317  ui->listTransactions->update();
318 
319  if (filter->rowCount() == 0) {
320  ui->emptyContainer->setVisible(true);
321  ui->listTransactions->setVisible(false);
322  } else {
323  showList();
324  }
325 
326  // Store settings
327  QSettings settings;
328  settings.setValue("transactionType", filterByType);
329 }
330 
332 {
333  if (this->isSync != sync) {
334  this->isSync = sync;
335  ui->layoutWarning->setVisible(!this->isSync);
336 #ifdef USE_QTCHARTS
337  if (!isVisible()) return;
338  tryChartRefresh();
339 #endif
340  }
341 }
342 
343 void DashboardWidget::changeTheme(bool isLightTheme, QString& theme)
344 {
345  static_cast<TxViewHolder*>(this->txViewDelegate->getRowFactory())->isLightTheme = isLightTheme;
346 #ifdef USE_QTCHARTS
347  if (chart) this->changeChartColors();
348 #endif
349 }
350 
351 #ifdef USE_QTCHARTS
352 
353 void DashboardWidget::tryChartRefresh()
354 {
355  if (!fShowCharts)
356  return;
357  if (hasStakes) {
358  // First check that everything was loaded properly.
359  if (!chart) {
360  loadChart();
361  } else {
362  // Check for min update time to not reload the UI so often if the node is syncing.
363  int64_t now = GetTime();
364  int chartLoadIntervalTime = CHART_LOAD_MIN_TIME_INTERVAL;
365  if (clientModel->inInitialBlockDownload()) chartLoadIntervalTime *= 6; // 90 seconds update
366  if (lastRefreshTime + chartLoadIntervalTime < now) {
367  lastRefreshTime = now;
368  refreshChart();
369  }
370  }
371  }
372 }
373 
374 void DashboardWidget::setChartShow(ChartShowType type)
375 {
376  this->chartShow = type;
377  if (chartShow == MONTH) {
378  ui->containerChartArrow->setVisible(true);
379  } else {
380  ui->containerChartArrow->setVisible(false);
381  }
382  if (isChartInitialized) refreshChart();
383 }
384 
385 const QStringList monthsNames = {QObject::tr("Jan"), QObject::tr("Feb"), QObject::tr("Mar"), QObject::tr("Apr"),
386  QObject::tr("May"), QObject::tr("Jun"), QObject::tr("Jul"), QObject::tr("Aug"),
387  QObject::tr("Sep"), QObject::tr("Oct"), QObject::tr("Nov"), QObject::tr("Dec")};
388 
390 {
391  if (hasStakes) {
392  if (!chart) {
393  showHideEmptyChart(false, false);
394  initChart();
395  QDate currentDate = QDate::currentDate();
396  monthFilter = currentDate.month();
397  yearFilter = currentDate.year();
398  for (int i = 1; i < 13; ++i) ui->comboBoxMonths->addItem(QString(monthsNames[i-1]), QVariant(i));
399  ui->comboBoxMonths->setCurrentIndex(monthFilter - 1);
400  connect(ui->comboBoxMonths, static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentTextChanged),
401  this, &DashboardWidget::onChartMonthChanged);
402  connect(ui->pushButtonChartArrow, &QPushButton::clicked, [this](){ onChartArrowClicked(true); });
403  connect(ui->pushButtonChartRight, &QPushButton::clicked, [this](){ onChartArrowClicked(false); });
404  }
405  refreshChart();
406  changeChartColors();
407  } else {
408  showHideEmptyChart(true, false);
409  }
410 }
411 
412 void DashboardWidget::showHideEmptyChart(bool showEmpty, bool loading, bool forceView)
413 {
414  if ((stakesFilter && stakesFilter->rowCount() > SHOW_EMPTY_CHART_VIEW_THRESHOLD) || forceView) {
415  ui->layoutChart->setVisible(!showEmpty);
416  ui->emptyContainerChart->setVisible(showEmpty);
417  }
418  // Enable/Disable sort buttons
419  bool invLoading = !loading;
420  ui->comboBoxMonths->setEnabled(invLoading);
421  ui->comboBoxYears->setEnabled(invLoading);
422  ui->pushButtonMonth->setEnabled(invLoading);
423  ui->pushButtonAll->setEnabled(invLoading);
424  ui->pushButtonYear->setEnabled(invLoading);
425  ui->labelEmptyChart->setText(loading ? tr("Loading chart..") : tr("You have no staking rewards"));
426 }
427 
428 void DashboardWidget::initChart()
429 {
430  chart = new QChart();
431  axisX = new QBarCategoryAxis();
432  axisY = new QValueAxis();
433 
434  // Chart style
435  chart->legend()->setVisible(false);
436  chart->legend()->setAlignment(Qt::AlignTop);
437  chart->layout()->setContentsMargins(0, 0, 0, 0);
438  chart->setMargins({0, 0, 0, 0});
439  chart->setBackgroundRoundness(0);
440  // Axis
441  chart->addAxis(axisX, Qt::AlignBottom);
442  chart->addAxis(axisY, Qt::AlignRight);
443  chart->setAnimationOptions(QChart::SeriesAnimations);
444 
445  chartView = new QChartView(chart);
446  chartView->setRenderHint(QPainter::Antialiasing);
447  chartView->setRubberBand( QChartView::HorizontalRubberBand );
448  chartView->setContentsMargins(0,0,0,0);
449 
450  QHBoxLayout *baseScreensContainer = new QHBoxLayout(this);
451  baseScreensContainer->setMargin(0);
452  baseScreensContainer->addWidget(chartView);
453  ui->chartContainer->setLayout(baseScreensContainer);
454  ui->chartContainer->setContentsMargins(0,0,0,0);
455  setCssProperty(ui->chartContainer, "container-chart");
456 }
457 
458 void DashboardWidget::changeChartColors()
459 {
460  QColor gridLineColorX;
461  QColor linePenColorY;
462  QColor backgroundColor;
463  QColor gridY;
464  if (isLightTheme()) {
465  gridLineColorX = QColor(255,255,255);
466  linePenColorY = gridLineColorX;
467  backgroundColor = linePenColorY;
468  axisY->setGridLineColor(QColor("#1a000000"));
469  } else {
470  gridY = QColor("#40ffffff");
471  axisY->setGridLineColor(gridY);
472  gridLineColorX = QColor(15,11,22);
473  linePenColorY = gridLineColorX;
474  backgroundColor = linePenColorY;
475  }
476 
477  axisX->setGridLineColor(gridLineColorX);
478  axisY->setLinePenColor(linePenColorY);
479  chart->setBackgroundBrush(QBrush(backgroundColor));
480  if (set0) set0->setBorderColor(gridLineColorX);
481  if (set1) set1->setBorderColor(gridLineColorX);
482 }
483 
484 void DashboardWidget::updateStakeFilter()
485 {
486  if (!stakesFilter) return;
487  if (chartShow != ALL) {
488  bool filterByMonth = false;
489  if (monthFilter != 0 && chartShow == MONTH) {
490  filterByMonth = true;
491  }
492  if (yearFilter != 0) {
493  if (filterByMonth) {
494  QDate monthFirst = QDate(yearFilter, monthFilter, 1);
495  QDate monthLast = QDate(yearFilter, monthFilter, monthFirst.daysInMonth());
496  stakesFilter->setDateRange(
497  QDateTime(monthFirst),
498  QDateTime(monthLast).addSecs(86399) // last second of the day
499  );
500  } else {
501  stakesFilter->setDateRange(
502  QDateTime(QDate(yearFilter, 1, 1)),
503  QDateTime(QDate(yearFilter, 12, 31))
504  );
505  }
506  } else if (filterByMonth) {
507  QDate currentDate = QDate::currentDate();
508  QDate monthFirst = QDate(currentDate.year(), monthFilter, 1);
509  stakesFilter->setDateRange(
510  QDateTime(monthFirst),
511  QDateTime(QDate(currentDate.year(), monthFilter, monthFirst.daysInMonth()))
512  );
513  ui->comboBoxYears->setCurrentText(QString::number(currentDate.year()));
514  } else {
515  stakesFilter->clearDateRange();
516  }
517  } else {
518  stakesFilter->clearDateRange();
519  }
520 }
521 
522 // pair PIV, MN Reward
523 QMap<int, std::pair<qint64, qint64>> DashboardWidget::getAmountBy()
524 {
525  if (filterUpdateNeeded) {
526  filterUpdateNeeded = false;
527  updateStakeFilter();
528  }
529  const int size = stakesFilter->rowCount();
530  QMap<int, std::pair<qint64, qint64>> amountBy;
531  // Get all the stakes
532  for (int i = 0; i < size; ++i) {
533  QModelIndex modelIndex = stakesFilter->index(i, TransactionTableModel::ToAddress);
534  qint64 amount = llabs(modelIndex.data(TransactionTableModel::AmountRole).toLongLong());
535  QDate date = modelIndex.data(TransactionTableModel::DateRole).toDateTime().date();
536  bool isPiv = modelIndex.data(TransactionTableModel::TypeRole).toInt() != TransactionRecord::MNReward;
537 
538  int time = 0;
539  switch (chartShow) {
540  case YEAR: {
541  time = date.month();
542  break;
543  }
544  case ALL: {
545  time = date.year();
546  break;
547  }
548  case MONTH: {
549  time = date.day();
550  break;
551  }
552  default:
553  inform(tr("Error loading chart, invalid show option"));
554  return amountBy;
555  }
556  if (amountBy.contains(time)) {
557  if (isPiv) {
558  amountBy[time].first += amount;
559  } else
560  amountBy[time].second += amount;
561  } else {
562  if (isPiv) {
563  amountBy[time] = std::make_pair(amount, 0);
564  } else {
565  amountBy[time] = std::make_pair(0, amount);
566  hasMNRewards = true;
567  }
568  }
569  }
570  return amountBy;
571 }
572 
573 bool DashboardWidget::loadChartData(bool withMonthNames)
574 {
575  if (chartData) {
576  delete chartData;
577  chartData = nullptr;
578  }
579 
580  chartData = new ChartData();
581  chartData->amountsByCache = getAmountBy(); // pair PIV, MN Reward
582 
583  std::pair<int,int> range = getChartRange(chartData->amountsByCache);
584  if (range.first == 0 && range.second == 0) {
585  // Problem loading the chart.
586  return false;
587  }
588 
589  bool isOrderedByMonth = chartShow == MONTH;
590  int daysInMonth = QDate(yearFilter, monthFilter, 1).daysInMonth();
591 
592  for (int j = range.first; j < range.second; j++) {
593  int num = (isOrderedByMonth && j > daysInMonth) ? (j % daysInMonth) : j;
594  qreal piv = 0;
595  qreal mn = 0;
596  if (chartData->amountsByCache.contains(num)) {
597  std::pair <qint64, qint64> pair = chartData->amountsByCache[num];
598  piv = (pair.first != 0) ? pair.first / 100000000 : 0;
599  mn = (pair.second != 0) ? pair.second / 100000000 : 0;
600  chartData->totalPiv += pair.first;
601  chartData->totalMN += pair.second;
602  }
603 
604  chartData->xLabels << ((withMonthNames) ? monthsNames[num - 1] : QString::number(num));
605 
606  chartData->valuesPiv.append(piv);
607  chartData->valuesMN.append(mn);
608 
609  int max = std::max(piv, mn);
610  if (max > chartData->maxValue) {
611  chartData->maxValue = max;
612  }
613  }
614 
615  return true;
616 }
617 
618 void DashboardWidget::onChartYearChanged(const QString& yearStr)
619 {
620  if (isChartInitialized) {
621  int newYear = yearStr.toInt();
622  if (newYear != yearFilter) {
623  yearFilter = newYear;
624  filterUpdateNeeded = true;
625  refreshChart();
626  }
627  }
628 }
629 
630 void DashboardWidget::onChartMonthChanged(const QString& monthStr)
631 {
632  if (isChartInitialized) {
633  int newMonth = ui->comboBoxMonths->currentData().toInt();
634  if (newMonth != monthFilter) {
635  monthFilter = newMonth;
636  filterUpdateNeeded = true;
637  refreshChart();
638 #ifndef Q_OS_MAC
639  // quick hack to re paint the chart view.
640  chart->removeSeries(series);
641  chart->addSeries(series);
642 #endif
643  }
644  }
645 }
646 
647 bool DashboardWidget::refreshChart()
648 {
649  if (isLoading) return false;
650  isLoading = true;
651  isChartMin = width() < 1300;
652  isChartInitialized = false;
653  showHideEmptyChart(true, true);
654  return execute(REQUEST_LOAD_TASK);
655 }
656 
657 void DashboardWidget::onChartRefreshed()
658 {
659  if (chart) {
660  if (series) {
661  series->clear();
662  series->detachAxis(axisX);
663  series->detachAxis(axisY);
664  }
665  axisX->clear();
666  }
667  // init sets
668  set0 = new QBarSet(tr("Stakes"));
669  set1 = new QBarSet(tr("MN"));
670  set0->setColor(QColor(92,75,125));
671  set1->setColor(QColor(176,136,255));
672 
673  if (!series) {
674  series = new QBarSeries();
675  chart->addSeries(series);
676  }
677  series->attachAxis(axisX);
678  series->attachAxis(axisY);
679 
680  set0->append(chartData->valuesPiv);
681  set1->append(chartData->valuesMN);
682 
683  // Total
685  if (chartData->totalPiv > 0 || chartData->totalMN > 0) {
686  setCssProperty(ui->labelAmountPiv, "text-stake-piv");
687  setCssProperty(ui->labelAmountMN, "text-stake-mn");
688  } else {
689  setCssProperty(ui->labelAmountPiv, "text-stake-piv-disable");
690  setCssProperty(ui->labelAmountMN, "text-stake-mn-disable");
691  }
692  forceUpdateStyle({ui->labelAmountPiv, ui->labelAmountMN});
693  ui->labelAmountPiv->setText(GUIUtil::formatBalance(chartData->totalPiv, nDisplayUnit));
694  ui->labelAmountMN->setText(GUIUtil::formatBalance(chartData->totalMN, nDisplayUnit));
695 
696  series->append(set0);
697  if (hasMNRewards)
698  series->append(set1);
699 
700  // bar width
701  if (chartShow == YEAR)
702  series->setBarWidth(0.8);
703  else {
704  series->setBarWidth(0.3);
705  }
706  axisX->append(chartData->xLabels);
707  axisY->setRange(0, chartData->maxValue);
708 
709  // Controllers
710  switch (chartShow) {
711  case ALL: {
712  ui->container_chart_dropboxes->setVisible(false);
713  break;
714  }
715  case YEAR: {
716  ui->container_chart_dropboxes->setVisible(true);
717  ui->containerBoxMonths->setVisible(false);
718  break;
719  }
720  case MONTH: {
721  ui->container_chart_dropboxes->setVisible(true);
722  ui->containerBoxMonths->setVisible(true);
723  break;
724  }
725  default: break;
726  }
727 
728  // Refresh years filter, first address created is the start
729  int yearStart = QDateTime::fromTime_t(static_cast<uint>(walletModel->getCreationTime())).date().year();
730  int currentYear = QDateTime::currentDateTime().date().year();
731 
732  QString selection;
733  if (ui->comboBoxYears->count() > 0) {
734  selection = ui->comboBoxYears->currentText();
735  isChartInitialized = false;
736  }
737  ui->comboBoxYears->clear();
738  if (yearStart == currentYear) {
739  ui->comboBoxYears->addItem(QString::number(currentYear));
740  } else {
741  for (int i = yearStart; i < (currentYear + 1); ++i)ui->comboBoxYears->addItem(QString::number(i));
742  }
743 
744  if (!selection.isEmpty()) {
745  ui->comboBoxYears->setCurrentText(selection);
746  isChartInitialized = true;
747  } else {
748  ui->comboBoxYears->setCurrentText(QString::number(currentYear));
749  }
750 
751  // back to normal
752  isChartInitialized = true;
753  showHideEmptyChart(false, false, true);
754  isLoading = false;
755 }
756 
757 std::pair<int, int> DashboardWidget::getChartRange(const QMap<int, std::pair<qint64, qint64>>& amountsBy)
758 {
759  switch (chartShow) {
760  case YEAR:
761  return std::make_pair(1, 13);
762  case ALL: {
763  QList<int> keys = amountsBy.uniqueKeys();
764  if (keys.isEmpty()) {
765  // This should never happen, ALL means from the beginning of time and if this is called then it must have at least one stake..
766  inform(tr("Error loading chart, invalid data"));
767  return std::make_pair(0, 0);
768  }
769  std::sort(keys.begin(), keys.end());
770  return std::make_pair(keys.first(), keys.last() + 1);
771  }
772  case MONTH:
773  return std::make_pair(dayStart, dayStart + 9);
774  default:
775  inform(tr("Error loading chart, invalid show option"));
776  return std::make_pair(0, 0);
777  }
778 }
779 
780 void DashboardWidget::updateAxisX(const QStringList* args)
781 {
782  axisX->clear();
783  QStringList months;
784  std::pair<int,int> range = getChartRange(chartData->amountsByCache);
785  if (args) {
786  months = *args;
787  } else {
788  for (int i = range.first; i < range.second; i++) months << QString::number(i);
789  }
790  axisX->append(months);
791 }
792 
793 void DashboardWidget::onChartArrowClicked(bool goLeft)
794 {
795  bool updateMonth = false;
796  bool updateYear = false;
797  int dataenddate = getChartRange(chartData->amountsByCache).second;
798  QDate currentDate = QDate::currentDate();
799  if (goLeft) {
800  dayStart--;
801  if (dayStart == 0) {
802  updateMonth = true;
803  if (monthFilter == 1) {
804  // Prev year
805  monthFilter = 12;
806  yearFilter--;
807  updateYear = true;
808  } else {
809  monthFilter--; // Prev month
810  }
811  dayStart = QDate(yearFilter, monthFilter, 1).daysInMonth();
812  }
813  } else {
814  int dayInMonth = QDate(yearFilter, monthFilter, dayStart).daysInMonth();
815  dayStart++;
816  if (dayStart > dayInMonth) {
817  dayStart = 1;
818  updateMonth = true;
819  if (monthFilter == 12) {
820  // Next year
821  monthFilter = 1;
822  yearFilter++;
823  updateYear = true;
824  } else {
825  monthFilter++; // Next month
826  }
827  }
828  }
829  filterUpdateNeeded = true;
830  refreshChart();
831  //Check if data end day is current date and monthfilter is current month
832  bool fEndDayisCurrent = dataenddate == currentDate.day() && monthFilter == currentDate.month();
833 
834  if (updateMonth)
835  ui->comboBoxMonths->setCurrentIndex(monthFilter - 1);
836 
837  if (updateYear)
838  ui->comboBoxYears->setCurrentText(QString::number(yearFilter));
839 
840  // enable/disable the pushButtonChartRight.
841  ui->pushButtonChartRight->setEnabled(!fEndDayisCurrent);
842 
843 
844 }
845 
846 void DashboardWidget::windowResizeEvent(QResizeEvent* event)
847 {
848  if (hasStakes && axisX) {
849  if (width() > 1300) {
850  if (isChartMin) {
851  isChartMin = false;
852  switch (chartShow) {
853  case YEAR: {
854  updateAxisX(&monthsNames);
855  break;
856  }
857  case ALL: break;
858  case MONTH: {
859  updateAxisX();
860  break;
861  }
862  default:
863  inform(tr("Error loading chart, invalid show option"));
864  return;
865  }
866  chartView->repaint();
867  }
868  } else {
869  if (!isChartMin) {
870  updateAxisX();
871  isChartMin = true;
872  }
873  }
874  }
875 }
876 
877 void DashboardWidget::onHideChartsChanged(bool fHide)
878 {
879  fShowCharts = !fHide;
880 
881  if (fShowCharts) {
882  if (!stakesFilter) {
883  stakesFilter = new TransactionFilterProxy(this);
884  stakesFilter->setDynamicSortFilter(false);
885  stakesFilter->setSortCaseSensitivity(Qt::CaseInsensitive);
886  stakesFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
887  stakesFilter->setTypeFilter(TransactionFilterProxy::TYPE(TransactionRecord::StakeMint) |
892  }
893  stakesFilter->setSourceModel(txModel);
894  hasStakes = stakesFilter->rowCount() > 0;
895  filterUpdateNeeded = true;
896  } else {
897  if (stakesFilter) {
898  stakesFilter->setSourceModel(nullptr);
899  }
900  }
901 
902  // Hide charts if requested
903  ui->right->setVisible(fShowCharts);
904  if (fShowCharts) tryChartRefresh();
905 }
906 
907 #endif
908 
909 void DashboardWidget::run(int type)
910 {
911 #ifdef USE_QTCHARTS
912  if (type == REQUEST_LOAD_TASK) {
913  bool withMonthNames = !isChartMin && (chartShow == YEAR);
914  if (loadChartData(withMonthNames))
915  QMetaObject::invokeMethod(this, "onChartRefreshed", Qt::QueuedConnection);
916  }
917 #endif
918 }
919 void DashboardWidget::onError(QString error, int type)
920 {
921  inform(tr("Error loading chart: %1").arg(error));
922 }
923 
924 void DashboardWidget::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
925 {
926  // Prevent notifications-spam when initial block download is in progress
928  return;
929 
931  return;
932 
933  QString date = txModel->index(start, TransactionTableModel::Date, parent).data().toString();
934  qint64 amount = txModel->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
935  QString type = txModel->index(start, TransactionTableModel::Type, parent).data().toString();
936  QString address = txModel->index(start, TransactionTableModel::ToAddress, parent).data().toString();
937 
938  Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address);
939 }
940 
942 {
943 #ifdef USE_QTCHARTS
944  delete chart;
945 #endif
946  delete ui;
947 }
bool inInitialBlockDownload() const
Return true if core is doing initial block download.
void handleTransactionClicked(const QModelIndex &index)
void changeTheme(bool isLightTheme, QString &theme) override
void incomingTransaction(const QString &date, int unit, const CAmount &amount, const QString &type, const QString &address)
Notify that a new transaction appeared.
Ui::DashboardWidget * ui
void onError(QString error, int type) override
TxViewHolder * txHolder
FurAbstractListItemDelegate * txViewDelegate
TransactionFilterProxy * filter
void processNewTransaction(const QModelIndex &parent, int start, int)
Show incoming transaction notification for new transactions.
void run(int type) override
TransactionTableModel * txModel
void changeSort(int nSortIndex)
void loadWalletModel() override
void walletSynced(bool isSync)
DashboardWidget(PIVXGUI *_window)
void onSortChanged(const QString &)
void onSortTypeChanged(const QString &value)
void onTxArrived(const QString &hash, const bool isCoinStake, const bool isMNReward, const bool isCSAnyType)
void hideChartsChanged(bool)
int getDisplayUnit()
Definition: optionsmodel.h:74
bool isHideCharts()
Definition: optionsmodel.h:71
PIVX GUI main class.
Definition: pivxgui.h:46
void showHide(bool show)
Definition: pivxgui.cpp:579
void windowResizeEvent(QResizeEvent *event)
PIVXGUI * window
Definition: pwidget.h:59
void inform(const QString &message)
Definition: pwidget.cpp:45
WalletModel * walletModel
Definition: pwidget.h:61
ClientModel * clientModel
Definition: pwidget.h:60
bool execute(int type, std::unique_ptr< WalletModel::UnlockContext > pctx=nullptr)
Definition: pwidget.cpp:94
void Mouse_Pressed()
Filter the transaction list according to pre-specified rules.
static const quint32 ALL_TYPES
Type filter bit field (all types)
int rowCount(const QModelIndex &parent=QModelIndex()) const
static quint32 TYPE(int type)
void setTypeFilter(quint32 modes)
@ TypeRole
Type of transaction.
@ DateRole
Date and time this transaction was created.
@ AmountRole
Net amount of transaction.
void txArrived(const QString &hash, const bool isCoinStake, const bool isMNReward, const bool isCSAnyType)
bool processingQueuedTransactions() const
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void setData(WalletModel *model, WalletModelTransaction *tx)
void setFilter(TransactionFilterProxy *_filter)
Definition: txviewholder.h:38
void setDisplayUnit(int displayUnit)
Definition: txviewholder.h:34
OptionsModel * getOptionsModel()
int64_t getCreationTime() const
TransactionTableModel * getTransactionTableModel()
#define REQUEST_LOAD_TASK
#define SHOW_EMPTY_CHART_VIEW_THRESHOLD
#define DECORATION_SIZE
#define CHART_LOAD_MIN_TIME_INTERVAL
#define NUM_ITEMS
ChartShowType
@ MONTH
@ ALL
@ YEAR
@ AMOUNT_ASC
@ DATE_DESC
@ DATE_ASC
@ AMOUNT_DESC
QString formatBalance(CAmount amount, int nDisplayUnit, bool isZpiv)
Definition: guiutil.cpp:119
QSettings * settings
Definition: qtutils.cpp:197
void forceUpdateStyle(QWidget *widget, bool forceUpdate)
Definition: qtutils.cpp:348
bool isLightTheme()
Definition: qtutils.cpp:210
bool openDialogWithOpaqueBackgroundY(QDialog *widget, PIVXGUI *gui, double posX, int posY, bool hideOpaqueBackground)
Definition: qtutils.cpp:59
void setCssTitleScreen(QLabel *label)
Definition: qtutils.cpp:324
void setSortTxTypeFilter(QComboBox *filter, SortEdit *lineEditType)
Definition: qtutils.cpp:155
void setShadow(QWidget *edit)
Definition: qtutils.cpp:292
void setCssProperty(std::initializer_list< QWidget * > args, const QString &value)
Definition: qtutils.cpp:334
void setCssSubtitleScreen(QWidget *wid)
Definition: qtutils.cpp:329
void setSortTx(QComboBox *filter, SortEdit *lineEdit)
Definition: qtutils.cpp:145
void setCssBtnSecondary(QPushButton *btn, bool forceUpdate)
Definition: qtutils.cpp:307
bool error(const char *fmt, const Args &... args)
Definition: system.h:77
int64_t GetTime()
DEPRECATED Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
Definition: utiltime.cpp:27