All checks were successful
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 11m48s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m18s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 12m18s
Build Packages / build:rpm (rocky8) (push) Successful in 11m43s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 12m31s
Build Packages / Unit tests (push) Has been skipped
Build Packages / Generate python client (push) Successful in 17s
Build Packages / Create release (push) Has been skipped
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 13m2s
Build Packages / Build documentation (push) Successful in 37s
Build Packages / build:rpm (rocky9) (push) Successful in 9m45s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 6m35s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 7m8s
This is an UNSTABLE release. * jfjoch_viewer: Auto load is better handling change of states * jfjoch_viewer: Fix DBus registration * jfjoch_viewer: Handle charts better with vertical lines on hover and status bar update * jfjoch_viewer: Calculate ROI in a more efficient way Reviewed-on: #6 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch> Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
149 lines
4.5 KiB
C++
149 lines
4.5 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include <QMenu>
|
|
#include <QApplication>
|
|
#include <QClipboard>
|
|
|
|
#include "JFJochChartView.h"
|
|
|
|
JFJochChartView::JFJochChartView(QWidget *parent)
|
|
: QChartView(new QChart(), parent) {
|
|
chart()->legend()->hide();
|
|
setRenderHint(QPainter::Antialiasing);
|
|
setRubberBand(QChartView::RubberBand::RectangleRubberBand);
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
void JFJochChartView::setImage(int64_t val) {
|
|
if (!currentSeries)
|
|
return;
|
|
|
|
curr_image = val;
|
|
|
|
if (val < values.size() && val >= 0) {
|
|
currentSeries->clear();
|
|
currentSeries->append(curr_image, values[curr_image]);
|
|
}
|
|
}
|
|
|
|
void JFJochChartView::mousePressEvent(QMouseEvent *event) {
|
|
if (event->button() == Qt::LeftButton) {
|
|
QPointF clickedPoint = event->pos();
|
|
QPointF chartCoord = chart()->mapToValue(clickedPoint);
|
|
int64_t val = std::lround(chartCoord.x());
|
|
if (val >= 0 && val < values.size())
|
|
emit imageSelected(val);
|
|
}
|
|
QChartView::mousePressEvent(event); // Call the base implementation
|
|
}
|
|
|
|
void JFJochChartView::resetZoom() {
|
|
chart()->zoomReset();
|
|
}
|
|
|
|
void JFJochChartView::updateChart() {
|
|
chart()->removeAllSeries();
|
|
if (m_hoverLine) {
|
|
chart()->scene()->removeItem(m_hoverLine);
|
|
delete m_hoverLine;
|
|
m_hoverLine = nullptr;
|
|
}
|
|
if (values.size() >= binning) {
|
|
// At least one full point
|
|
|
|
series = new QLineSeries(this);
|
|
currentSeries = new QScatterSeries(this);
|
|
|
|
if (binning == 1) {
|
|
for (int i = 0; i < values.size(); i++)
|
|
series->append(i, values[i]);
|
|
} else {
|
|
for (int i = 0; i < values.size() / binning; i++) {
|
|
float tmp = 0;
|
|
for (int b = 0; b < binning; b++)
|
|
tmp += values[i * binning + b];
|
|
series->append((i + 0.5) * binning, tmp / binning);
|
|
}
|
|
}
|
|
|
|
if (curr_image < values.size() && curr_image >= 0)
|
|
currentSeries->append(curr_image, values[curr_image]);
|
|
|
|
chart()->addSeries(series);
|
|
chart()->addSeries(currentSeries);
|
|
chart()->createDefaultAxes();
|
|
}
|
|
}
|
|
|
|
void JFJochChartView::setBinning(int64_t val) {
|
|
if (val >= 1) {
|
|
binning = val;
|
|
updateChart();
|
|
}
|
|
}
|
|
|
|
void JFJochChartView::contextMenuEvent(QContextMenuEvent *event) {
|
|
QMenu menu(this);
|
|
QAction *copyXY = menu.addAction("Copy (x y) points");
|
|
copyXY->setEnabled(!values.empty());
|
|
|
|
QAction *chosen = menu.exec(event->globalPos());
|
|
if (chosen == copyXY) {
|
|
QString out;
|
|
out.reserve(static_cast<int>(values.size() * 16)); // rough prealloc
|
|
for (size_t i = 0; i < values.size(); ++i) {
|
|
out.append(QString::number(i));
|
|
out.append(' ');
|
|
out.append(QString::number(values[i], 'g', 10));
|
|
if (i + 1 < values.size()) out.append('\n');
|
|
}
|
|
QClipboard *cb = QApplication::clipboard();
|
|
cb->setText(out);
|
|
}
|
|
}
|
|
|
|
void JFJochChartView::mouseMoveEvent(QMouseEvent *event) {
|
|
QChartView::mouseMoveEvent(event);
|
|
if (!series || values.empty())
|
|
return;
|
|
|
|
// Map mouse position to chart coordinates
|
|
const QPointF chartPos = chart()->mapToValue(event->pos(), series);
|
|
double xVal = chartPos.x();
|
|
|
|
// Convert x to closest image index
|
|
int64_t idx = std::lround(xVal);
|
|
if (idx < 0 || idx >= static_cast<int64_t>(values.size()))
|
|
return;
|
|
|
|
// Map that x position to scene coords for the vertical line
|
|
const QRectF plotArea = chart()->plotArea();
|
|
const QPointF ptOnChart = chart()->mapToPosition(QPointF(static_cast<double>(idx), 0.0), series);
|
|
|
|
if (!m_hoverLine) {
|
|
m_hoverLine = new QGraphicsLineItem;
|
|
m_hoverLine->setPen(QPen(QColor(200, 0, 0, 150), 1.0));
|
|
chart()->scene()->addItem(m_hoverLine);
|
|
}
|
|
|
|
m_hoverLine->setLine(QLineF(ptOnChart.x(), plotArea.top(),
|
|
ptOnChart.x(), plotArea.bottom()));
|
|
|
|
// Status bar text
|
|
QString text = QString("image = %1, value = %2")
|
|
.arg(idx)
|
|
.arg(values[static_cast<size_t>(idx)], 0, 'g', 6);
|
|
emit writeStatusBar(text, 6000);
|
|
}
|
|
|
|
void JFJochChartView::leaveEvent(QEvent *event) {
|
|
QChartView::leaveEvent(event);
|
|
if (m_hoverLine) {
|
|
chart()->scene()->removeItem(m_hoverLine);
|
|
delete m_hoverLine;
|
|
m_hoverLine = nullptr;
|
|
}
|
|
emit writeStatusBar(QString(), 0);
|
|
}
|