Initial commit for CachePlotView
The CachePlotView will implement familiar plot interaction features such as
value readouts etc.
ADD
src/cachesim/cacheplotview.cpp
+#include "cacheplotview.h"
+
+#include <QtCharts/QChart>
+#include <QtCharts/QLineSeries>
+#include <QtCharts/QSplineSeries>
+#include <QtGui/QMouseEvent>
+#include <QtGui/QResizeEvent>
+#include <QtWidgets/QGraphicsScene>
+#include <QtWidgets/QGraphicsTextItem>
+#include "callout.h"
+
+void constrainInPlotRange(QPointF& pos, QChart* chart) {
+ const auto plotValue = chart->mapToValue(pos);
+
+ const QPointF chartBottomLeft = chart->mapToPosition({0, 0});
+ const QRectF cbr = chart->boundingRect();
+ const QPointF chartTopRight = QPointF{chartBottomLeft.x() + cbr.width(), chartBottomLeft.y() + cbr.height()};
+
+ pos.rx() = plotValue.x() < 0 ? chartBottomLeft.x() : pos.x();
+ pos.ry() = plotValue.y() < 0 ? chartBottomLeft.y() : pos.y();
+
+ pos.rx() = pos.x() > chartTopRight.x() ? chartTopRight.x() : pos.x();
+ pos.ry() = pos.y() > chartTopRight.y() ? chartTopRight.y() : pos.y();
+}
+
+namespace Ripes {
+
+CachePlotView::CachePlotView(QWidget* parent) : QGraphicsView(new QGraphicsScene, parent) {
+ setMouseTracking(true);
+}
+
+void CachePlotView::setPlot(QChart* chart) {
+ // Clear any previous plot or plot markers
+ scene()->clear();
+ m_chart = chart;
+ scene()->addItem(chart);
+
+ for (auto* series : chart->series()) {
+ if (auto* lineSeries = dynamic_cast<QLineSeries*>(series)) {
+ connect(lineSeries, &QLineSeries::clicked, this, &CachePlotView::keepCallout);
+ connect(lineSeries, &QLineSeries::hovered, this, &CachePlotView::tooltip);
+ }
+ }
+
+ m_coordX = new QGraphicsSimpleTextItem();
+ scene()->addItem(m_coordX);
+ m_coordY = new QGraphicsSimpleTextItem();
+ scene()->addItem(m_coordY);
+ m_coordX->setText("X: ");
+ m_coordY->setText("Y: ");
+ m_coordX->setPos(m_chart->size().width() / 2 - 50, m_chart->size().height());
+ m_coordY->setPos(m_chart->size().width() / 2 + 50, m_chart->size().height());
+}
+
+void CachePlotView::resizeEvent(QResizeEvent* event) {
+ if (scene() && m_chart) {
+ scene()->setSceneRect(QRect(QPoint(0, 0), event->size()));
+ m_chart->resize(event->size());
+ m_coordX->setPos(m_chart->size().width() / 2 - 50, m_chart->size().height() - 20);
+ m_coordY->setPos(m_chart->size().width() / 2 + 50, m_chart->size().height() - 20);
+ for (auto* item : scene()->items()) {
+ if (auto* callout = dynamic_cast<Callout*>(item)) {
+ callout->updateGeometry();
+ }
+ }
+ }
+ QGraphicsView::resizeEvent(event);
+}
+
+void CachePlotView::mouseMoveEvent(QMouseEvent* event) {
+ m_hoverPos = event->pos();
+ constrainInPlotRange(m_hoverPos, m_chart);
+
+ m_coordX->setText(QString("X: %1").arg(m_chart->mapToValue(m_hoverPos).x()));
+ m_coordY->setText(QString("Y: %1").arg(m_chart->mapToValue(m_hoverPos).y()));
+ QGraphicsView::mouseMoveEvent(event);
+}
+
+void CachePlotView::keepCallout() {
+ m_tooltipLock.lock();
+ m_tooltip = nullptr;
+ m_tooltipLock.unlock();
+}
+
+void CachePlotView::tooltip(QPointF point, bool state) {
+ m_tooltipLock.lock();
+ if (m_tooltip == nullptr)
+ m_tooltip = new Callout(m_chart);
+
+ if (state) {
+ m_tooltip->setText(QString("X: %1 \nY: %2 ").arg(point.x()).arg(point.y()));
+ m_tooltip->setAnchor(point);
+ m_tooltip->setZValue(11);
+ m_tooltip->updateGeometry();
+ m_tooltip->show();
+ } else {
+ m_tooltip->hide();
+ }
+ m_tooltipLock.unlock();
+}
+} // namespace Ripes

ADD
src/cachesim/cacheplotview.h
+#pragma once
+
+#include <QMutex>
+#include <QtCharts/QChartGlobal>
+#include <QtWidgets/QGraphicsView>
+
+QT_BEGIN_NAMESPACE
+class QGraphicsScene;
+class QMouseEvent;
+class QResizeEvent;
+QT_END_NAMESPACE
+
+QT_CHARTS_BEGIN_NAMESPACE
+class QChart;
+QT_CHARTS_END_NAMESPACE
+
+class Callout;
+
+QT_CHARTS_USE_NAMESPACE
+
+namespace Ripes {
+
+class CachePlotView : public QGraphicsView {
+ Q_OBJECT
+
+public:
+ CachePlotView(QWidget* parent = nullptr);
+
+ void setPlot(QChart* chart);
+
+protected:
+ void resizeEvent(QResizeEvent* event);
+ void mouseMoveEvent(QMouseEvent* event);
+
+public slots:
+ void keepCallout();
+ void tooltip(QPointF point, bool state);
+
+private:
+ QPointF m_hoverPos;
+
+ QGraphicsSimpleTextItem* m_coordX;
+ QGraphicsSimpleTextItem* m_coordY;
+ QChart* m_chart = nullptr;
+ Callout* m_tooltip = nullptr;
+ QMutex m_tooltipLock;
+};
+
+} // namespace Ripes

MODIFY
src/cachesim/cacheplotwidget.cpp
src/cachesim/cacheplotwidget.cpp
@@ -270,12 +270,9 @@ void CachePlotWidget::setPlot(QChart* plot) {
if (plot == nullptr)
return;
- auto* oldChart = m_ui->plotView->chart();
+ // The plotView takes ownership of @param plot once the plot is set on the view
m_currentPlot = plot;
- m_ui->plotView->setChart(m_currentPlot);
- if (oldChart) {
- delete oldChart;
- }
+ m_ui->plotView->setPlot(m_currentPlot);
}
} // namespace Ripes

MODIFY
src/cachesim/cacheplotwidget.ui
src/cachesim/cacheplotwidget.ui
</layout>
</item>
<item>
- <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
<item>
- <layout class="QGridLayout" name="gridLayout_2"/>
- </item>
- <item>
- <widget class="QtCharts::QChartView" name="plotView" native="true"/>
+ <widget class="CachePlotView" name="plotView"/>
</item>
</layout>
</item>
</widget>
<customwidgets>
<customwidget>
- <class>QtCharts::QChartView</class>
- <extends>QWidget</extends>
- <header>QtCharts/QChartView</header>
- <container>1</container>
+ <class>CachePlotView</class>
+ <extends>QGraphicsView</extends>
+ <header>cachesim/cacheplotview.h</header>
</customwidget>
</customwidgets>
<resources/>

DELETE
src/cachesim/cacheratioplotview.cpp
-#include "cacheratioplotview.h"
-
-CacheRatioPlotView::CacheRatioPlotView()
-{
-
-}

DELETE
src/cachesim/cacheratioplotview.h
-#ifndef CACHERATIOPLOTVIEW_H
-#define CACHERATIOPLOTVIEW_H
-
-
-class CacheRatioPlotView
-{
-public:
- CacheRatioPlotView();
-};
-
-#endif // CACHERATIOPLOTVIEW_H

Initial commit for CachePlotView

The CachePlotView will implement familiar plot interaction features such as
value readouts etc.