From bc785672c57009bea02bd663b302f6f6d4464cb3 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 14 Apr 2020 09:45:08 +0200 Subject: [PATCH] WIP: mempool stats chart --- src/Makefile.qt.include | 4 + src/interfaces/node.cpp | 51 ++++++ src/interfaces/node.h | 21 +++ src/qt/clientmodel.cpp | 15 ++ src/qt/clientmodel.h | 16 +- src/qt/forms/debugwindow.ui | 44 ++++++ src/qt/forms/mempoolstats.ui | 46 ++++++ src/qt/mempoolstats.cpp | 291 +++++++++++++++++++++++++++++++++++ src/qt/mempoolstats.h | 60 ++++++++ src/qt/rpcconsole.cpp | 1 + 10 files changed, 544 insertions(+), 5 deletions(-) create mode 100644 src/qt/forms/mempoolstats.ui create mode 100644 src/qt/mempoolstats.cpp create mode 100644 src/qt/mempoolstats.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f46310a6039..4fa23b4e7e7 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -22,6 +22,7 @@ QT_FORMS_UI = \ qt/forms/helpmessagedialog.ui \ qt/forms/intro.ui \ qt/forms/modaloverlay.ui \ + qt/forms/mempoolstats.ui \ qt/forms/openuridialog.ui \ qt/forms/optionsdialog.ui \ qt/forms/overviewpage.ui \ @@ -54,6 +55,7 @@ QT_MOC_CPP = \ qt/moc_intro.cpp \ qt/moc_macdockiconhandler.cpp \ qt/moc_macnotificationhandler.cpp \ + qt/moc_mempoolstats.cpp \ qt/moc_modaloverlay.cpp \ qt/moc_notificator.cpp \ qt/moc_openuridialog.cpp \ @@ -124,6 +126,7 @@ BITCOIN_QT_H = \ qt/macdockiconhandler.h \ qt/macnotificationhandler.h \ qt/macos_appnap.h \ + qt/mempoolstats.h \ qt/modaloverlay.h \ qt/networkstyle.h \ qt/notificator.h \ @@ -221,6 +224,7 @@ BITCOIN_QT_BASE_CPP = \ qt/csvmodelwriter.cpp \ qt/guiutil.cpp \ qt/intro.cpp \ + qt/mempoolstats.cpp \ qt/modaloverlay.cpp \ qt/networkstyle.cpp \ qt/notificator.cpp \ diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 2c5f8627e61..a6ba332b087 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -158,6 +158,57 @@ class NodeImpl : public Node int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } + mempool_feehistogram getMempoolFeeHistogram() override { + /* TODO: define log scale formular for dynamically creating the + * feelimits but with the property of not constantly changing + * (and thus screw up client implementations) */ + static const std::vector feelimits{1, 2, 3, 4, 5, 6, 7, 8, 10, + 12, 14, 17, 20, 25, 30, 40, 50, 60, 70, 80, 100, + 120, 140, 170, 200, 250, 300, 400, 500, 600, 700, 800, 1000, + 1200, 1400, 1700, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 8000, 10000}; + + /* keep histogram per... + * ... cumulated tx sizes + * ... txns (count) + * ... cumulated fees */ + std::vector sizes(feelimits.size(), 0); + std::vector count(feelimits.size(), 0); + std::vector fees(feelimits.size(), 0); + + { + LOCK(m_context->mempool->cs); + for (const CTxMemPoolEntry& e : m_context->mempool->mapTx) { + int size = (int)e.GetTxSize(); + CAmount fee = e.GetFee(); + uint64_t asize = e.GetSizeWithAncestors(); + CAmount afees = e.GetModFeesWithAncestors(); + uint64_t dsize = e.GetSizeWithDescendants(); + CAmount dfees = e.GetModFeesWithDescendants(); + + CAmount fpb = fee / size; //fee per byte + CAmount afpb = afees / asize; //fee per byte including ancestors + CAmount dfpb = dfees / dsize; //fee per byte including descendants + CAmount tfpb = (afees + dfees - fee) / (asize + dsize - size); + CAmount feeperbyte = std::max(std::min(dfpb, tfpb), std::min(fpb, afpb)); + + // distribute feerates into feelimits + for (size_t i = 0; i < feelimits.size(); i++) { + if (feeperbyte >= feelimits[i] && (i == feelimits.size() - 1 || feeperbyte < feelimits[i + 1])) { + sizes[i] += size; + count[i]++; + fees[i] += fee; + break; + } + } + } + } + mempool_feehistogram feeinfo; + for (size_t i = 0; i < feelimits.size(); i++) { + feeinfo.push_back({sizes[i], fees[i], count[i], feelimits[i], (i == feelimits.size() - 1 ? std::numeric_limits::max() : feelimits[i + 1])}); + } + + return feeinfo; + } bool getHeaderTip(int& height, int64_t& block_time) override { LOCK(::cs_main); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 5079be038eb..aca7d1941e1 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -48,6 +48,24 @@ struct BlockAndHeaderTipInfo double verification_progress; }; +class mempool_feeinfo { +public: + uint64_t total_size; + uint64_t total_fee; + uint64_t tx_count; + CAmount fee_from; + CAmount fee_to; + + //TODO: remove + // added for storing and loading a mempool set during development to avoid waiting hours for collecting enought samples + SERIALIZE_METHODS(mempool_feeinfo, obj) + { + READWRITE(obj.total_size, obj.total_fee, obj.tx_count, obj.fee_from, obj.fee_to); + } +}; + +typedef std::vector mempool_feehistogram; + //! Top-level interface for a bitcoin node (bitcoind process). class Node { @@ -121,6 +139,9 @@ class Node //! Get mempool dynamic usage. virtual size_t getMempoolDynamicUsage() = 0; + //! Get mempool fee histogram + virtual mempool_feehistogram getMempoolFeeHistogram() = 0; + //! Get header tip height and time. virtual bool getHeaderTip(int& height, int64_t& block_time) = 0; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index a2f46c339b8..39644ba0dee 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include +#include #include #include @@ -45,6 +47,19 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO connect(timer, &QTimer::timeout, [this] { // no locking required at this point // the following calls will acquire the required lock + + int64_t now = GetTime(); + if (m_mempool_feehist_last_sample_timestamp == 0 || m_mempool_feehist_last_sample_timestamp+m_mempool_collect_intervall < static_cast(now)) { + QMutexLocker locker(&m_mempool_locker); + interfaces::mempool_feehistogram fee_histogram = m_node.getMempoolFeeHistogram(); + m_mempool_feehist.push_back({now, fee_histogram}); + if (m_mempool_feehist.size() > m_mempool_max_samples) { + m_mempool_feehist.erase(m_mempool_feehist.begin(), m_mempool_feehist.begin()+1); + } + m_mempool_feehist_last_sample_timestamp = now; + Q_EMIT mempoolFeeHistChanged(); + } + Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage()); Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent()); }); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 7f12cce1d91..f60bc3777c2 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_QT_CLIENTMODEL_H #define BITCOIN_QT_CLIENTMODEL_H +#include #include #include @@ -13,17 +14,14 @@ #include #include +#include + class BanTableModel; class CBlockIndex; class OptionsModel; class PeerTableModel; enum class SynchronizationState; -namespace interfaces { -class Handler; -class Node; -} - QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE @@ -85,6 +83,13 @@ class ClientModel : public QObject Mutex m_cached_tip_mutex; uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){}; + typedef std::pair> mempool_feehist_sample; //!< sample plus timestamp + mutable QMutex m_mempool_locker; + const static size_t m_mempool_max_samples{540}; + const static size_t m_mempool_collect_intervall{20}; // 540*20 = 3h of sample window + std::vector m_mempool_feehist; + std::atomic m_mempool_feehist_last_sample_timestamp{0}; + private: interfaces::Node& m_node; std::unique_ptr m_handler_show_progress; @@ -108,6 +113,7 @@ class ClientModel : public QObject void numConnectionsChanged(int count); void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state); void mempoolSizeChanged(long count, size_t mempoolSizeInBytes); + void mempoolFeeHistChanged(); void networkActiveChanged(bool networkActive); void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index d210faec03d..c4201610e22 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -917,6 +917,44 @@ + + + + 747 + 0 + + + + Mempool + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + &Peers @@ -1536,6 +1574,12 @@ clear() + + MempoolStats + QWidget +
qt/mempoolstats.h
+ 1 +
diff --git a/src/qt/forms/mempoolstats.ui b/src/qt/forms/mempoolstats.ui new file mode 100644 index 00000000000..a6df26c2637 --- /dev/null +++ b/src/qt/forms/mempoolstats.ui @@ -0,0 +1,46 @@ + + + MempoolStats + + + + 0 + 0 + 800 + 640 + + + + + 800 + 640 + + + + Mempool Stats + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::ScrollBarAlwaysOff + + + + + + + + diff --git a/src/qt/mempoolstats.cpp b/src/qt/mempoolstats.cpp new file mode 100644 index 00000000000..ac657211f77 --- /dev/null +++ b/src/qt/mempoolstats.cpp @@ -0,0 +1,291 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include +#include + +static const char *LABEL_FONT = "Arial"; +static int LABEL_TITLE_SIZE = 22; +static int LABEL_KV_SIZE = 12; + +static const int LABEL_LEFT_SIZE = 30; +static const int LABEL_RIGHT_SIZE = 30; +static const int GRAPH_PADDING_LEFT = 30+LABEL_LEFT_SIZE; +static const int GRAPH_PADDING_RIGHT = 30+LABEL_RIGHT_SIZE; +static const int GRAPH_PADDING_TOP = 10; +static const int GRAPH_PADDING_TOP_LABEL = 10; +static const int GRAPH_PADDING_BOTTOM = 50; + +void ClickableTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_EMIT objectClicked(this); +} + +void ClickableRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_EMIT objectClicked(this); +} + +MempoolStats::MempoolStats(QWidget *parent) : +QWidget(parent) +{ + if (parent) { + parent->installEventFilter(this); + raise(); + } + + // autoadjust font size + QGraphicsTextItem testText("jY"); //screendesign expected 27.5 pixel in width for this string + testText.setFont(QFont(LABEL_FONT, LABEL_TITLE_SIZE, QFont::Light)); + LABEL_TITLE_SIZE *= 27.5/testText.boundingRect().width(); + LABEL_KV_SIZE *= 27.5/testText.boundingRect().width(); + + m_gfx_view = new QGraphicsView(this); + m_scene = new QGraphicsScene(m_gfx_view); + m_gfx_view->setScene(m_scene); + m_gfx_view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + if (m_clientmodel) + drawChart(); +} + +void MempoolStats::setClientModel(ClientModel *model) +{ + m_clientmodel = model; + if (model) { + connect(model, &ClientModel::mempoolFeeHistChanged, this, &MempoolStats::drawChart); + drawChart(); + } +} + +// define the colors for the feeranges +// TODO: find a more dynamic way to assign colors +const static std::vector colors = { QColor("#535154"), QColor("#0000ac"), QColor("#0000c2"), QColor("#0000d8"), QColor("#0000ec"), QColor("#0000ff"), QColor("#2c2cff"), QColor("#5858ff"), QColor("#8080ff"), + QColor("#008000"), QColor("#00a000"), QColor("#00c000"), QColor("#00e000"), QColor("#30e030"), QColor("#60e060"), QColor("#90e090"), + QColor("#808000"), QColor("#989800"), QColor("#b0b000"), QColor("#c8c800"), QColor("#e0e000"), QColor("#e0e030"), QColor("#e0e060"), + QColor("#800000"), QColor("#a00000"), QColor("#c00000"), QColor("#e00000"), QColor("#e02020"), QColor("#e04040"), QColor("#e06060"), + QColor("#800080"), QColor("#ac00ac"), QColor("#d800d8"), QColor("#ff00ff"), QColor("#ff2cff"), QColor("#ff58ff"), QColor("#ff80ff"), + QColor("#000000") }; +void MempoolStats::drawChart() +{ + if (!m_clientmodel) + return; + + m_scene->clear(); + + std::vector fee_paths; + std::vector fee_subtotal_txcount; + qreal current_x = GRAPH_PADDING_LEFT; + const qreal bottom = m_gfx_view->scene()->sceneRect().height()-GRAPH_PADDING_BOTTOM; + const qreal maxheight_g = (m_gfx_view->scene()->sceneRect().height()-GRAPH_PADDING_TOP-GRAPH_PADDING_TOP_LABEL-GRAPH_PADDING_BOTTOM); + size_t max_txcount=0; + QFont gridFont; + gridFont.setPointSize(8); + int display_up_to_range = 0; + qreal maxwidth = m_gfx_view->scene()->sceneRect().width()-GRAPH_PADDING_LEFT-GRAPH_PADDING_RIGHT; + { + // we are going to access the clientmodel feehistogram directly avoding a copy + QMutexLocker locker(&m_clientmodel->m_mempool_locker); + + /* TODO: remove + helpful for testing/development (loading a prestored dataset) + */ + //FILE *filestr = fsbridge::fopen("/tmp/statsdump", "rb"); + //CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); + //ile >> m_clientmodel->m_mempool_feehist; + //file.fclose(); + + size_t max_txcount_graph=0; + + if (m_clientmodel->m_mempool_feehist.size() == 0) { + // draw nothing + return; + } + + fee_subtotal_txcount.resize(m_clientmodel->m_mempool_feehist[0].second.size()); + // calculate max tx for upper bound of chart + for (const ClientModel::mempool_feehist_sample& sample : m_clientmodel->m_mempool_feehist) { + uint64_t txcount = 0; + int i = 0; + for (const interfaces::mempool_feeinfo& list_entry : sample.second) { + txcount += list_entry.tx_count; + fee_subtotal_txcount[i] += list_entry.tx_count; + i++; + } + if (txcount > max_txcount) max_txcount = txcount; + } + + // hide ranges we don't have txns + for(size_t i = 0; i < fee_subtotal_txcount.size(); i++) { + if (fee_subtotal_txcount[i] > 0) { + display_up_to_range = i; + } + } + + // make a nice y-axis scale + const int amount_of_h_lines = 5; + if (max_txcount > 0) { + int val = floor(log10(1.0*max_txcount/amount_of_h_lines)); + int stepbase = pow(10.0f, val); + int step = ceil((1.0*max_txcount/amount_of_h_lines) / stepbase) * stepbase; + max_txcount_graph = step*amount_of_h_lines; + } + + // calculate the x axis step per sample + // we ignore the time difference of collected samples due to locking issues + const qreal x_increment = 1.0 * (width()-GRAPH_PADDING_LEFT-GRAPH_PADDING_RIGHT) / m_clientmodel->m_mempool_max_samples; //samples.size(); + + // draw horizontal grid + QPainterPath tx_count_grid_path(QPointF(current_x, bottom)); + int bottomTxCount = 0; + for (int i=0; i < amount_of_h_lines; i++) + { + qreal lY = bottom-i*(maxheight_g/(amount_of_h_lines-1)); + tx_count_grid_path.moveTo(GRAPH_PADDING_LEFT, lY); + tx_count_grid_path.lineTo(GRAPH_PADDING_LEFT+maxwidth, lY); + + size_t grid_tx_count = (float)i*(max_txcount_graph-bottomTxCount)/(amount_of_h_lines-1) + bottomTxCount; + QGraphicsTextItem *item_tx_count = m_scene->addText(QString::number(grid_tx_count), gridFont); + item_tx_count->setPos(GRAPH_PADDING_LEFT+maxwidth, lY-(item_tx_count->boundingRect().height()/2)); + } + + QPen gridPen(QColor(100,100,100, 200), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); + m_scene->addPath(tx_count_grid_path, gridPen); + + + // draw fee ranges + QGraphicsTextItem *fee_range_title = m_scene->addText("Fee ranges\n(sat/b)", gridFont); + fee_range_title->setPos(2, bottom+10); + + qreal c_y = bottom; + const qreal c_w = 10; + const qreal c_h = 10; + const qreal c_margin = 2; + c_y-=c_margin; + int i = 0; + for (const interfaces::mempool_feeinfo& list_entry : m_clientmodel->m_mempool_feehist[0].second) { + if (i > display_up_to_range) { + continue; + } + ClickableRectItem *fee_rect = new ClickableRectItem(); + fee_rect->setRect(4, c_y, c_w, c_h); + + QColor brush_color = colors[(i < static_cast(colors.size()) ? i : static_cast(colors.size())-1)]; + brush_color.setAlpha(85); + if (m_selected_range >= 0 && m_selected_range != i) { + // if one item is selected, hide out the other ones + brush_color.setAlpha(30); + } + + fee_rect->setBrush(QBrush(brush_color)); + fee_rect->setCursor(Qt::PointingHandCursor); + connect(fee_rect, &ClickableRectItem::objectClicked, [this, i](QGraphicsItem*item) { + // if clicked, we select or deselect if selected + if (m_selected_range == i) { + m_selected_range = -1; + } else { + m_selected_range = i; + } + drawChart(); + + /*TODO remove + store the existing feehistory to a temporary file + */ + //FILE *filestr = fsbridge::fopen("/tmp/statsdump", "wb"); + //CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); + //file << m_clientmodel->m_mempool_feehist; + //file.fclose(); + }); + m_scene->addItem(fee_rect); + + ClickableTextItem *fee_text = new ClickableTextItem(); + fee_text->setText(QString::number(list_entry.fee_from)+"-"+QString::number(list_entry.fee_to)); + if (i+1 == static_cast(m_clientmodel->m_mempool_feehist[0].second.size())) { + fee_text->setText(QString::number(list_entry.fee_from)+"+"); + } + fee_text->setFont(gridFont); + fee_text->setPos(4+c_w+2, c_y); + m_scene->addItem(fee_text); + connect(fee_text, &ClickableTextItem::objectClicked, [&fee_rect](QGraphicsItem*item) { + fee_rect->objectClicked(item); + }); + + c_y-=c_h+c_margin; + i++; + } + + // draw the paths + bool first = true; + for (const ClientModel::mempool_feehist_sample& sample : m_clientmodel->m_mempool_feehist) { + current_x += x_increment; + int i = 0; + qreal y = bottom; + for (const interfaces::mempool_feeinfo& list_entry : sample.second) { + if (i > display_up_to_range) { + // skip ranges without txns + continue; + } + y -= (maxheight_g / max_txcount_graph * list_entry.tx_count); + if (first) { + // first sample, initiate the path with first point + fee_paths.emplace_back(QPointF(current_x, y)); + } + else { + fee_paths[i].lineTo(current_x, y); + } + i++; + } + first = false; + } + } // release lock for the acctual drawing + + int i = 0; + QString total_text = tr("Last %1 hours").arg(QString::number(m_clientmodel->m_mempool_max_samples*m_clientmodel->m_mempool_collect_intervall/3600)); + for (auto feepath : fee_paths) { + // close paths + if (i > 0) { + feepath.lineTo(fee_paths[i-1].currentPosition()); + feepath.connectPath(fee_paths[i-1].toReversed()); + } else { + feepath.lineTo(current_x, bottom); + feepath.lineTo(GRAPH_PADDING_LEFT, bottom); + } + QColor pen_color = colors[(i < static_cast(colors.size()) ? i : static_cast(colors.size())-1)]; + QColor brush_color = pen_color; + pen_color.setAlpha(95); + brush_color.setAlpha(85); + if (m_selected_range >= 0 && m_selected_range != i) { + pen_color.setAlpha(40); + brush_color.setAlpha(30); + } + if (m_selected_range >= 0 && m_selected_range == i) { + total_text = "transactions in selected fee range: "+QString::number(fee_subtotal_txcount[i]); + } + QPen pen_blue(pen_color, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); + m_scene->addPath(feepath, pen_blue, QBrush(brush_color)); + i++; + } + + QGraphicsTextItem *item_tx_count = m_scene->addText(total_text, gridFont); + item_tx_count->setPos(GRAPH_PADDING_LEFT+(maxwidth/2), bottom); +} + +// We override the virtual resizeEvent of the QWidget to adjust tables column +// sizes as the tables width is proportional to the dialogs width. +void MempoolStats::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + m_gfx_view->resize(size()); + m_gfx_view->scene()->setSceneRect(rect().left(), rect().top(),rect().width(),std::max(500, rect().height())); + drawChart(); +} + +void MempoolStats::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + if (m_clientmodel) + drawChart(); +} diff --git a/src/qt/mempoolstats.h b/src/qt/mempoolstats.h new file mode 100644 index 00000000000..1e2f0227978 --- /dev/null +++ b/src/qt/mempoolstats.h @@ -0,0 +1,60 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_MEMPOOLSTATS_H +#define BITCOIN_QT_MEMPOOLSTATS_H + +#include +#include +#include +#include +#include +#include +#include + +class ClientModel; + +class ClickableTextItem : public QObject, public QGraphicsSimpleTextItem +{ + Q_OBJECT +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; +Q_SIGNALS: + void objectClicked(QGraphicsItem*); +}; + +class ClickableRectItem : public QObject, public QGraphicsRectItem +{ + Q_OBJECT +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; +Q_SIGNALS: + void objectClicked(QGraphicsItem*); +}; + + +class MempoolStats : public QWidget +{ + Q_OBJECT + +public: + explicit MempoolStats(QWidget *parent = nullptr); + void setClientModel(ClientModel *model); + +public Q_SLOTS: + void drawChart(); + +private: + ClientModel* m_clientmodel = nullptr; + + QGraphicsView *m_gfx_view; + QGraphicsScene *m_scene; + + virtual void resizeEvent(QResizeEvent* event); + virtual void showEvent(QShowEvent* event); + + int m_selected_range = -1; +}; + +#endif // BITCOIN_QT_MEMPOOLSTATS_H diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 4c5601242e9..da7be892f1b 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -572,6 +572,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ } ui->trafficGraph->setClientModel(model); + ui->mempool_graph->setClientModel(model); if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) { // Keep up to date with client setNumConnections(model->getNumConnections());