v1.0.0-rc.113 #19
@@ -24,8 +24,8 @@ ADD_EXECUTABLE(jfjoch_viewer jfjoch_viewer.cpp JFJochViewerWindow.cpp JFJochView
|
||||
JFJochViewerSidePanel.h
|
||||
widgets/TitleLabel.cpp
|
||||
widgets/TitleLabel.h
|
||||
charts/JFJochChartView.cpp
|
||||
charts/JFJochChartView.h
|
||||
charts/JFJochDatasetInfoChartView.cpp
|
||||
charts/JFJochDatasetInfoChartView.h
|
||||
widgets/JFJochViewerImageStatistics.cpp
|
||||
widgets/JFJochViewerImageStatistics.h
|
||||
charts/JFJochSimpleChartView.cpp
|
||||
|
||||
@@ -35,7 +35,7 @@ JFJochViewerDatasetInfo::JFJochViewerDatasetInfo(QWidget *parent) : QWidget(pare
|
||||
layout->addWidget(grid_button, 0, 3);
|
||||
|
||||
stack = new QStackedWidget(this);
|
||||
chart_view = new JFJochChartView(this);
|
||||
chart_view = new JFJochDatasetInfoChartView(this);
|
||||
grid_scan_image = new JFJochGridScanImage(this);
|
||||
|
||||
stack->addWidget(chart_view);
|
||||
@@ -43,7 +43,7 @@ JFJochViewerDatasetInfo::JFJochViewerDatasetInfo(QWidget *parent) : QWidget(pare
|
||||
|
||||
layout->addWidget(stack, 1, 0, 1, 4);
|
||||
|
||||
connect(chart_view, &JFJochChartView::imageSelected,
|
||||
connect(chart_view, &JFJochDatasetInfoChartView::imageSelected,
|
||||
this, &JFJochViewerDatasetInfo::imageSelectedInChart);
|
||||
|
||||
connect(reset_button, &QPushButton::clicked,
|
||||
@@ -55,7 +55,7 @@ JFJochViewerDatasetInfo::JFJochViewerDatasetInfo(QWidget *parent) : QWidget(pare
|
||||
connect(grid_scan_image, &JFJochGridScanImage::imageSelected,
|
||||
this, &JFJochViewerDatasetInfo::imageSelectedInChart);
|
||||
|
||||
connect(chart_view, &JFJochChartView::writeStatusBar,
|
||||
connect(chart_view, &JFJochDatasetInfoChartView::writeStatusBar,
|
||||
[this](QString string, int timeout_ms) {
|
||||
emit writeStatusBar(string, timeout_ms);
|
||||
});
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "charts/JFJochChartView.h"
|
||||
#include "charts/JFJochDatasetInfoChartView.h"
|
||||
#include "../reader/JFJochReader.h"
|
||||
#include "image_viewer/JFJochGridScanImage.h"
|
||||
|
||||
class JFJochViewerDatasetInfo : public QWidget {
|
||||
Q_OBJECT
|
||||
QComboBox *combo_box;
|
||||
JFJochChartView *chart_view;
|
||||
JFJochDatasetInfoChartView *chart_view;
|
||||
const std::vector<float> *GetDataset();
|
||||
std::shared_ptr<const JFJochReaderDataset> dataset;
|
||||
std::shared_ptr<const JFJochReaderImage> image;
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
#include <QClipboard>
|
||||
#include <QCategoryAxis>
|
||||
|
||||
#include "JFJochChartView.h"
|
||||
#include "JFJochDatasetInfoChartView.h"
|
||||
|
||||
JFJochChartView::JFJochChartView(QWidget *parent)
|
||||
JFJochDatasetInfoChartView::JFJochDatasetInfoChartView(QWidget *parent)
|
||||
: QChartView(new QChart(), parent) {
|
||||
chart()->legend()->hide();
|
||||
setRenderHint(QPainter::Antialiasing);
|
||||
setRubberBand(QChartView::RubberBand::RectangleRubberBand);
|
||||
// setRubberBand(QChartView::RubberBand::RectangleRubberBand);
|
||||
setMouseTracking(true);
|
||||
|
||||
m_hoverLoadTimer = new QTimer(this);
|
||||
m_hoverLoadTimer->setSingleShot(true);
|
||||
connect(m_hoverLoadTimer, &QTimer::timeout, this, &JFJochChartView::onHoverLoadTimeout);
|
||||
connect(m_hoverLoadTimer, &QTimer::timeout, this, &JFJochDatasetInfoChartView::onHoverLoadTimeout);
|
||||
}
|
||||
|
||||
void JFJochChartView::setImage(int64_t val) {
|
||||
void JFJochDatasetInfoChartView::setImage(int64_t val) {
|
||||
if (!currentSeries)
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,7 @@ void JFJochChartView::setImage(int64_t val) {
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochChartView::mousePressEvent(QMouseEvent *event) {
|
||||
void JFJochDatasetInfoChartView::mousePressEvent(QMouseEvent *event) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
QPointF clickedPoint = event->pos();
|
||||
QPointF chartCoord = chart()->mapToValue(clickedPoint);
|
||||
@@ -47,11 +47,11 @@ void JFJochChartView::mousePressEvent(QMouseEvent *event) {
|
||||
QChartView::mousePressEvent(event); // Call the base implementation
|
||||
}
|
||||
|
||||
void JFJochChartView::resetZoom() {
|
||||
void JFJochDatasetInfoChartView::resetZoom() {
|
||||
chart()->zoomReset();
|
||||
}
|
||||
|
||||
void JFJochChartView::updateChart() {
|
||||
void JFJochDatasetInfoChartView::updateChart() {
|
||||
chart()->removeAllSeries();
|
||||
if (m_hoverLine) {
|
||||
chart()->scene()->removeItem(m_hoverLine);
|
||||
@@ -72,10 +72,7 @@ void JFJochChartView::updateChart() {
|
||||
if (!std::isfinite(values[i])) continue;
|
||||
const double disp = values[i];
|
||||
if (!std::isfinite(disp)) continue;
|
||||
if (goniometer_axis)
|
||||
series->append(goniometer_axis->GetAngle_deg(i), disp);
|
||||
else
|
||||
series->append(i, disp);
|
||||
series->append(i, disp);
|
||||
if (disp < dispMin) dispMin = disp;
|
||||
if (disp > dispMax) dispMax = disp;
|
||||
}
|
||||
@@ -94,11 +91,7 @@ void JFJochChartView::updateChart() {
|
||||
const double mean = tmp / static_cast<double>(count);
|
||||
const double disp = mean;
|
||||
if (std::isfinite(disp)) {
|
||||
if (goniometer_axis) {
|
||||
series->append(goniometer_axis->GetAngle_deg((i + 0.5) * binning), disp);
|
||||
} else {
|
||||
series->append((i + 0.5) * binning, disp);
|
||||
}
|
||||
series->append((i + 0.5) * binning, disp);
|
||||
if (disp < dispMin) dispMin = disp;
|
||||
if (disp > dispMax) dispMax = disp;
|
||||
}
|
||||
@@ -107,22 +100,67 @@ void JFJochChartView::updateChart() {
|
||||
}
|
||||
|
||||
if (curr_image < values.size() && curr_image >= 0 && std::isfinite(values[curr_image])) {
|
||||
if (goniometer_axis)
|
||||
currentSeries->append(goniometer_axis->GetAngle_deg(curr_image), values[curr_image]);
|
||||
else
|
||||
currentSeries->append(curr_image, values[curr_image]);
|
||||
currentSeries->append(curr_image, values[curr_image]);
|
||||
}
|
||||
|
||||
chart()->addSeries(series);
|
||||
chart()->addSeries(currentSeries);
|
||||
chart()->createDefaultAxes();
|
||||
if (goniometer_axis) {
|
||||
chart()->axisX(series)->setTitleText("Rotation angle (deg.)");
|
||||
} else {
|
||||
chart()->axisX(series)->setTitleText("Image number");
|
||||
|
||||
// ----- X axis handling -----
|
||||
QValueAxis *axisX = qobject_cast<QValueAxis *>(chart()->axisX(series));
|
||||
if (axisX) {
|
||||
if (goniometer_axis.has_value() && m_xUseGoniometerAxis) {
|
||||
// Hide labels on numeric axis and move it to the top
|
||||
|
||||
axisX->setTitleText(QString(""));
|
||||
axisX->setLabelsVisible(false);
|
||||
|
||||
// Re-attach numeric axis on top side (default axis on other side)
|
||||
chart()->removeAxis(axisX);
|
||||
chart()->addAxis(axisX, Qt::AlignTop);
|
||||
series->attachAxis(axisX);
|
||||
currentSeries->attachAxis(axisX);
|
||||
|
||||
// Build a visible category axis on the bottom with goniometer angles
|
||||
auto *axXcat = new QCategoryAxis();
|
||||
axXcat->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
|
||||
axXcat->setGridLineVisible(false);
|
||||
axXcat->setMinorGridLineVisible(false);
|
||||
axXcat->setTitleText(QStringLiteral("Rotation angle (deg.)"));
|
||||
|
||||
const int tickCountX = std::max(2, axisX->tickCount());
|
||||
const double xmin = axisX->min();
|
||||
const double xmax = axisX->max();
|
||||
const double xstep = (tickCountX > 1) ? (xmax - xmin) / (tickCountX - 1) : 0.0;
|
||||
const int64_t lastIdx = static_cast<int64_t>(values.empty() ? 0 : values.size() - 1);
|
||||
|
||||
for (int i = 0; i < tickCountX; ++i) {
|
||||
const double xv = (i == tickCountX - 1) ? xmax : (xmin + i * xstep);
|
||||
// Map tick position to closest image index
|
||||
int64_t imgIdx = static_cast<int64_t>(std::llround(xv));
|
||||
if (imgIdx < 0) imgIdx = 0;
|
||||
if (imgIdx > lastIdx) imgIdx = lastIdx;
|
||||
|
||||
double angleDeg = 0.0;
|
||||
if (lastIdx >= 0) {
|
||||
angleDeg = goniometer_axis->GetAngle_deg(imgIdx);
|
||||
}
|
||||
|
||||
QString lab = QString::number(angleDeg, 'f', 2);
|
||||
axXcat->append(lab, xv);
|
||||
}
|
||||
|
||||
chart()->addAxis(axXcat, Qt::AlignBottom);
|
||||
series->attachAxis(axXcat);
|
||||
currentSeries->attachAxis(axXcat);
|
||||
} else {
|
||||
axisX->setLabelsVisible(true);
|
||||
axisX->setTitleText(QStringLiteral("Image number"));
|
||||
}
|
||||
}
|
||||
|
||||
// Set Y-axis behavior according to options
|
||||
// ----- Y-axis handling -----
|
||||
QValueAxis *axisY = qobject_cast<QValueAxis *>(chart()->axisY(series));
|
||||
if (axisY) {
|
||||
if (std::isfinite(dispMin) && std::isfinite(dispMax)) {
|
||||
@@ -140,11 +178,15 @@ void JFJochChartView::updateChart() {
|
||||
}
|
||||
}
|
||||
|
||||
// If Y is in 1/d^2, hide numeric labels and add a category axis with d labels
|
||||
if (m_yOneOverD) {
|
||||
axisY->setLabelsVisible(false); // keep grid/ticks from value axis
|
||||
// Keep value axis for numeric range + grid, but move it to the RIGHT
|
||||
axisY->setLabelsVisible(false);
|
||||
chart()->removeAxis(axisY);
|
||||
chart()->addAxis(axisY, Qt::AlignRight);
|
||||
series->attachAxis(axisY);
|
||||
currentSeries->attachAxis(axisY);
|
||||
|
||||
// Build a mirrored visible axis with labels in d (Å)
|
||||
// Build a mirrored visible axis with labels in d (Å) on the LEFT
|
||||
auto *axYcat = new QCategoryAxis();
|
||||
axYcat->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
|
||||
axYcat->setGridLineVisible(false);
|
||||
@@ -169,6 +211,7 @@ void JFJochChartView::updateChart() {
|
||||
}
|
||||
axYcat->append(lab, yv);
|
||||
}
|
||||
|
||||
chart()->addAxis(axYcat, Qt::AlignLeft);
|
||||
series->attachAxis(axYcat);
|
||||
currentSeries->attachAxis(axYcat);
|
||||
@@ -180,7 +223,12 @@ void JFJochChartView::updateChart() {
|
||||
chart()->setMargins(m);
|
||||
}
|
||||
} else {
|
||||
// Normal numeric labels
|
||||
// Normal numeric labels, axis on the LEFT
|
||||
chart()->removeAxis(axisY);
|
||||
chart()->addAxis(axisY, Qt::AlignLeft);
|
||||
series->attachAxis(axisY);
|
||||
currentSeries->attachAxis(axisY);
|
||||
|
||||
axisY->setTitleText(QString());
|
||||
axisY->setLabelsVisible(true);
|
||||
}
|
||||
@@ -188,14 +236,14 @@ void JFJochChartView::updateChart() {
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochChartView::setBinning(int64_t val) {
|
||||
void JFJochDatasetInfoChartView::setBinning(int64_t val) {
|
||||
if (val >= 1) {
|
||||
binning = val;
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochChartView::contextMenuEvent(QContextMenuEvent *event) {
|
||||
void JFJochDatasetInfoChartView::contextMenuEvent(QContextMenuEvent *event) {
|
||||
QMenu menu(this);
|
||||
QAction *copyXY = menu.addAction("Copy (x y) points");
|
||||
copyXY->setEnabled(!values.empty());
|
||||
@@ -207,6 +255,11 @@ void JFJochChartView::contextMenuEvent(QContextMenuEvent *event) {
|
||||
actMinYZero->setCheckable(true);
|
||||
actMinYZero->setChecked(m_minYZeroEnabled);
|
||||
|
||||
QAction *actXGoniometer = menu.addAction("Use goniometer X-axis");
|
||||
actXGoniometer->setCheckable(true);
|
||||
actXGoniometer->setChecked(m_xUseGoniometerAxis);
|
||||
actXGoniometer->setEnabled(goniometer_axis.has_value());
|
||||
|
||||
QAction *chosen = menu.exec(event->globalPos());
|
||||
if (chosen == copyXY) {
|
||||
QString out;
|
||||
@@ -222,10 +275,13 @@ void JFJochChartView::contextMenuEvent(QContextMenuEvent *event) {
|
||||
} else if (chosen == actMinYZero) {
|
||||
m_minYZeroEnabled = !m_minYZeroEnabled;
|
||||
updateChart();
|
||||
} else if (chosen == actXGoniometer) {
|
||||
m_xUseGoniometerAxis = !m_xUseGoniometerAxis;
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
void JFJochDatasetInfoChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
QChartView::mouseMoveEvent(event);
|
||||
if (!series || values.empty())
|
||||
return;
|
||||
@@ -255,6 +311,7 @@ void JFJochChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
// Status bar text
|
||||
const double yv = values[static_cast<size_t>(idx)];
|
||||
QString text;
|
||||
|
||||
if (m_yOneOverD) {
|
||||
if (std::isfinite(yv) && yv > 0.0) {
|
||||
const double d = 1.0 / std::sqrt(yv);
|
||||
@@ -286,7 +343,7 @@ void JFJochChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochChartView::leaveEvent(QEvent *event) {
|
||||
void JFJochDatasetInfoChartView::leaveEvent(QEvent *event) {
|
||||
QChartView::leaveEvent(event);
|
||||
if (m_hoverLine) {
|
||||
chart()->scene()->removeItem(m_hoverLine);
|
||||
@@ -298,7 +355,7 @@ void JFJochChartView::leaveEvent(QEvent *event) {
|
||||
emit writeStatusBar(QString(), 0);
|
||||
}
|
||||
|
||||
void JFJochChartView::onHoverLoadTimeout() {
|
||||
void JFJochDatasetInfoChartView::onHoverLoadTimeout() {
|
||||
if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier))
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <QTimer>
|
||||
#include "../../common/GoniometerAxis.h"
|
||||
|
||||
class JFJochChartView : public QChartView {
|
||||
class JFJochDatasetInfoChartView : public QChartView {
|
||||
Q_OBJECT
|
||||
|
||||
QLineSeries *series = nullptr;
|
||||
@@ -27,7 +27,7 @@ class JFJochChartView : public QChartView {
|
||||
|
||||
bool m_minYZeroEnabled = true;
|
||||
bool m_yOneOverD = false;
|
||||
|
||||
bool m_xUseGoniometerAxis = true;
|
||||
|
||||
std::optional<GoniometerAxis> goniometer_axis;
|
||||
void updateChart();
|
||||
@@ -46,7 +46,7 @@ private:
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
|
||||
public:
|
||||
explicit JFJochChartView(QWidget *parent = nullptr);
|
||||
explicit JFJochDatasetInfoChartView(QWidget *parent = nullptr);
|
||||
void setImage(int64_t val);
|
||||
|
||||
public slots:
|
||||
Reference in New Issue
Block a user