// 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); auto findBeamCenterButton = new QPushButton("Calibrate detector (LaB6)", this); connect(findBeamCenterButton, &QPushButton::clicked,[this] {emit findBeamCenter();}); layout->addWidget(findBeamCenterButton); // 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::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) { 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); }