320 lines
12 KiB
C++
320 lines
12 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include <QCheckBox>
|
|
#include <QLabel>
|
|
#include <QHBoxLayout>
|
|
#include <QLineEdit>
|
|
#include <QPushButton>
|
|
#include <QColorDialog>
|
|
#include <QComboBox>
|
|
|
|
#include "JFJochViewerSidePanel.h"
|
|
|
|
#include "JFJochViewerImageROIStatistics.h"
|
|
#include "widgets/TitleLabel.h"
|
|
#include "JFJochViewerImageStatistics.h"
|
|
#include "../image_analysis/geom_refinement/AssignSpotsToRings.h"
|
|
|
|
JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent) {
|
|
|
|
auto layout = new QVBoxLayout(this);
|
|
|
|
layout->addWidget(new TitleLabel("Image statistics", this));
|
|
|
|
auto stats = new JFJochViewerImageStatistics(this);
|
|
layout->addWidget(stats);
|
|
connect(this, &JFJochViewerSidePanel::imageLoaded, stats, &JFJochViewerImageStatistics::loadImage);
|
|
|
|
layout->addWidget(new TitleLabel("Image features", this));
|
|
|
|
// Image features...
|
|
auto spotToggleCheckBox = new QCheckBox("Show spots", this);
|
|
spotToggleCheckBox->setCheckState(Qt::CheckState::Checked);
|
|
|
|
connect(spotToggleCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::spotsToggled);
|
|
|
|
auto highlightIceRingToggleCheckBox = new QCheckBox("Highlight spots on ice rings", this);
|
|
highlightIceRingToggleCheckBox->setCheckState(Qt::CheckState::Checked);
|
|
connect(highlightIceRingToggleCheckBox, &QCheckBox::toggled, this,&JFJochViewerSidePanel::highlightIceRingsToggled);
|
|
|
|
auto predictionsToggleCheckBox = new QCheckBox("Show predictions", this);
|
|
predictionsToggleCheckBox->setCheckState(Qt::CheckState::Unchecked);
|
|
|
|
connect(predictionsToggleCheckBox, &QCheckBox::toggled, this,
|
|
&JFJochViewerSidePanel::predictionsToggled);
|
|
|
|
resRingsCheckBox = new QCheckBox("Show resolution rings", this);
|
|
resRingsCheckBox->setCheckState(Qt::Unchecked);
|
|
|
|
autoResRingsCheckBox = new QCheckBox("Resolution rings auto.", this);
|
|
res_rings_edit = new QLineEdit(this);
|
|
res_rings_edit->setPlaceholderText("Ring positions, e.g., 1.0,2.5,3.7");
|
|
res_rings_edit->setEnabled(false); // Initially disabled as "Auto Res Rings" is checked by default
|
|
|
|
auto highestPixelsComboBox = new QComboBox(this);
|
|
highestPixelsComboBox->addItem("Show 0 highest pixels", 0);
|
|
highestPixelsComboBox->addItem("Show 1 highest pixel", 1);
|
|
highestPixelsComboBox->addItem("Show 2 highest pixels", 2);
|
|
highestPixelsComboBox->addItem("Show 3 highest pixels", 3);
|
|
highestPixelsComboBox->addItem("Show 5 highest pixels", 5);
|
|
highestPixelsComboBox->addItem("Show 10 highest pixels", 10);
|
|
highestPixelsComboBox->setCurrentIndex(0);
|
|
|
|
connect(highestPixelsComboBox, &QComboBox::currentIndexChanged, this, [this, highestPixelsComboBox](int index) {
|
|
int value = highestPixelsComboBox->itemData(index).toInt();
|
|
emit showHighestPixels(value);
|
|
});
|
|
|
|
auto saturatedPixelsCheckBox = new QCheckBox("Show saturated pixels", this);
|
|
|
|
connect(saturatedPixelsCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::saturatedPixelsToggled);
|
|
|
|
auto colorSelectButton = new QPushButton("Select feature color", this);
|
|
|
|
connect(colorSelectButton, &QPushButton::clicked, this, [this]() {
|
|
QColor color = QColorDialog::getColor(Qt::magenta, this, "Select Feature Color");
|
|
if (color.isValid()) {
|
|
emit setFeatureColor(color);
|
|
}
|
|
});
|
|
|
|
auto spotColorSelectButton = new QPushButton("Select spot color", this);
|
|
|
|
connect(spotColorSelectButton, &QPushButton::clicked, this, [this]() {
|
|
QColor color = QColorDialog::getColor(Qt::green, this, "Select Spot Color");
|
|
if (color.isValid()) {
|
|
emit setSpotColor(color);
|
|
}
|
|
});
|
|
|
|
auto image_feature_grid = new QGridLayout();
|
|
image_feature_grid->addWidget(spotToggleCheckBox, 0, 0);
|
|
image_feature_grid->addWidget(highlightIceRingToggleCheckBox, 0, 1);
|
|
image_feature_grid->addWidget(predictionsToggleCheckBox, 1, 0);
|
|
image_feature_grid->addWidget(resRingsCheckBox, 2, 0);
|
|
image_feature_grid->addWidget(autoResRingsCheckBox, 2, 1);
|
|
image_feature_grid->addWidget(res_rings_edit, 3, 0, 1, 2);
|
|
image_feature_grid->addWidget(saturatedPixelsCheckBox, 4, 0);
|
|
image_feature_grid->addWidget(highestPixelsComboBox, 4, 1);
|
|
image_feature_grid->addWidget(colorSelectButton, 5, 0);
|
|
image_feature_grid->addWidget(spotColorSelectButton, 5, 1);
|
|
|
|
layout->addLayout(image_feature_grid);
|
|
|
|
connect(autoResRingsCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::enableAutoResRings);
|
|
|
|
connect(res_rings_edit, &QLineEdit::editingFinished,
|
|
this, &JFJochViewerSidePanel::editingFinished);
|
|
|
|
connect(resRingsCheckBox, &QCheckBox::toggled,
|
|
this, &JFJochViewerSidePanel::enableResRings);
|
|
|
|
layout->addWidget(new TitleLabel("Image statistics plot", this));
|
|
chart = new JFJochViewerSidePanelChart(this);
|
|
layout->addWidget(chart);
|
|
|
|
connect(this, &JFJochViewerSidePanel::imageLoaded,
|
|
chart, &JFJochViewerSidePanelChart::loadImage);
|
|
|
|
layout->addWidget(new TitleLabel("ROI", this));
|
|
|
|
roi = new JFJochViewerImageROIStatistics(this);
|
|
layout->addWidget(roi);
|
|
connect(this, &JFJochViewerSidePanel::imageLoaded, roi, &JFJochViewerImageROIStatistics::loadImage);
|
|
|
|
connect(roi, &JFJochViewerImageROIStatistics::ROIBoxConfigured, [this] (QRect box) {
|
|
emit ROIBoxConfigured(box);
|
|
});
|
|
|
|
connect(roi, &JFJochViewerImageROIStatistics::ROICircleConfigured, [this] (double x, double y, double radius) {
|
|
emit ROICircleConfigured(x, y, radius);
|
|
});
|
|
|
|
connect(roi, &JFJochViewerImageROIStatistics::AddROIToUserMask, [this]() { emit AddROIToUserMask(); });
|
|
connect(roi, &JFJochViewerImageROIStatistics::SubtractROIFromUserMask, [this]() { emit SubtractROIFromUserMask(); });
|
|
|
|
layout->addWidget(new TitleLabel("Data analysis", this));
|
|
|
|
auto analyzeButton = new QPushButton("Full analysis", this);
|
|
connect(analyzeButton, &QPushButton::clicked,[this] {emit analyze();});
|
|
layout->addWidget(analyzeButton);
|
|
|
|
// Calibrant selection combo box
|
|
layout->addWidget(new TitleLabel("Powder geometry calibration", this));
|
|
|
|
calibrantCombo = new QComboBox(this);
|
|
updateCalibrantList();
|
|
|
|
auto findBeamCenterButton = new QPushButton("Guess detector calibration", this);
|
|
connect(findBeamCenterButton, &QPushButton::clicked,this, &JFJochViewerSidePanel::findBeamCenterClicked);
|
|
|
|
auto optimizeBeamCenterButton = new QPushButton("Refine detector calibration", this);
|
|
connect(optimizeBeamCenterButton, &QPushButton::clicked,this, &JFJochViewerSidePanel::optimizeBeamCenterClicked);
|
|
|
|
auto calibrantRingsButton = new QPushButton("Display calibrant rings", this);
|
|
connect(calibrantRingsButton, &QPushButton::clicked, this, [this]() {
|
|
std::vector<float> rings = CalculateXtalRings(GetCalibrant(), 10);
|
|
QVector<float> q_rings;
|
|
for (float ring : rings) {
|
|
q_rings.append(2 * M_PI / ring);
|
|
}
|
|
setRings(q_rings);
|
|
});
|
|
|
|
// Add preset ice rings button below LaB6 calibration
|
|
auto iceRingsButton = new QPushButton("Display ice rings", this);
|
|
connect(iceRingsButton, &QPushButton::clicked, this, [this]() {
|
|
// Set manual rings and enable display
|
|
const QVector<float> ice_rings(ICE_RING_RES_A.begin(), ICE_RING_RES_A.end());
|
|
setRings(ice_rings);
|
|
});
|
|
|
|
auto refine_row = new QGridLayout();
|
|
refine_row->setSpacing(12);
|
|
refine_row->addWidget(new QLabel("Calibrant:"),0,0);
|
|
refine_row->addWidget(calibrantCombo,0,1);
|
|
refine_row->addWidget(findBeamCenterButton,1, 0);
|
|
refine_row->addWidget(optimizeBeamCenterButton,1, 1);
|
|
|
|
refine_row->addWidget(calibrantRingsButton,2, 0);
|
|
refine_row->addWidget(iceRingsButton,2, 1);
|
|
|
|
layout->addLayout(refine_row);
|
|
|
|
layout->addStretch();
|
|
setLayout(layout); // Set the layout to the widget
|
|
}
|
|
|
|
UnitCell JFJochViewerSidePanel::GetCalibrant() const {
|
|
UnitCell uc(LAB6_CELL_A, LAB6_CELL_A, LAB6_CELL_A, 90, 90, 90);
|
|
switch (calibrantCombo->currentIndex()) {
|
|
case 1:
|
|
// T. C. Huang , H. Toraya, T. N. Blanton, Y. Wu, J. Appl. Cryst. 26 (1993), 180-184.
|
|
uc = UnitCell(5.1769, 4.7218, 58.380, 89.440, 89.634, 75.854);
|
|
break;
|
|
case 2:
|
|
if (sample_cell)
|
|
uc = sample_cell.value();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return uc;
|
|
}
|
|
|
|
void JFJochViewerSidePanel::updateCalibrantList() {
|
|
calibrantCombo->clear();
|
|
|
|
calibrantCombo->addItem("LaB6");
|
|
calibrantCombo->addItem("Silver Behenate");
|
|
if (sample_cell)
|
|
calibrantCombo->addItem(QString("Current sample (%1 %2 %3 %4 %5 %6)")
|
|
.arg(QString::number(sample_cell->a, 'f', 1))
|
|
.arg(QString::number(sample_cell->b, 'f', 1))
|
|
.arg(QString::number(sample_cell->c, 'f', 1))
|
|
.arg(QString::number(sample_cell->alpha, 'f', 1))
|
|
.arg(QString::number(sample_cell->beta, 'f', 1))
|
|
.arg(QString::number(sample_cell->gamma, 'f', 1)));
|
|
calibrantCombo->setCurrentIndex(0);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::optimizeBeamCenterClicked() {
|
|
|
|
emit findBeamCenter(GetCalibrant(), false);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::findBeamCenterClicked() {
|
|
emit findBeamCenter(GetCalibrant(), true);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::setRings(const QVector <float> &v) {
|
|
QString txt;
|
|
for (float i : v) {
|
|
txt += QString::number(i, 'f', 4) + ",";
|
|
}
|
|
|
|
resRingsCheckBox->setChecked(true);
|
|
autoResRingsCheckBox->setChecked(false);
|
|
res_rings_edit->setEnabled(true);
|
|
res_rings_edit->setText(txt);
|
|
emit setResRings(v);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::enableAutoResRings(bool input) {
|
|
res_rings_edit->setEnabled(!input); // Enable line edit only if "Auto Res Rings" is unchecked
|
|
if (input) {
|
|
emit autoResRings();
|
|
} else {
|
|
editingFinished();
|
|
}
|
|
}
|
|
|
|
void JFJochViewerSidePanel::spotsToggled(bool input) {
|
|
emit showSpots(input);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::predictionsToggled(bool input) {
|
|
emit showPredictions(input);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::editingFinished() {
|
|
QString text = res_rings_edit->text();
|
|
QStringList stringList = text.split(',', Qt::SkipEmptyParts);
|
|
QVector<float> manualResRings;
|
|
|
|
bool validInput = true;
|
|
for (const QString &str: stringList) {
|
|
bool ok;
|
|
float value = str.toFloat(&ok);
|
|
if (ok && value >= 0) {
|
|
manualResRings.append(value);
|
|
} else {
|
|
validInput = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!validInput) {
|
|
res_rings_edit->setStyleSheet("border: 1px solid red;"); // Highlight error
|
|
} else {
|
|
res_rings_edit->setStyleSheet(""); // Reset to default style
|
|
emit setResRings(manualResRings); // Update resolution ring vector
|
|
}
|
|
}
|
|
|
|
void JFJochViewerSidePanel::loadImage(std::shared_ptr<const JFJochReaderImage> image) {
|
|
if (image)
|
|
sample_cell = image->Dataset().experiment.GetUnitCell();
|
|
updateCalibrantList();
|
|
|
|
emit imageLoaded(image);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::enableResRings(bool input) {
|
|
if (!input) {
|
|
autoResRingsCheckBox->setEnabled(false);
|
|
res_rings_edit->setEnabled(false);
|
|
emit setResRings({});
|
|
} else {
|
|
autoResRingsCheckBox->setEnabled(true);
|
|
enableAutoResRings(autoResRingsCheckBox->isChecked());
|
|
}
|
|
}
|
|
|
|
void JFJochViewerSidePanel::saturatedPixelsToggled(bool input) {
|
|
emit showSaturatedPixels(input);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::highlightIceRingsToggled(bool input) {
|
|
emit highlightIceRings(input);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::SetROIBox(QRect box) {
|
|
roi->SetROIBox(box);
|
|
}
|
|
|
|
void JFJochViewerSidePanel::SetROICircle(double x, double y, double radius) {
|
|
roi->SetROICircle(x, y, radius);
|
|
}
|