/
klischa
/
AstraScanner2
Обзор
Документация
Войти
/
klischa
/
AstraScanner2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/controllers/ScanTabBinder.cpp
246 строк
11 KB
Arena Agent
ui: add contextual help for quality coverage summary
19 июн 2026, 15:25
19 июн 2026, 15:25
942953a
Код
Авторство
О чём код?
#include "ScanTabBinder.h" #include <QTabWidget> #include <QWidget> #include <QVBoxLayout> #include <QHBoxLayout> #include <QGridLayout> #include <QPushButton> #include <QLabel> #include <QGroupBox> #include <QCheckBox> #include <QSpinBox> #include <QComboBox> #include <QProgressBar> #include <QStackedWidget> #include <QSizePolicy> #include <QVTKOpenGLNativeWidget.h> ScanTabBinder::ScanTabBinder(QObject *parent) : QObject(parent) { } ScanTabBinder::Widgets ScanTabBinder::bind(QTabWidget *tabWidget, QWidget *owner) { Widgets w; QWidget* scanTab = new QWidget(); tabWidget->addTab(scanTab, "Сканирование"); QVBoxLayout* scanLayout = new QVBoxLayout(scanTab); QHBoxLayout* trackingLayout = new QHBoxLayout(); QLabel* trackingLabel = new QLabel("Режим трекинга:", owner); w.trackingModeCombo = new QComboBox(owner); w.trackingModeCombo->addItem("ICP", "icp"); w.trackingModeCombo->addItem("Маркеры", "marker_based"); w.trackingModeCombo->addItem("Нейросетевой", "neural_based"); w.trackingHelpBtn = new QPushButton("?", owner); w.trackingHelpBtn->setFixedWidth(28); w.trackingHelpBtn->setToolTip("Открыть справку по режимам трекинга"); trackingLayout->addWidget(trackingLabel); trackingLayout->addWidget(w.trackingModeCombo); trackingLayout->addWidget(w.trackingHelpBtn); trackingLayout->addStretch(); scanLayout->addLayout(trackingLayout); QHBoxLayout* buttonLayout = new QHBoxLayout(); w.previewBtn = new QPushButton("Preview", owner); w.scanBtn = new QPushButton("Scan", owner); w.pauseBtn = new QPushButton("Pause", owner); w.stopBtn = new QPushButton("Stop", owner); w.clearBtn = new QPushButton("Clear", owner); w.pauseBtn->setEnabled(false); w.stopBtn->setEnabled(false); buttonLayout->addWidget(w.previewBtn); buttonLayout->addWidget(w.scanBtn); buttonLayout->addWidget(w.pauseBtn); buttonLayout->addWidget(w.stopBtn); buttonLayout->addWidget(w.clearBtn); buttonLayout->addStretch(); scanLayout->addLayout(buttonLayout); QHBoxLayout* viewerToolbarLayout = new QHBoxLayout(); viewerToolbarLayout->addWidget(new QLabel("Просмотр:", owner)); w.show3dBtn = new QPushButton("3D", owner); w.showQualityBtn = new QPushButton("Quality", owner); w.showRgbBtn = new QPushButton("RGB", owner); w.showDepthBtn = new QPushButton("Depth", owner); for (QPushButton* btn : {w.show3dBtn, w.showQualityBtn, w.showRgbBtn, w.showDepthBtn}) { btn->setCheckable(true); btn->setAutoExclusive(true); } w.show3dBtn->setChecked(true); viewerToolbarLayout->addWidget(w.show3dBtn); viewerToolbarLayout->addWidget(w.showQualityBtn); viewerToolbarLayout->addWidget(w.showRgbBtn); viewerToolbarLayout->addWidget(w.showDepthBtn); viewerToolbarLayout->addStretch(); scanLayout->addLayout(viewerToolbarLayout); w.viewerStack = new QStackedWidget(owner); w.viewerStack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QWidget* vtkPage = new QWidget(owner); QVBoxLayout* vtkLayout = new QVBoxLayout(vtkPage); vtkLayout->setContentsMargins(0, 0, 0, 0); vtkLayout->setSpacing(0); w.vtkWidget = new QVTKOpenGLNativeWidget(owner); w.vtkWidget->setMinimumSize(640, 480); w.vtkWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); vtkLayout->addWidget(w.vtkWidget); w.viewerStack->addWidget(vtkPage); QWidget* rgbPage = new QWidget(owner); QVBoxLayout* rgbLayout = new QVBoxLayout(rgbPage); rgbLayout->setContentsMargins(0, 0, 0, 0); rgbLayout->setSpacing(0); w.rgbLabel = new QLabel("RGB Stream", owner); w.rgbLabel->setMinimumSize(640, 480); w.rgbLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); w.rgbLabel->setStyleSheet("border: 1px solid gray; background-color: black;"); w.rgbLabel->setAlignment(Qt::AlignCenter); rgbLayout->addWidget(w.rgbLabel); w.viewerStack->addWidget(rgbPage); QWidget* depthPage = new QWidget(owner); QVBoxLayout* depthLayout = new QVBoxLayout(depthPage); depthLayout->setContentsMargins(0, 0, 0, 0); depthLayout->setSpacing(0); w.depthLabel = new QLabel("Depth Stream", owner); w.depthLabel->setMinimumSize(640, 480); w.depthLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); w.depthLabel->setStyleSheet("border: 1px solid gray; background-color: black;"); w.depthLabel->setAlignment(Qt::AlignCenter); depthLayout->addWidget(w.depthLabel); w.viewerStack->addWidget(depthPage); scanLayout->addWidget(w.viewerStack, 1); QGroupBox *runtimeGroup = new QGroupBox("Статус сканирования и поворотный стол", owner); runtimeGroup->setFlat(true); QVBoxLayout *runtimeLayout = new QVBoxLayout(runtimeGroup); runtimeLayout->setContentsMargins(6, 4, 6, 4); runtimeLayout->setSpacing(6); QHBoxLayout *telemetryLayout = new QHBoxLayout(); telemetryLayout->addWidget(new QLabel("Distance:", owner)); w.distanceIndicator = new QProgressBar(owner); w.distanceIndicator->setRange(0, 2000); w.distanceIndicator->setValue(0); w.distanceIndicator->setFormat("%v mm"); w.distanceIndicator->setMinimumWidth(220); w.distanceIndicator->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); telemetryLayout->addWidget(w.distanceIndicator, 1); w.scannerPoseLabel = new QLabel("Поза: недоступно", owner); w.scannerPoseLabel->setStyleSheet("font-weight: bold;"); w.scannerPoseLabel->setMinimumWidth(320); w.scannerPoseLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); telemetryLayout->addWidget(w.scannerPoseLabel); runtimeLayout->addLayout(telemetryLayout); QHBoxLayout *turntableLayout = new QHBoxLayout(); turntableLayout->addWidget(new QLabel("Поворотный стол:", owner)); w.turntableEnableChk = new QCheckBox("Вкл.", owner); w.turntableEnableChk->setToolTip( "Периодически сохраняет текущее облако как скан проекта и очищает " "накопитель. Работает только при открытом проекте и активном сканировании."); turntableLayout->addWidget(w.turntableEnableChk); w.turntableHelpBtn = new QPushButton("?", owner); w.turntableHelpBtn->setFixedWidth(28); w.turntableHelpBtn->setToolTip("Открыть справку по режиму поворотного стола"); turntableLayout->addWidget(w.turntableHelpBtn); w.turntableModeCombo = new QComboBox(owner); w.turntableModeCombo->addItem("Отдельные", "separate"); w.turntableModeCombo->addItem("Накопление", "accumulate"); w.turntableModeCombo->setCurrentIndex(1); w.turntableModeCombo->setToolTip("Отдельные - каждый скан отдельно. Накопление - всё в одно облако"); turntableLayout->addWidget(new QLabel("Режим:", owner)); turntableLayout->addWidget(w.turntableModeCombo); turntableLayout->addWidget(new QLabel("Интервал:", owner)); w.turntableIntervalSpin = new QSpinBox(owner); w.turntableIntervalSpin->setRange(1, 600); w.turntableIntervalSpin->setSuffix(" сек"); w.turntableIntervalSpin->setValue(10); w.turntableIntervalSpin->setMaximumWidth(110); turntableLayout->addWidget(w.turntableIntervalSpin); turntableLayout->addWidget(new QLabel("Сканов:", owner)); w.turntableCountSpin = new QSpinBox(owner); w.turntableCountSpin->setRange(1, 360); w.turntableCountSpin->setValue(12); w.turntableCountSpin->setMaximumWidth(90); w.turntableCountSpin->setToolTip("Остановить режим после стольких сохранённых сканов."); turntableLayout->addWidget(w.turntableCountSpin); w.turntableStatusLabel = new QLabel("Сохранено 0 сканов", owner); w.turntableStatusLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); turntableLayout->addWidget(w.turntableStatusLabel, 1); runtimeLayout->addLayout(turntableLayout); QGroupBox *qualityGroup = new QGroupBox("Качество сканирования", owner); QGridLayout *qualityLayout = new QGridLayout(qualityGroup); auto createQualityValue = [owner](const QString &text) { QLabel *label = new QLabel(text, owner); label->setStyleSheet("padding: 6px 10px; border: 1px solid #b8c0cb; border-radius: 6px; background: #f4f6f9; font-weight: 600;"); label->setMinimumWidth(190); return label; }; qualityLayout->addWidget(new QLabel("Трекинг:", owner), 0, 0); w.trackingQualityLabel = createQualityValue("Ожидание данных"); qualityLayout->addWidget(w.trackingQualityLabel, 0, 1); qualityLayout->addWidget(new QLabel("Совмещение:", owner), 0, 2); w.alignmentQualityLabel = createQualityValue("Нет оценки"); qualityLayout->addWidget(w.alignmentQualityLabel, 0, 3); qualityLayout->addWidget(new QLabel("Depth quality:", owner), 1, 0); w.depthQualityLabel = createQualityValue("Нет оценки"); qualityLayout->addWidget(w.depthQualityLabel, 1, 1); qualityLayout->addWidget(new QLabel("Drift risk:", owner), 1, 2); w.driftQualityLabel = createQualityValue("Нет оценки"); qualityLayout->addWidget(w.driftQualityLabel, 1, 3); QWidget *legendWidget = new QWidget(owner); QHBoxLayout *legendLayout = new QHBoxLayout(legendWidget); legendLayout->setContentsMargins(0, 2, 0, 0); legendLayout->setSpacing(8); legendLayout->addWidget(new QLabel("Легенда Quality:", owner)); auto makeLegend = [owner](const QString &text, const QString &bg, const QString &border) { QLabel *label = new QLabel(text, owner); label->setStyleSheet(QString( "padding: 4px 8px; border: 1px solid %1; border-radius: 5px; background: %2; font-weight: 600;") .arg(border, bg)); return label; }; legendLayout->addWidget(makeLegend("Слабо", "#fdeeee", "#d27a7a")); legendLayout->addWidget(makeLegend("Средне", "#fff7e8", "#d8aa57")); legendLayout->addWidget(makeLegend("Уверенно", "#eef8ef", "#78b48a")); legendLayout->addStretch(); runtimeLayout->addWidget(qualityGroup); runtimeLayout->addWidget(legendWidget); QHBoxLayout *coverageRow = new QHBoxLayout(); w.coverageSummaryLabel = new QLabel("Coverage: нет данных", owner); w.coverageSummaryLabel->setStyleSheet("padding: 6px 10px; border: 1px solid #b8c0cb; border-radius: 6px; background: #f8fafc; font-weight: 600;"); coverageRow->addWidget(w.coverageSummaryLabel, 1); w.coverageHelpBtn = new QPushButton("?", owner); w.coverageHelpBtn->setFixedWidth(28); w.coverageHelpBtn->setToolTip("Открыть справку по режиму Quality и Coverage summary"); coverageRow->addWidget(w.coverageHelpBtn); runtimeLayout->addLayout(coverageRow); scanLayout->addWidget(runtimeGroup); return w; }