Files
Jungfraujoch/viewer/charts/JFJochChartView.cpp
T
leonarski_f f44c6520a8
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 7m33s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 8m46s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 7m19s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 7m50s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 9m8s
Build Packages / Generate python client (push) Successful in 17s
Build Packages / Build documentation (push) Successful in 43s
Build Packages / Create release (push) Has been skipped
Build Packages / build:rpm (rocky8) (push) Successful in 8m25s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 7m39s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 8m13s
Build Packages / build:rpm (rocky9) (push) Successful in 9m33s
Build Packages / Unit tests (push) Successful in 1h14m4s
v1.0.0-rc.107 (#12)
This is an UNSTABLE release.

* jfjoch_viewer: Minor polishing of new functionality
* jfjoch_broker: User NaN for empty azimuthal bins

Reviewed-on: #12
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
2025-11-24 07:07:32 +01:00

190 lines
5.8 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);
m_hoverLoadTimer = new QTimer(this);
m_hoverLoadTimer->setSingleShot(true);
connect(m_hoverLoadTimer, &QTimer::timeout, this, &JFJochChartView::onHoverLoadTimeout);
}
void JFJochChartView::setImage(int64_t val) {
if (!currentSeries)
return;
curr_image = val;
if (val < values.size() && val >= 0) {
currentSeries->clear();
if (std::isfinite(values[curr_image]))
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++)
if (std::isfinite(values[i]))
series->append(i, values[i]);
} else {
for (int i = 0; i < values.size() / binning; i++) {
float tmp = 0;
int64_t count = 0;
for (int b = 0; b < binning; b++) {
if (std::isfinite(values[i * binning + b])) {
tmp += values[i * binning + b];
count++;
}
}
if (count > 0)
series->append((i + 0.5) * binning, tmp / count);
}
}
if (curr_image < values.size() && curr_image >= 0 && std::isfinite(values[curr_image]))
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);
// Debounced image load on hover when Shift is pressed
if (event->modifiers() & Qt::ShiftModifier) {
if (!m_hoverLoadTimer->isActive()) {
m_hoverPendingIdx = -1;
if (idx != curr_image)
emit imageSelected(idx);
m_hoverLoadTimer->start(500); // debounce
} else
m_hoverPendingIdx = idx;
} else {
m_hoverLoadTimer->stop();
m_hoverPendingIdx = -1;
}
}
void JFJochChartView::leaveEvent(QEvent *event) {
QChartView::leaveEvent(event);
if (m_hoverLine) {
chart()->scene()->removeItem(m_hoverLine);
delete m_hoverLine;
m_hoverLine = nullptr;
}
m_hoverLoadTimer->stop();
m_hoverPendingIdx = -1;
emit writeStatusBar(QString(), 0);
}
void JFJochChartView::onHoverLoadTimeout() {
if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier))
return;
if (m_hoverPendingIdx >= 0 &&
m_hoverPendingIdx < static_cast<int64_t>(values.size())) {
if (m_hoverPendingIdx != curr_image) {
emit imageSelected(m_hoverPendingIdx);
}
}
}