// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include #include #include #include #include #include #include "JFJochViewerSidePanel.h" #include "widgets/TitleLabel.h" #include "JFJochViewerImageStatistics.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)); auto spotToggleCheckBox = new QCheckBox("Show spots", this); spotToggleCheckBox->setCheckState(Qt::CheckState::Checked); layout->addWidget(spotToggleCheckBox); // Add checkbox to the grid layout connect(spotToggleCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::spotsToggled); auto highlightIceRingToggleCheckBox = new QCheckBox("Highlight spots on ice rings", this); highlightIceRingToggleCheckBox->setCheckState(Qt::CheckState::Checked); layout->addWidget(highlightIceRingToggleCheckBox); connect(highlightIceRingToggleCheckBox, &QCheckBox::toggled, this,&JFJochViewerSidePanel::highlightIceRingsToggled); auto predictionsToggleCheckBox = new QCheckBox("Show predictions", this); predictionsToggleCheckBox->setCheckState(Qt::CheckState::Unchecked); layout->addWidget(predictionsToggleCheckBox); // Add checkbox to the grid layout connect(predictionsToggleCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::predictionsToggled); resRingsCheckBox = new QCheckBox("Show resolution rings", this); resRingsCheckBox->setCheckState(Qt::Unchecked); layout->addWidget(resRingsCheckBox); autoResRingsCheckBox = new QCheckBox("Resolution rings auto.", this); res_rings_edit = new QLineEdit(this); res_rings_edit->setPlaceholderText("Enter non-negative floats, e.g., 1.0,2.5,3.7"); res_rings_edit->setEnabled(false); // Initially disabled as "Auto Res Rings" is checked by default layout->addWidget(autoResRingsCheckBox); // Add the checkbox to the grid layout layout->addWidget(res_rings_edit); // Add the line edit to the grid layout auto highestPixelsComboBox = new QComboBox(this); highestPixelsComboBox->addItem("0 highest pixels", 0); highestPixelsComboBox->addItem("1 highest pixel", 1); highestPixelsComboBox->addItem("2 highest pixels", 2); highestPixelsComboBox->addItem("3 highest pixels", 3); highestPixelsComboBox->addItem("5 highest pixels", 5); highestPixelsComboBox->addItem("10 highest pixels", 10); highestPixelsComboBox->setCurrentIndex(0); layout->addWidget(highestPixelsComboBox); 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); layout->addWidget(saturatedPixelsCheckBox); connect(saturatedPixelsCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::saturatedPixelsToggled); auto colorSelectButton = new QPushButton("Select feature color", this); layout->addWidget(colorSelectButton); 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); layout->addWidget(spotColorSelectButton); connect(spotColorSelectButton, &QPushButton::clicked, this, [this]() { QColor color = QColorDialog::getColor(Qt::green, this, "Select Spot Color"); if (color.isValid()) { emit setSpotColor(color); } }); 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("Data analysis (experimental)", 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); layout->addWidget(calibrantCombo); updateCalibrantList(); auto findBeamCenterButton = new QPushButton("Guess detector calibration", this); connect(findBeamCenterButton, &QPushButton::clicked,this, &JFJochViewerSidePanel::findBeamCenterClicked); layout->addWidget(findBeamCenterButton); auto optimizeBeamCenterButton = new QPushButton("Refine detector calibration", this); connect(optimizeBeamCenterButton, &QPushButton::clicked,this, &JFJochViewerSidePanel::optimizeBeamCenterClicked); layout->addWidget(optimizeBeamCenterButton); // 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 ice_rings(ICE_RING_RES_A.begin(), ICE_RING_RES_A.end()); setRings(ice_rings); }); layout->addWidget(iceRingsButton); layout->addStretch(); setLayout(layout); // Set the layout to the widget } 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() { 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; } emit findBeamCenter(uc, false); } void JFJochViewerSidePanel::findBeamCenterClicked() { 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; } emit findBeamCenter(uc, true); } void JFJochViewerSidePanel::setRings(const QVector &v) { QString txt; for (float i : v) { txt += QString::number(i, 'f', 3) + ","; } 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 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 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); }