mempoolstats.cpp raw
1 // Copyright (c) 2016 The Limenka 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/mempoolstats.h>
6 #include <qt/forms/ui_mempoolstats.h>
7
8 #include <qt/clientmodel.h>
9 #include <qt/guiutil.h>
10 #include <stats/stats.h>
11
12 #include <math.h>
13
14 #include <QApplication>
15 #include <QColor>
16 #include <QPalette>
17
18 static const char *LABEL_FONT = "Arial";
19 static int LABEL_TITLE_SIZE = 22;
20 static int LABEL_KV_SIZE = 12;
21
22 static const int TEN_MINS = 600;
23 static const int ONE_HOUR = 3600;
24 static const int ONE_DAY = ONE_HOUR*24;
25
26 static const int LABEL_LEFT_SIZE = 30;
27 static const int LABEL_RIGHT_SIZE = 30;
28 static const int GRAPH_PADDING_LEFT = 30+LABEL_LEFT_SIZE;
29 static const int GRAPH_PADDING_RIGHT = 30+LABEL_RIGHT_SIZE;
30 static const int GRAPH_PADDING_TOP = 10;
31 static const int GRAPH_PADDING_TOP_LABEL = 150;
32 static const int GRAPH_PADDING_BOTTOM = 50;
33 static const int LABEL_HEIGHT = 15;
34
35 static const MempoolStats::ThemeColors LIGHT_THEME_COLORS = {
36 .orange = QColor(216, 92, 1, 250),
37 .green = QColor(0, 125, 50, 250),
38 .blue = QColor(2, 61, 204, 250),
39 };
40
41 static const MempoolStats::ThemeColors DARK_THEME_COLORS = {
42 .orange = QColor(247, 147, 26, 250),
43 .green = QColor(69, 222, 181, 250),
44 .blue = QColor(137, 170, 255, 250),
45 };
46
47 void ClickableTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
48 {
49 Q_EMIT objectClicked(this);
50 }
51
52 void ClickableTextItem::setEnabled(bool state)
53 {
54 if (state)
55 // Use system default text color for active item
56 setDefaultTextColor(QApplication::palette().color(QPalette::WindowText));
57 else
58 // Use system disabled color
59 setDefaultTextColor(QApplication::palette().color(QPalette::Disabled, QPalette::WindowText));
60 }
61
62 MempoolStats::MempoolStats(QWidget *parent) :
63 QWidget(parent, Qt::Window),
64 timeFilter(TEN_MINS),
65 ui(new Ui::MempoolStats)
66 {
67 ui->setupUi(this);
68 if (parent) {
69 parent->installEventFilter(this);
70 raise();
71 }
72
73 // autoadjust font size
74 QGraphicsTextItem testText("jY"); //screendesign expected 27.5 pixel in width for this string
75 testText.setFont(QFont(LABEL_FONT, LABEL_TITLE_SIZE, QFont::Light));
76 LABEL_TITLE_SIZE *= 27.5/testText.boundingRect().width();
77 LABEL_KV_SIZE *= 27.5/testText.boundingRect().width();
78
79 scene = new QGraphicsScene();
80 ui->graphicsView->setScene(scene);
81 ui->graphicsView->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
82
83 updateThemeColors();
84
85 if (clientModel)
86 drawChart();
87 }
88
89 void MempoolStats::setClientModel(ClientModel *model)
90 {
91 clientModel = model;
92
93 if (model)
94 connect(model, SIGNAL(mempoolStatsDidUpdate()), this, SLOT(drawChart()));
95 }
96
97 void MempoolStats::drawChart()
98 {
99 if (!(isVisible() && clientModel))
100 return;
101
102 if (!titleItem)
103 {
104 // create labels (only once)
105 titleItem = scene->addText(tr("Mempool Statistics"));
106 titleItem->setFont(QFont(LABEL_FONT, LABEL_TITLE_SIZE, QFont::Light));
107 titleLine = scene->addLine(0,0,100,100);
108 titleLine->setPen(QPen(QColor(100,100,100, 200), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
109
110 cbShowMemUsage = new QCheckBox("Dynamic Memory Usage");
111 cbShowMemUsage->setChecked(true);
112 dynMemUsageSwitch = scene->addWidget(cbShowMemUsage);
113 connect(cbShowMemUsage, SIGNAL(stateChanged(int)), this, SLOT(drawChart()));
114 cbShowMemUsage->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
115 dynMemUsageValueItem = scene->addText("N/A");
116 dynMemUsageValueItem->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Bold));
117
118 cbShowNumTxns = new QCheckBox("Amount of Transactions");
119 cbShowNumTxns->setChecked(true);
120 txCountSwitch = scene->addWidget(cbShowNumTxns);
121 scene->addItem(txCountSwitch);
122 connect(cbShowNumTxns, SIGNAL(stateChanged(int)), this, SLOT(drawChart()));
123 cbShowNumTxns->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
124 txCountValueItem = scene->addText("N/A");
125 txCountValueItem->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Bold));
126
127 cbShowMinFeerate = new QCheckBox("Min relay fee per kB");
128 cbShowMinFeerate->setChecked(false);
129 minFeeSwitch = scene->addWidget(cbShowMinFeerate);
130 scene->addItem(minFeeSwitch);
131 connect(cbShowMinFeerate, SIGNAL(stateChanged(int)), this, SLOT(drawChart()));
132 cbShowMinFeerate->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
133 minFeeValueItem = scene->addText(tr("N/A"));
134 minFeeValueItem->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Bold));
135
136 noDataItem = scene->addText(tr("No Data available"));
137 noDataItem->setFont(QFont(LABEL_FONT, LABEL_TITLE_SIZE, QFont::Light));
138 noDataItem->setDefaultTextColor(QColor(100,100,100, 200));
139
140 last10MinLabel = new ClickableTextItem(); last10MinLabel->setPlainText(tr("Last 10 min"));
141 scene->addItem(last10MinLabel);
142 connect(last10MinLabel, SIGNAL(objectClicked(QGraphicsItem*)), this, SLOT(objectClicked(QGraphicsItem*)));
143 last10MinLabel->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
144 lastHourLabel = new ClickableTextItem(); lastHourLabel->setPlainText(tr("Last Hour"));
145 scene->addItem(lastHourLabel);
146 connect(lastHourLabel, SIGNAL(objectClicked(QGraphicsItem*)), this, SLOT(objectClicked(QGraphicsItem*)));
147 lastHourLabel->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
148 lastDayLabel = new ClickableTextItem(); lastDayLabel->setPlainText(tr("Last Day"));
149 scene->addItem(lastDayLabel);
150 connect(lastDayLabel, SIGNAL(objectClicked(QGraphicsItem*)), this, SLOT(objectClicked(QGraphicsItem*)));
151 lastDayLabel->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
152 allDataLabel = new ClickableTextItem(); allDataLabel->setPlainText(tr("All Data"));
153 scene->addItem(allDataLabel);
154 connect(allDataLabel, SIGNAL(objectClicked(QGraphicsItem*)), this, SLOT(objectClicked(QGraphicsItem*)));
155 allDataLabel->setFont(QFont(LABEL_FONT, LABEL_KV_SIZE, QFont::Light));
156 }
157
158 // Use transparent background to respect system theme
159 static const QString checkbox_style_base = QStringLiteral("background-color:transparent;");
160
161 // Set checkbox colors based on theme, using HexRgb to intentionally omit alpha channel
162 cbShowNumTxns->setStyleSheet(cbShowNumTxns->isChecked() ? (checkbox_style_base + "color:" + m_theme_colors->orange.name(QColor::HexRgb) + ";") : checkbox_style_base);
163 cbShowMinFeerate->setStyleSheet(cbShowMinFeerate->isChecked() ? (checkbox_style_base + "color:" + m_theme_colors->green.name(QColor::HexRgb) + ";") : checkbox_style_base);
164 cbShowMemUsage->setStyleSheet(cbShowMemUsage->isChecked() ? (checkbox_style_base + "color:" + m_theme_colors->blue.name(QColor::HexRgb) + ";") : checkbox_style_base);
165
166 last10MinLabel->setEnabled((timeFilter == TEN_MINS));
167 lastHourLabel->setEnabled((timeFilter == ONE_HOUR));
168 lastDayLabel->setEnabled((timeFilter == ONE_DAY));
169 allDataLabel->setEnabled((timeFilter == 0));
170
171 // Update text items with system text color
172 if (titleItem) titleItem->setDefaultTextColor(m_text_color);
173 if (dynMemUsageValueItem) dynMemUsageValueItem->setDefaultTextColor(m_text_color);
174 if (txCountValueItem) txCountValueItem->setDefaultTextColor(m_text_color);
175 if (minFeeValueItem) minFeeValueItem->setDefaultTextColor(m_text_color);
176
177 // remove the items which needs to be redrawn
178 for (QGraphicsItem * item : redrawItems)
179 {
180 scene->removeItem(item);
181 delete item;
182 }
183 redrawItems.clear();
184
185 // get the samples
186 QDateTime toDateTime = QDateTime::currentDateTime();
187 QDateTime fromDateTime = toDateTime.addSecs(-timeFilter); //-1h
188 if (timeFilter == 0)
189 {
190 // disable filter if timeFilter == 0
191 toDateTime.setSecsSinceEpoch(0);
192 fromDateTime.setSecsSinceEpoch(0);
193 }
194
195 mempoolSamples_t vSamples = clientModel->getMempoolStatsInRange(fromDateTime, toDateTime);
196
197 // set the values into the overview labels
198 if (vSamples.size())
199 {
200 dynMemUsageValueItem->setPlainText(GUIUtil::formatBytes((uint64_t)vSamples.back().m_dyn_mem_usage));
201 txCountValueItem->setPlainText(QString::number(vSamples.back().m_tx_count));
202 minFeeValueItem->setPlainText(QString::number(vSamples.back().m_min_fee_per_k));
203 }
204
205 // set dynamic label positions
206 int maxValueSize = std::max(std::max(txCountValueItem->boundingRect().width(), dynMemUsageValueItem->boundingRect().width()), minFeeValueItem->boundingRect().width());
207 maxValueSize = ceil(maxValueSize*0.11)*10; //use size steps of 10dip
208
209 int rightPaddingLabels = std::max(std::max(dynMemUsageSwitch->boundingRect().width(), txCountSwitch->boundingRect().width()), minFeeSwitch->boundingRect().width())+maxValueSize;
210 int rightPadding = 10;
211 dynMemUsageSwitch->setPos(width()-rightPaddingLabels-rightPadding, 5);
212
213 txCountSwitch->setPos(width()-rightPaddingLabels-rightPadding, dynMemUsageSwitch->pos().y()+dynMemUsageSwitch->boundingRect().height());
214
215 minFeeSwitch->setPos(width()-rightPaddingLabels-rightPadding, txCountSwitch->pos().y()+txCountSwitch->boundingRect().height());
216
217 dynMemUsageValueItem->setPos(width()-dynMemUsageValueItem->boundingRect().width()-rightPadding, dynMemUsageSwitch->pos().y());
218 txCountValueItem->setPos(width()-txCountValueItem->boundingRect().width()-rightPadding, txCountSwitch->pos().y());
219 minFeeValueItem->setPos(width()-minFeeValueItem->boundingRect().width()-rightPadding, minFeeSwitch->pos().y());
220
221 titleItem->setPos(5,minFeeSwitch->pos().y()+minFeeSwitch->boundingRect().height()-titleItem->boundingRect().height()+10);
222 titleLine->setLine(10, titleItem->pos().y()+titleItem->boundingRect().height(), width()-10, titleItem->pos().y()+titleItem->boundingRect().height());
223
224 // center the optional "no data" label
225 noDataItem->setPos(width()/2.0-noDataItem->boundingRect().width()/2.0, height()/2.0);
226
227 // set the position of the filter icons
228 static const int filterBottomPadding = 30;
229 int totalWidth = last10MinLabel->boundingRect().width()+lastHourLabel->boundingRect().width()+lastDayLabel->boundingRect().width()+allDataLabel->boundingRect().width()+30;
230 last10MinLabel->setPos((width()-totalWidth)/2.0,height()-filterBottomPadding);
231 lastHourLabel->setPos((width()-totalWidth)/2.0+last10MinLabel->boundingRect().width()+10,height()-filterBottomPadding);
232 lastDayLabel->setPos((width()-totalWidth)/2.0+last10MinLabel->boundingRect().width()+lastHourLabel->boundingRect().width()+20,height()-filterBottomPadding);
233 allDataLabel->setPos((width()-totalWidth)/2.0+last10MinLabel->boundingRect().width()+lastHourLabel->boundingRect().width()+lastDayLabel->boundingRect().width()+30,height()-filterBottomPadding);
234
235 // don't paint the grind/graph if there are no or only a single sample
236 if (vSamples.size() < 2)
237 {
238 noDataItem->setVisible(true);
239 return;
240 }
241 noDataItem->setVisible(false);
242
243 int bottom = ui->graphicsView->size().height()-GRAPH_PADDING_BOTTOM;
244 qreal maxwidth = ui->graphicsView->size().width()-GRAPH_PADDING_LEFT-GRAPH_PADDING_RIGHT;
245 qreal maxheightG = ui->graphicsView->size().height()-GRAPH_PADDING_TOP-GRAPH_PADDING_TOP_LABEL-LABEL_HEIGHT;
246 float paddingTopSizeFactor = 1.2;
247 qreal step = maxwidth/(double)vSamples.size();
248
249 // make sure we skip samples that would be drawn narrower then 1px
250 // larger window can result in drawing more samples
251 int samplesStep = 1;
252 if (step < 1)
253 samplesStep = ceil(1/samplesStep);
254
255 // find maximum values
256 int64_t maxDynMemUsage = 0;
257 int64_t minDynMemUsage = std::numeric_limits<int64_t>::max();
258 int64_t maxTxCount = 0;
259 int64_t minTxCount = std::numeric_limits<int64_t>::max();
260 int64_t maxMinFee = 0;
261 uint32_t maxTimeDetla = vSamples.back().m_time_delta-vSamples.front().m_time_delta;
262 for(const struct CStatsMempoolSample &sample : vSamples)
263 {
264 if (sample.m_dyn_mem_usage > maxDynMemUsage)
265 maxDynMemUsage = sample.m_dyn_mem_usage;
266
267 if (sample.m_dyn_mem_usage < minDynMemUsage)
268 minDynMemUsage = sample.m_dyn_mem_usage;
269
270 if (sample.m_tx_count > maxTxCount)
271 maxTxCount = sample.m_tx_count;
272
273 if (sample.m_tx_count < minTxCount)
274 minTxCount = sample.m_tx_count;
275
276 if (sample.m_min_fee_per_k > maxMinFee)
277 maxMinFee = sample.m_min_fee_per_k;
278 }
279
280 int64_t dynMemUsagelog10Val = pow(10.0, floor(log10(maxDynMemUsage*paddingTopSizeFactor-minDynMemUsage)));
281 int64_t topDynMemUsage = ceil((double)maxDynMemUsage*paddingTopSizeFactor/dynMemUsagelog10Val)*dynMemUsagelog10Val;
282 int64_t bottomDynMemUsage = floor((double)minDynMemUsage/dynMemUsagelog10Val)*dynMemUsagelog10Val;
283
284 int64_t txCountLog10Val = pow(10.0, floor(log10(maxTxCount*paddingTopSizeFactor-minTxCount)));
285 int64_t topTxCount = ceil((double)maxTxCount*paddingTopSizeFactor/txCountLog10Val)*txCountLog10Val;
286 int64_t bottomTxCount = floor((double)minTxCount/txCountLog10Val)*txCountLog10Val;
287
288 qreal currentX = GRAPH_PADDING_LEFT;
289 QPainterPath dynMemUsagePath(QPointF(currentX, bottom));
290 QPainterPath txCountPath(QPointF(currentX, bottom));
291 QPainterPath minFeePath(QPointF(currentX, bottom));
292
293 // draw the three possible paths
294 for (mempoolSamples_t::iterator it = vSamples.begin(); it != vSamples.end(); it+=samplesStep)
295 {
296 const struct CStatsMempoolSample &sample = (*it);
297 qreal xPos = maxTimeDetla > 0 ? maxwidth/maxTimeDetla*(sample.m_time_delta-vSamples.front().m_time_delta) : maxwidth/(double)vSamples.size();
298 if (sample.m_time_delta == vSamples.front().m_time_delta)
299 {
300 dynMemUsagePath.moveTo(GRAPH_PADDING_LEFT+xPos, bottom-maxheightG/(topDynMemUsage-bottomDynMemUsage)*(sample.m_dyn_mem_usage-bottomDynMemUsage));
301 txCountPath.moveTo(GRAPH_PADDING_LEFT+xPos, bottom-maxheightG/(topTxCount-bottomTxCount)*(sample.m_tx_count-bottomTxCount));
302 minFeePath.moveTo(GRAPH_PADDING_LEFT+xPos, bottom-maxheightG/maxMinFee*sample.m_min_fee_per_k);
303 }
304 else
305 {
306 dynMemUsagePath.lineTo(GRAPH_PADDING_LEFT+xPos, bottom-maxheightG/(topDynMemUsage-bottomDynMemUsage)*(sample.m_dyn_mem_usage-bottomDynMemUsage));
307 txCountPath.lineTo(GRAPH_PADDING_LEFT+xPos, bottom-maxheightG/(topTxCount-bottomTxCount)*(sample.m_tx_count-bottomTxCount));
308 minFeePath.lineTo(GRAPH_PADDING_LEFT+xPos, bottom-maxheightG/maxMinFee*sample.m_min_fee_per_k);
309 }
310 }
311
312 // copy the path for the fill
313 QPainterPath dynMemUsagePathFill(dynMemUsagePath);
314
315 // close the path for the fill
316 dynMemUsagePathFill.lineTo(GRAPH_PADDING_LEFT+maxwidth, bottom);
317 dynMemUsagePathFill.lineTo(GRAPH_PADDING_LEFT, bottom);
318
319 QPainterPath dynMemUsageGridPath(QPointF(currentX, bottom));
320
321 // draw horizontal grid
322 int amountOfLinesH = 5;
323 QFont gridFont;
324 gridFont.setPointSize(8);
325 for (int i=0; i < amountOfLinesH; i++)
326 {
327 qreal lY = bottom-i*(maxheightG/(amountOfLinesH-1));
328 dynMemUsageGridPath.moveTo(GRAPH_PADDING_LEFT, lY);
329 dynMemUsageGridPath.lineTo(GRAPH_PADDING_LEFT+maxwidth, lY);
330
331 size_t gridDynSize = (float)i*(topDynMemUsage-bottomDynMemUsage)/(amountOfLinesH-1) + bottomDynMemUsage;
332 size_t gridTxCount = (float)i*(topTxCount-bottomTxCount)/(amountOfLinesH-1) + bottomTxCount;
333
334 QGraphicsTextItem *itemDynSize = scene->addText(GUIUtil::formatBytes(gridDynSize), gridFont);
335 QGraphicsTextItem *itemTxCount = scene->addText(QString::number(gridTxCount), gridFont);
336 itemDynSize->setDefaultTextColor(m_text_color);
337 itemTxCount->setDefaultTextColor(m_text_color);
338
339 itemDynSize->setPos(GRAPH_PADDING_LEFT-itemDynSize->boundingRect().width(), lY-(itemDynSize->boundingRect().height()/2));
340 itemTxCount->setPos(GRAPH_PADDING_LEFT+maxwidth, lY-(itemDynSize->boundingRect().height()/2));
341 redrawItems.append(itemDynSize);
342 redrawItems.append(itemTxCount);
343 }
344
345 // draw vertical grid
346 int amountOfLinesV = 4;
347 QDateTime drawTime(fromDateTime);
348 std::string fromS = fromDateTime.toString().toStdString();
349 std::string toS = toDateTime.toString().toStdString();
350 qint64 secsTotal = fromDateTime.secsTo(toDateTime);
351 for (int i=0; i <= amountOfLinesV; i++)
352 {
353 qreal lX = i*(maxwidth/(amountOfLinesV));
354 dynMemUsageGridPath.moveTo(GRAPH_PADDING_LEFT+lX, bottom);
355 dynMemUsageGridPath.lineTo(GRAPH_PADDING_LEFT+lX, bottom-maxheightG);
356
357 QGraphicsTextItem *item = scene->addText(drawTime.toString("HH:mm"), gridFont);
358 item->setDefaultTextColor(m_text_color);
359 item->setPos(GRAPH_PADDING_LEFT+lX-(item->boundingRect().width()/2), bottom);
360 redrawItems.append(item);
361 qint64 step = secsTotal/amountOfLinesV;
362 drawTime = drawTime.addSecs(step);
363 }
364
365 // materialize path
366 QPen gridPen(QColor(100,100,100, 200), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
367 redrawItems.append(scene->addPath(dynMemUsageGridPath, gridPen));
368
369 // draw semi-transparent gradient for the dynamic memory size fill
370 QLinearGradient gradient(currentX, bottom, currentX, 0);
371 gradient.setColorAt(1.0, m_theme_colors->blue);
372 QColor gradientBase = m_bg_color;
373 gradientBase.setAlpha(0);
374 gradient.setColorAt(0, gradientBase);
375 QBrush graBru(gradient);
376
377 QPen linePenBlue(m_theme_colors->blue, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
378 QPen linePenRed(m_theme_colors->orange, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
379 QPen linePenGreen(m_theme_colors->green, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
380
381 // Draw gradient fill first (underneath everything)
382 if (cbShowMemUsage->isChecked()) {
383 redrawItems.append(scene->addPath(dynMemUsagePathFill, QPen(Qt::NoPen), graBru));
384 redrawItems.append(scene->addPath(dynMemUsagePath, linePenBlue));
385 }
386 if (cbShowNumTxns->isChecked())
387 redrawItems.append(scene->addPath(txCountPath, linePenRed));
388 if (cbShowMinFeerate->isChecked())
389 redrawItems.append(scene->addPath(minFeePath, linePenGreen));
390 }
391
392 // We override the virtual resizeEvent of the QWidget to adjust tables column
393 // sizes as the tables width is proportional to the dialogs width.
394 void MempoolStats::resizeEvent(QResizeEvent *event)
395 {
396 QWidget::resizeEvent(event);
397 ui->graphicsView->resize(size());
398 ui->graphicsView->scene()->setSceneRect(rect());
399 drawChart();
400 }
401
402 void MempoolStats::showEvent(QShowEvent *event)
403 {
404 QWidget::showEvent(event);
405 if (clientModel)
406 drawChart();
407 }
408
409 void MempoolStats::changeEvent(QEvent* e)
410 {
411 if (e->type() == QEvent::PaletteChange) {
412 updateThemeColors();
413 drawChart(); // Redraw with new theme colors
414 }
415 QWidget::changeEvent(e);
416 }
417
418 void MempoolStats::objectClicked(QGraphicsItem *item)
419 {
420 if (item == last10MinLabel)
421 timeFilter = 600;
422
423 if (item == lastHourLabel)
424 timeFilter = 3600;
425
426 if (item == lastDayLabel)
427 timeFilter = 24*3600;
428
429 if (item == allDataLabel)
430 timeFilter = 0;
431
432 drawChart();
433 }
434
435 void MempoolStats::updateThemeColors()
436 {
437 // Detect dark mode for color palette selection
438 const QColor bg_colour = palette().color(backgroundRole());
439 const bool dark_mode = GUIUtil::isDarkMode(bg_colour);
440
441 // Store background color for gradient use
442 m_bg_color = bg_colour;
443
444 // Store text color for labels
445 m_text_color = palette().color(foregroundRole());
446
447 m_theme_colors = dark_mode ? &DARK_THEME_COLORS : &LIGHT_THEME_COLORS;
448 }
449
450 MempoolStats::~MempoolStats()
451 {
452 if (titleItem)
453 {
454 for (QGraphicsItem * item : redrawItems)
455 {
456 scene->removeItem(item);
457 delete item;
458 }
459 redrawItems.clear();
460
461 delete titleItem;
462 delete titleLine;
463 delete noDataItem;
464 delete dynMemUsageValueItem;
465 delete txCountValueItem;
466 delete minFeeValueItem;
467 delete last10MinLabel;
468 delete lastHourLabel;
469 delete lastDayLabel;
470 delete allDataLabel;
471 delete txCountSwitch;
472 delete minFeeSwitch;
473 delete dynMemUsageSwitch;
474 delete scene;
475 }
476 }
477