颜色映射图例,规格尺寸修改

This commit is contained in:
2026-01-15 16:13:36 +08:00
parent f700dd360e
commit 354552dc88
21 changed files with 1200 additions and 223 deletions

View File

@@ -6,7 +6,7 @@ import TactileIPC 1.0
Item {
id: root
width: 350
implicitWidth: 350
property alias title: titleText.text
property bool expanded: true

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls.Material
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import "."
import TactileIPC 1.0
@@ -208,6 +209,42 @@ Rectangle {
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("宽")
Layout.preferredWidth: 90
color: root.textColor
}
SpinBox {
Layout.fillWidth: true
from: 1
to: 20
value: Backend.sensorCol
enabled: Backend.serial.connected === false
onValueModified: Backend.sensorCol = value
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("高")
Layout.preferredWidth: 90
color: root.textColor
}
SpinBox {
Layout.fillWidth: true
from: 1
to: 20
value: Backend.sensorRow
enabled: Backend.serial.connected === false
onValueModified: Backend.sensorRow = value
}
}
RowLayout {
Layout.fillWidth: true
spacing: 12
@@ -372,6 +409,150 @@ Rectangle {
}
}
CollapsiblePanel {
title: root.tr("颜色映射")
expanded: true
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("最小值")
Layout.preferredWidth: 90
color: root.textColor
}
SpinBox {
Layout.fillWidth: true
from: -999999
to: 999999
editable: true
value: Backend.rangeMin
onValueModified: Backend.rangeMin = value
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("最大值")
Layout.preferredWidth: 90
color: root.textColor
}
SpinBox {
Layout.fillWidth: true
from: -999999
to: 999999
editable: true
value: Backend.rangeMax
onValueModified: Backend.rangeMax = value
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("低色")
Layout.preferredWidth: 90
color: root.textColor
}
Rectangle {
width: 22
height: 22
radius: 4
color: Backend.colorLow
border.width: 1
border.color: Qt.rgba(0, 0, 0, 0.2)
Layout.alignment: Qt.AlignVCenter
MouseArea {
anchors.fill: parent
onClicked: {
lowColorDialog.selectedColor = Backend.colorLow
lowColorDialog.open()
}
}
}
Button {
text: root.tr("选择")
Layout.fillWidth: true
onClicked: {
lowColorDialog.selectedColor = Backend.colorLow
lowColorDialog.open()
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("中色")
Layout.preferredWidth: 90
color: root.textColor
}
Rectangle {
width: 22
height: 22
radius: 4
color: Backend.colorMid
border.width: 1
border.color: Qt.rgba(0, 0, 0, 0.2)
Layout.alignment: Qt.AlignVCenter
MouseArea {
anchors.fill: parent
onClicked: {
midColorDialog.selectedColor = Backend.colorMid
midColorDialog.open()
}
}
}
Button {
text: root.tr("选择")
Layout.fillWidth: true
onClicked: {
midColorDialog.selectedColor = Backend.colorMid
midColorDialog.open()
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label {
text: root.tr("高色")
Layout.preferredWidth: 90
color: root.textColor
}
Rectangle {
width: 22
height: 22
radius: 4
color: Backend.colorHigh
border.width: 1
border.color: Qt.rgba(0, 0, 0, 0.2)
Layout.alignment: Qt.AlignVCenter
MouseArea {
anchors.fill: parent
onClicked: {
highColorDialog.selectedColor = Backend.colorHigh
highColorDialog.open()
}
}
}
Button {
text: root.tr("选择")
Layout.fillWidth: true
onClicked: {
highColorDialog.selectedColor = Backend.colorHigh
highColorDialog.open()
}
}
}
}
CollapsiblePanel {
title: root.tr("显示控制")
expanded: true
@@ -399,7 +580,14 @@ Rectangle {
Button {
Layout.fillWidth: true
text: root.tr("导出数据")
onClicked: exportDlg.open()
onClicked: {
if (Backend.data.frameCount != 0) {
exportDlg.open()
}
else {
console.log("Backend.data.frameCount() === 0")
}
}
}
}
}
@@ -407,6 +595,25 @@ Rectangle {
Item { Layout.fillHeight: true }
}
}
ColorDialog {
id: lowColorDialog
title: root.tr("选择低色")
onAccepted: Backend.colorLow = selectedColor
}
ColorDialog {
id: midColorDialog
title: root.tr("选择中色")
onAccepted: Backend.colorMid = selectedColor
}
ColorDialog {
id: highColorDialog
title: root.tr("选择高色")
onAccepted: Backend.colorHigh = selectedColor
}
SaveAsExportDialog {
id: exportDlg
/* onSaveTo: (folder, filename, format, method) => {

View File

@@ -7,9 +7,14 @@ Item {
id: root
property int minValue: 0
property int maxValue: 100
property color colorLow: Qt.rgba(0.10, 0.75, 1.00, 1.0)
property color colorMid: Qt.rgba(0.10, 0.95, 0.35, 1.0)
property color colorHigh: Qt.rgba(1.00, 0.22, 0.10, 1.0)
property int barWidth: 34
property int barRadius: 8
implicitWidth: 90
implicitHeight: 220
implicitWidth: barWidth + 48
implicitHeight: 240
ColumnLayout {
anchors.fill: parent
@@ -25,17 +30,16 @@ Item {
Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
width: 26
radius: 6
width: root.barWidth
radius: root.barRadius
border.width: 1
border.color: Qt.rgba(1, 1, 1, 0.18)
gradient: Gradient {
// must match shaders/dots.frag:dataColorRamp (high at top)
GradientStop { position: 0.00; color: Qt.rgba(1.00, 0.22, 0.10, 1.0) } // c3
GradientStop { position: 0.34; color: Qt.rgba(1.00, 0.92, 0.22, 1.0) } // c2
GradientStop { position: 0.67; color: Qt.rgba(0.10, 0.95, 0.35, 1.0) } // c1
GradientStop { position: 1.00; color: Qt.rgba(0.10, 0.75, 1.00, 1.0) } // c0
GradientStop { position: 0.00; color: root.colorHigh }
GradientStop { position: 0.50; color: root.colorMid }
GradientStop { position: 1.00; color: root.colorLow }
}
}
@@ -47,4 +51,3 @@ Item {
}
}
}

View File

@@ -92,8 +92,8 @@ Rectangle {
anchors.left: parent.left
anchors.leftMargin: 8
Image {
width: 18
height: 12
width: 16
height: 16
fillMode: Image.PreserveAspectFit
source: modelData.icon
}
@@ -107,10 +107,13 @@ Rectangle {
anchors.fill: parent
Row {
spacing: 8
anchors.left: parent.left
anchors.leftMargin: 10
anchors.rightMargin: 24
anchors.verticalCenter: parent.verticalCenter
Image {
width: 18
height: 12
width: 16
height: 16
fillMode: Image.PreserveAspectFit
source: langBox.model[langBox.currentIndex]
? langBox.model[langBox.currentIndex].icon

View File

@@ -0,0 +1,291 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import Qt.labs.folderlistmodel 2.15
import QtCore 6.2
import QtQuick.Dialogs
import TactileIPC 1.0
Window {
id: root
width: 980
height: 640
minimumWidth: 880
minimumHeight: 560
visible: false
modality: Qt.ApplicationModal
flags: Qt.Dialog | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
title: root.tr("导入数据")
color: windowBg
readonly property bool isDark: !Backend.lightMode
readonly property color windowBg: isDark ? "#1B1F1B" : "#F7F8F9"
Material.accent: root.accent
Material.primary: root.accent
Material.theme: root.isDark ? Material.Dark : Material.Light
readonly property color accent: "#21A453"
readonly property color accentSoft: root.isDark ? "#1F3A2A" : "#E6F6EC"
readonly property color panel: root.isDark ? "#242924" : "#FFFFFF"
readonly property color border: root.isDark ? "#343A35" : "#E1E5EA"
readonly property color text: root.isDark ? "#E6ECE7" : "#1E2A32"
readonly property color subText: root.isDark ? "#9AA5A0" : "#6E7A86"
readonly property color fieldBg: root.isDark ? "#1E221E" : "#FFFFFF"
readonly property color surfaceAlt: root.isDark ? "#202520" : "#F9FAFB"
readonly property color hoverBg: root.isDark ? "#2C322D" : "#F3F6F8"
readonly property color iconBg: root.isDark ? "#25362B" : "#E8F3EA"
readonly property color iconBgAlt: root.isDark ? "#2A302A" : "#EFF2F5"
readonly property color disabledBg: root.isDark ? "#4B544E" : "#C9D2D8"
readonly property string uiFont: "Microsoft YaHei UI"
property url currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) + "/"
property string chosenFilename: ""
property string importFormat: ""
property string importMethod: ""
signal importIn(url filename, string format, string method)
function open() {
}
function accept() {
visible = false
}
function reject() {
visible = false
}
function centerOnScreen_() {
x = Math.round((Screen.width - width) / 2)
y = Math.round((Screen.height - height) / 2)
}
function normalizeFolder_(path) {
if (!path)
return path
if (path.endsWith("/"))
return path
return path + "/"
}
function tr(text) {
I18n.retranslateToken
return qsTr(text)
}
onVisibleChanged: if (visible) centerOnScreen_()
ColumnLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 12
Rectangle {
Layout.fillWidth: true
height: 54
radius: 6
color: root.panel
border.color: root.border
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 8
ToolButton {
id: backBtn
text: "<"
font.family: root.uiFont
onClicked: {
}
background: Rectangle {
radius: 4
color: backBtn.hovered ? root.accentSoft : "transparent"
border.color: backBtn.hovered ? root.accent : root.border
}
}
ToolButton {
id: forwardBtn
text: ">"
font.family: root.uiFont
onClicked: {
}
background: Rectangle {
radius: 4
color: backBtn.hovered ? root.accentSoft : "transparent"
border.color: backBtn.hovered ? root.accent : root.border
}
}
ToolButton {
id: upBtn
text: "^"
font.family: root.uiFont
onClicked: {
}
background: Rectangle {
radius: 4
color: backBtn.hovered ? root.accentSoft : "transparent"
border.color: backBtn.hovered ? root.accent : root.border
}
}
TextField {
id: breadcrumb
Layout.fillWidth: true
readOnly: true
font.family: root.uiFont
color: root.text
text: root.currentFolder.toString()
background: Rectangle {
radius: 4
color: root.surfaceAlt
border.color: root.border
}
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 12
Rectangle {
Layout.preferredWidth: 220
Layout.fillHeight: true
radius: 6
color: root.panel
border.color: root.border
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
spacing: 8
Label {
text: root.tr("位置")
font.bold: true
font.family: root.uiFont
color: root.text
}
ListView {
id: places
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: [
{ name: root.tr("此电脑"), url: "file:///", icon: root.isDark ? "qrc:/images/computer_dark.png" : "qrc:/images/computer_light.png" },
{ name: root.tr("桌面"), url: StandardPaths.writableLocation(StandardPaths.DesktopLocation) + "/", icon: root.isDark ? "qrc:/images/desktop_dark.png" : "qrc:/images/desktop_light.png" },
{ name: root.tr("文档"), url: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) + "/", icon: root.isDark ? "qrc:/images/docs_dark.png" : "qrc:/images/docs_light.png" },
{ name: root.tr("下载"), url: StandardPaths.writableLocation(StandardPaths.DownloadLocation) + "/", icon: root.isDark ? "qrc:/images/download_dark.png" : "qrc:/images/download_light.png" }
]
delegate: ItemDelegate {
width: ListView.view.width
onClicked: {
places.currentIndex = index
root.currentFolder = normalizeFolder_(modeData.url)
}
background: Rectangle {
radius: 4
color: places.currentIndex === index ? root.accentSoft : "transparent"
border.color: places.currentIndex === index ? root.accent : "transparent"
}
contentItem: RowLayout {
spacing: 8
Image {
width: 16
height: 16
source: modelData.icon
fillMode: Image.PreserveAspectFit
smooth: true
}
Label {
text: modelData.name
font.family: root.uiFont
color: root.text
}
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
radius: 6
color: root.panel
border.color: root.border
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
spacing: 6
RowLayout {
Layout.fillWidth: true
Lable {
// TODO table title
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: root.border
}
FolderListModel {
id: fileModel
folder: root.currentFolder
showDotAndDotDot: false
showDirs: true
showFiles: true
sortField: FolderListModel.Name
}
ListView {
id: fileList
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: fileModel
delegate: ItemDelegate {
id: fileRow
width: ListView.view.width
onDoubleClicked: {
const isDir = fileModel.get(index, "fileIsDir")
if (isDir) {
root.currentFolder = normalizeFolder_(fileModel.get(index, "filePath"))
}
else {
// TODO import file
}
}
onClicked: {
fileList.currentIndex = index
}
background: Rectangle {
radius: 4
color: fileRow.hovered ? root.hoverBg : "transparent"
}
contentItem: RowLayout {
spacing: 8
Rectangle {
width+:
}
}
}
}
}
}
}
}
}

View File

@@ -22,177 +22,205 @@ Rectangle {
Material.accent: Material.Green
Material.primary: Material.Green
ColumnLayout {
ScrollView {
id: scrollView
anchors.fill: parent
anchors.margins: 12
spacing: 12
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentWidth: scrollView.availableWidth
contentHeight: contentLayout.implicitHeight + 24
LiveTrendCard {
id: card
Layout.fillWidth: true
Layout.preferredHeight: 180
title: root.tr("Payload Sum")
ColumnLayout {
id: contentLayout
x: 12
y: 12
width: scrollView.availableWidth - 24
spacing: 12
Connections {
target: Backend.data
function onMetricsChanged() {
if (Backend.data.frameCount > 0)
card.plot.append(Backend.data.metricSum)
}
}
}
CollapsiblePanel {
title: root.tr("Live Trend")
expanded: true
Layout.fillWidth: true
/*
Canvas {
id: trendCanvas
LiveTrendCard {
id: card
Layout.fillWidth: true
Layout.preferredHeight: 180
property var samples: [0.08, 0.24, 0.52, 0.41, 0.63, 0.47, 0.72, 0.58, 0.82, 0.69]
title: root.tr("Payload Sum")
onPaint: {
const ctx = getContext("2d")
const w = width
const h = height
ctx.clearRect(0, 0, w, h)
Connections {
target: Backend.data
function onMetricsChanged() {
if (Backend.data.frameCount > 0)
card.plot.append(Backend.data.metricSum)
}
}
}
ctx.strokeStyle = "#D8EAD9"
ctx.lineWidth = 1
for (let i = 1; i < 5; i++) {
const y = (h / 5) * i
CollapsiblePanel {
title: root.tr("Live Trend")
expanded: true
Layout.fillWidth: true
/*
Canvas {
id: trendCanvas
Layout.fillWidth: true
Layout.preferredHeight: 180
property var samples: [0.08, 0.24, 0.52, 0.41, 0.63, 0.47, 0.72, 0.58, 0.82, 0.69]
onPaint: {
const ctx = getContext("2d")
const w = width
const h = height
ctx.clearRect(0, 0, w, h)
ctx.strokeStyle = "#D8EAD9"
ctx.lineWidth = 1
for (let i = 1; i < 5; i++) {
const y = (h / 5) * i
ctx.beginPath()
ctx.moveTo(0, y)
ctx.lineTo(w, y)
ctx.stroke()
}
ctx.strokeStyle = root.accentColor
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(0, y)
ctx.lineTo(w, y)
for (let i = 0; i < samples.length; i++) {
const x = (w - 12) * (i / (samples.length - 1)) + 6
const y = h - (h - 12) * samples[i] - 6
if (i === 0)
ctx.moveTo(x, y)
else
ctx.lineTo(x, y)
}
ctx.stroke()
}
ctx.strokeStyle = root.accentColor
ctx.lineWidth = 2
ctx.beginPath()
for (let i = 0; i < samples.length; i++) {
const x = (w - 12) * (i / (samples.length - 1)) + 6
const y = h - (h - 12) * samples[i] - 6
if (i === 0)
ctx.moveTo(x, y)
else
ctx.lineTo(x, y)
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
Component.onCompleted: requestPaint()
}
*/
}
CollapsiblePanel {
title: root.tr("Metrics")
expanded: true
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
spacing: 8
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("峰值"); font.pixelSize: 12 }
Label { text: Backend.data.metricPeak.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("均方根"); font.pixelSize: 12 }
Label { text: Backend.data.metricRms.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
ctx.stroke()
}
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
Component.onCompleted: requestPaint()
RowLayout {
Layout.fillWidth: true
spacing: 8
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("平均值"); font.pixelSize: 12 }
Label { text: Backend.data.metricAvg.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("变化量"); font.pixelSize: 12 }
Label { text: Backend.data.metricDelta.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
}
}
*/
CollapsiblePanel {
title: root.tr("Session")
expanded: true
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
spacing: 8
Label { text: root.tr("Frames"); Layout.preferredWidth: 80 }
Label { text: Backend.data.frameCount; Layout.fillWidth: true; horizontalAlignment: Text.AlignRight }
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label { text: root.tr("Playback"); Layout.preferredWidth: 80 }
Label {
text: Backend.data.playbackRunning ? root.tr("Running") : root.tr("Idle")
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
}
}
}
CollapsiblePanel {
title: root.tr("Legend")
expanded: true
Layout.fillWidth: true
Legend {
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: 200
barWidth: 36
minValue: Backend.rangeMin
maxValue: Backend.rangeMax
colorLow: Backend.colorLow
colorMid: Backend.colorMid
colorHigh: Backend.colorHigh
}
}
Item { Layout.fillHeight: true }
}
CollapsiblePanel {
title: root.tr("Metrics")
expanded: true
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
spacing: 8
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("峰值"); font.pixelSize: 12 }
Label { text: Backend.data.metricPeak.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("均方根"); font.pixelSize: 12 }
Label { text: Backend.data.metricRms.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("平均值"); font.pixelSize: 12 }
Label { text: Backend.data.metricAvg.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
Rectangle {
Layout.fillWidth: true
radius: 6
color: Qt.rgba(0, 0, 0, 0.03)
border.color: Qt.rgba(0, 0, 0, 0.08)
height: 72
Column {
anchors.centerIn: parent
spacing: 4
Label { text: root.tr("变化量"); font.pixelSize: 12 }
Label { text: Backend.data.metricDelta.toFixed(0); font.pixelSize: 18; font.bold: true }
}
}
}
}
CollapsiblePanel {
title: root.tr("Session")
expanded: true
Layout.fillWidth: true
RowLayout {
Layout.fillWidth: true
spacing: 8
Label { text: root.tr("Frames"); Layout.preferredWidth: 80 }
Label { text: Backend.data.frameCount; Layout.fillWidth: true; horizontalAlignment: Text.AlignRight }
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Label { text: root.tr("Playback"); Layout.preferredWidth: 80 }
Label {
text: Backend.data.playbackRunning ? root.tr("Running") : root.tr("Idle")
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
}
}
}
Item { Layout.fillHeight: true }
}
}