Files
pyGUI/src/pygui/ISC/Tabs/components/ComparisonSidePanel.qml
T

240 lines
9.0 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ISC
// Right-column bento stack: DTW readout, overlap settings, frame/time
// controls, and scrubber readout. Pure view — all comparison state lives
// on DataTab; scrubber writes go up via frameIndexRequested.
ColumnLayout {
id: panel
required property real warpingDistance
required property int frameOffset
required property real frameOffsetSeconds
required property real maxSensorDeviation
required property int seriesLen
required property bool hasSeries
required property var trendDataA
required property var trendDataB
required property var timeDataA
required property var timeDataB
required property var frameForTime // function(seconds) -> frame index
required property real frameIndex // bound from ComparisonChartCard.frameIndex
// Overlap settings outputs (consumed by OverlapMapCard via DataTab)
property alias overlapEnabled: enableOverlapCheck.checked
property alias blendAmount: blendSlider.value
signal frameIndexRequested(real idx)
signal scrubberFocusRequested()
spacing: 12
// Bento 3: DTW Comparison Readout
PanelBox {
Layout.fillWidth: true
Layout.preferredHeight: readoutCol.implicitHeight + 24
ColumnLayout {
id: readoutCol
anchors.fill: parent
anchors.margins: 12
spacing: 8
SectionTitle { text: "DTW READOUT" }
ReadoutStat {
label: "DTW ALIGNMENT DISTANCE"
value: panel.warpingDistance >= 0 ? panel.warpingDistance.toFixed(2) : "--"
valueColor: panel.warpingDistance >= 0
? (panel.warpingDistance < 50 ? Theme.metricGood : panel.warpingDistance < 100 ? Theme.metricWarn : Theme.metricBad)
: Theme.sideMutedText
}
ReadoutStat {
label: "TEMPORAL FRAME OFFSET"
value: panel.warpingDistance >= 0
? ((panel.frameOffset >= 0 ? "+" : "") + panel.frameOffset + " frames / "
+ (panel.frameOffsetSeconds >= 0 ? "+" : "") + panel.frameOffsetSeconds.toFixed(1) + "s")
: "--"
valueColor: panel.warpingDistance >= 0 ? Theme.headingColor : Theme.sideMutedText
}
ReadoutStat {
label: "MAX SENSOR DEVIATION"
value: panel.maxSensorDeviation >= 0 ? panel.maxSensorDeviation.toFixed(2) + "°C" : "--"
valueColor: panel.maxSensorDeviation >= 0
? (panel.maxSensorDeviation < 1.0 ? Theme.metricGood : panel.maxSensorDeviation < 3.0 ? Theme.metricWarn : Theme.metricBad)
: Theme.sideMutedText
}
}
}
// Bento 2: Overlap Map Settings
PanelBox {
Layout.fillWidth: true
Layout.preferredHeight: overlapCol.implicitHeight + 24
ColumnLayout {
id: overlapCol
anchors.fill: parent
anchors.margins: 12
spacing: 8
SectionTitle { text: "OVERLAP SETTINGS" }
PanelCheckBox {
id: enableOverlapCheck
Layout.fillWidth: true
checked: true
text: "Enable Overlap Map"
font.pixelSize: Theme.fontSm
}
ColumnLayout {
Layout.fillWidth: true
spacing: 6
visible: enableOverlapCheck.checked
Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.cardBorder
}
RowLayout {
Layout.fillWidth: true
spacing: 6
Label {
text: "Blend"
font.pixelSize: Theme.fontSm
color: Theme.bodyColor
Layout.preferredWidth: 40
}
PanelSlider {
id: blendSlider
Layout.fillWidth: true
from: 0; to: 100; value: 50
stepSize: 1
}
Label {
text: Math.round(blendSlider.value) + "%"
font.pixelSize: Theme.fontSm
color: Theme.sideMutedText
Layout.preferredWidth: 30
}
}
// Diff color legend
Row {
Layout.fillWidth: true
spacing: 10
Row {
spacing: 3
Rectangle { width: 8; height: 8; radius: 4; color: Theme.sensorLow; anchors.verticalCenter: parent.verticalCenter }
Label { text: "B cooler"; font.pixelSize: Theme.fontSm; color: Theme.sideMutedText }
}
Row {
spacing: 3
Rectangle { width: 8; height: 8; radius: 4; color: Theme.sensorInRange; anchors.verticalCenter: parent.verticalCenter }
Label { text: "Match"; font.pixelSize: Theme.fontSm; color: Theme.sideMutedText }
}
Row {
spacing: 3
Rectangle { width: 8; height: 8; radius: 4; color: Theme.sensorHigh; anchors.verticalCenter: parent.verticalCenter }
Label { text: "B hotter"; font.pixelSize: Theme.fontSm; color: Theme.sideMutedText }
}
}
}
}
}
// Bento: Frame / Time Controls
PanelBox {
Layout.fillWidth: true
Layout.preferredHeight: frameTimeCol.implicitHeight + 32
ColumnLayout {
id: frameTimeCol
anchors.fill: parent
anchors.margins: 16
spacing: 20
SectionTitle { text: "FRAME / TIME" }
EditableScrubStat {
label: "Frame"
value: scrubReadout.scrubIdx
unit: "/ " + Math.max(0, panel.seriesLen - 1) + (Math.max(0, panel.seriesLen - 1) <= 1 ? " frame" : " frames")
editable: panel.hasSeries
onValueEdited: (newValue) => {
var maxVal = Math.max(0, panel.seriesLen - 1)
var val = Math.max(0, Math.min(newValue, maxVal))
panel.frameIndexRequested(val)
}
onEditingDone: panel.scrubberFocusRequested()
}
EditableScrubStat {
label: "Time"
value: scrubReadout.scrubTime
unit: "/ " + scrubReadout.maxTime.toFixed(1) + "s"
editable: panel.hasSeries
onValueEdited: (newValue) => {
var closestIdx = panel.frameForTime(newValue)
panel.frameIndexRequested(closestIdx)
}
onEditingDone: panel.scrubberFocusRequested()
}
}
}
// Bento: Scrub Readout
PanelBox {
Layout.fillWidth: true
Layout.preferredHeight: scrubReadout.implicitHeight + 24
ColumnLayout {
id: scrubReadout
anchors.fill: parent
anchors.margins: 12
spacing: 8
readonly property int scrubIdx: Math.round(panel.frameIndex)
readonly property bool hasA: scrubIdx < panel.trendDataA.length
readonly property bool hasB: scrubIdx < panel.trendDataB.length
readonly property real tempA: hasA ? panel.trendDataA[scrubIdx] : 0
readonly property real tempB: hasB ? panel.trendDataB[scrubIdx] : 0
readonly property real scrubTime: {
var t = scrubIdx < panel.timeDataA.length ? panel.timeDataA[scrubIdx]
: (scrubIdx < panel.timeDataB.length ? panel.timeDataB[scrubIdx] : 0)
return t
}
readonly property real maxTime: {
var ta = panel.timeDataA.length > 0 ? panel.timeDataA[panel.timeDataA.length - 1] : 0
var tb = panel.timeDataB.length > 0 ? panel.timeDataB[panel.timeDataB.length - 1] : 0
return Math.max(ta, tb)
}
SectionTitle { text: "SCRUBBER READOUT" }
ReadoutStat {
label: "Temp (Run A)"
value: scrubReadout.hasA ? scrubReadout.tempA.toFixed(1) + "°C" : "none"
valueColor: Theme.primaryAccent
}
ReadoutStat {
label: "Temp (Run B)"
value: scrubReadout.hasB ? scrubReadout.tempB.toFixed(1) + "°C" : "none"
valueColor: Theme.themeSkill
}
ReadoutStat {
label: "Difference"
value: (scrubReadout.hasA && scrubReadout.hasB)
? Math.abs(scrubReadout.tempA - scrubReadout.tempB).toFixed(1) + "°C" : "none"
}
}
}
Item { Layout.fillHeight: true }
}