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

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

@@ -20,6 +20,8 @@ AppBackend::AppBackend(QObject* parent)
m_data->clear();
emit connectedChanged();
});
connect(this, &AppBackend::sensorRowChanged, m_serial, &SerialBackend::setSensorHeight);
connect(this, &AppBackend::sensorColChanged, m_serial, &SerialBackend::setSensorWidth);
}
bool AppBackend::connected() const {
@@ -47,4 +49,67 @@ void AppBackend::setShowGrid(bool on) {
m_showGrid = on;
emit showGridChanged(on);
}
}
void AppBackend::setSensorCol(int c) {
if (m_serial->connected()) {
return;
}
m_sensorCol = c;
qInfo() << "sensorColChanged: " << c;
emit sensorColChanged(c);
}
void AppBackend::setSensorRow(int r) {
if (m_serial->connected()) {
return;
}
m_sensorRow = r;
qInfo() << "sensorRowChanged: " << r;
emit sensorRowChanged(r);
}
void AppBackend::setRangeMin(int v) {
if (m_rangeMin == v)
return;
m_rangeMin = v;
if (m_rangeMin > m_rangeMax) {
m_rangeMax = m_rangeMin;
emit rangeMaxChanged(m_rangeMax);
}
emit rangeMinChanged(m_rangeMin);
emit rangeChanged(m_rangeMin, m_rangeMax);
}
void AppBackend::setRangeMax(int v) {
if (m_rangeMax == v)
return;
m_rangeMax = v;
if (m_rangeMax < m_rangeMin) {
m_rangeMin = m_rangeMax;
emit rangeMinChanged(m_rangeMin);
}
emit rangeMaxChanged(m_rangeMax);
emit rangeChanged(m_rangeMin, m_rangeMax);
}
void AppBackend::setColorLow(const QColor& color) {
if (m_colorLow == color)
return;
m_colorLow = color;
emit colorLowChanged(m_colorLow);
}
void AppBackend::setColorMid(const QColor& color) {
if (m_colorMid == color)
return;
m_colorMid = color;
emit colorMidChanged(m_colorMid);
}
void AppBackend::setColorHigh(const QColor& color) {
if (m_colorHigh == color)
return;
m_colorHigh = color;
emit colorHighChanged(m_colorHigh);
}

View File

@@ -6,6 +6,7 @@
#define TACTILEIPC3D_BACKEND_H
#include <QObject>
#include <QString>
#include <QColor>
#include <qtmetamacros.h>
#include "data_backend.h"
@@ -19,6 +20,13 @@ class AppBackend : public QObject {
Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid NOTIFY showGridChanged);
Q_PROPERTY(SerialBackend* serial READ serial CONSTANT)
Q_PROPERTY(DataBackend* data READ data CONSTANT)
Q_PROPERTY(int sensorCol READ sensorCol WRITE setSensorCol NOTIFY sensorColChanged);
Q_PROPERTY(int sensorRow READ sensorRow WRITE setSensorRow NOTIFY sensorRowChanged);
Q_PROPERTY(int rangeMin READ rangeMin WRITE setRangeMin NOTIFY rangeMinChanged);
Q_PROPERTY(int rangeMax READ rangeMax WRITE setRangeMax NOTIFY rangeMaxChanged);
Q_PROPERTY(QColor colorLow READ colorLow WRITE setColorLow NOTIFY colorLowChanged);
Q_PROPERTY(QColor colorMid READ colorMid WRITE setColorMid NOTIFY colorMidChanged);
Q_PROPERTY(QColor colorHigh READ colorHigh WRITE setColorHigh NOTIFY colorHighChanged);
public:
explicit AppBackend(QObject* parent=nullptr);
@@ -35,12 +43,34 @@ public:
bool showGrid() const { return m_showGrid; }
void setShowGrid(bool on);
int sensorCol() const { qInfo() << "col:" << m_sensorCol; return m_sensorCol; }
int sensorRow() const { qInfo() << "row:" << m_sensorRow; return m_sensorRow; }
void setSensorRow(int r);
void setSensorCol(int c);
int rangeMin() const { return m_rangeMin; }
int rangeMax() const { return m_rangeMax; }
void setRangeMin(int v);
void setRangeMax(int v);
QColor colorLow() const { return m_colorLow; }
QColor colorMid() const { return m_colorMid; }
QColor colorHigh() const { return m_colorHigh; }
void setColorLow(const QColor& color);
void setColorMid(const QColor& color);
void setColorHigh(const QColor& color);
signals:
void lightModeChanged();
void languageChanged();
void connectedChanged();
void showGridChanged(bool on);
void sensorColChanged(int c);
void sensorRowChanged(int r);
void rangeMinChanged(int v);
void rangeMaxChanged(int v);
void rangeChanged(int minV, int maxV);
void colorLowChanged(const QColor& color);
void colorMidChanged(const QColor& color);
void colorHighChanged(const QColor& color);
private:
SerialBackend* m_serial = nullptr;
DataBackend* m_data = nullptr;
@@ -48,6 +78,13 @@ private:
QString m_language = QStringLiteral("zh_CN");
bool m_showGrid = true;
int m_sensorRow = 12;
int m_sensorCol = 7;
int m_rangeMin = 0;
int m_rangeMax = 1000;
QColor m_colorLow = QColor::fromRgbF(0.10, 0.75, 1.00);
QColor m_colorMid = QColor::fromRgbF(0.10, 0.95, 0.35);
QColor m_colorHigh = QColor::fromRgbF(1.00, 0.22, 0.10);
};
#endif //TACTILEIPC3D_BACKEND_H

View File

@@ -23,7 +23,7 @@ DataBackend::DataBackend(QObject* parent)
emitFrame_(m_frames[m_playbackIndex], cb);
m_playbackIndex++;
});
seedDebugFrames_();
// seedDebugFrames_();
}
void DataBackend::ingestFrame(const DataFrame& frame) {
@@ -56,6 +56,14 @@ bool DataBackend::exportCsv(const QString& path) const {
return true;
}
bool DataBackend::exportXlsx(const QString& path) const {
/* QFile file(path);
if (!file.open(QIODevice::WriteOnly))
return false;
*/
return buildXlsx_(path);
}
bool DataBackend::importJson(const QString& path) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
@@ -70,6 +78,57 @@ bool DataBackend::importCsv(const QString& path) {
return loadCsv_(file.readAll());
}
bool DataBackend::importXlsx(const QString& path) {
QXlsx::Document doc(path);
if (!doc.isLoadPackage()) {
qCritical() << "failed to open file: " << path;
return false;
}
stopPlayback();
clear();
doc.selectSheet(1);
QXlsx::CellRange range = doc.dimension();
int firstRow = range.firstRow();
int lastRow = range.lastRow();
int firstCol = range.firstColumn();
int lastCol = range.lastColumn();
if (firstRow == 0 && lastRow == 0 && firstCol == 0 && lastCol == 0) {
return false;
}
int row_count = lastRow - firstRow + 1;
int col_count = lastCol - firstCol + 1;
// TODO 完善xlsx数据导入
struct SpanInfo {
int row = 0;
int column = 0;
int rowSpan = 1;
int colSpan = 1;
};
QVector<QVector<QVariant>> cells;
QVector<SpanInfo> spans;
for (int r = 0; r < row_count; ++r) {
cells.resize(col_count);
for (int c = 0; c < col_count; ++c) {
int excel_row = firstRow + r;
int excel_col = firstCol + c;
std::shared_ptr<QXlsx::Cell> cell_obj = doc.cellAt(excel_row, excel_col);
QVariant v;
if (cell_obj) {
v = cell_obj->readValue();
}
else {
v = doc.read(excel_row, excel_col);
}
cells[r][c] = v;
}
}
}
void DataBackend::startPlayback(int intervalMs) {
if (m_frames.isEmpty())
return;
@@ -273,6 +332,47 @@ QByteArray DataBackend::buildCsv_() const {
return out;
}
bool DataBackend::buildXlsx_(const QString& path) const {
QXlsx::Document xlsx;
int current_sheet_index = 1;
int current_sheet_row_start = 1;
int col_count = m_frames.at(0).data.size();
auto ensure_sheet_for_row = [&](int row){
if (m_frames.size() <= 0) {
if (xlsx.currentWorksheet() == nullptr)
xlsx.addSheet("Sheet1");
return;
}
if (xlsx.currentWorksheet() == nullptr) {
xlsx.addSheet(QStringLiteral("Sheet%1").arg(current_sheet_index));
current_sheet_row_start = 1;
return;
}
if (row - current_sheet_row_start >= m_frames.size()) {
++current_sheet_index;
xlsx.addSheet(QStringLiteral("Sheet%1").arg(current_sheet_index));
current_sheet_row_start = row;
}
};
for (size_t row = 1; row <= m_frames.size(); row++) {
ensure_sheet_for_row(row);
xlsx.write(1, 1, m_frames.at(row - 1).pts);
int col_index = 2;
for (auto data : m_frames.at(row - 1).data) {
xlsx.write(row, col_index, data);
col_index++;
}
}
if (!xlsx.saveAs(path)) {
qCritical() << "failed to save file: " << path;
return false;
}
return true;
}
void DataBackend::seedDebugFrames_() {
if (!m_frames.isEmpty())
return;

View File

@@ -9,6 +9,8 @@
#include <QSerialPort>
#include <QSerialPortInfo>
#include <qtmetamacros.h>
#include "xlsxdocument.h"
#include "xlsxformat.h"
#include "data_frame.h"
class DataBackend : public QObject {
@@ -42,13 +44,14 @@ public:
Q_INVOKABLE void clear();
Q_INVOKABLE bool exportJson(const QString& path) const;
Q_INVOKABLE bool exportCsv(const QString& path) const;
Q_INVOKABLE bool exportXlsx(const QString& path) const;
Q_INVOKABLE bool importJson(const QString& path);
Q_INVOKABLE bool importCsv(const QString& path);
Q_INVOKABLE bool importXlsx(const QString& path);
Q_INVOKABLE void startPlayback(int intervalMs);
Q_INVOKABLE void stopPlayback();
Q_INVOKABLE void exportHandler(const QUrl& folder, const QString& filename,
const QString& format, const QString& method);
signals:
void frameCountChanged();
void playbackRunningChanged();
@@ -59,6 +62,7 @@ private:
bool loadCsv_(const QByteArray& data);
QByteArray buildJson_() const;
QByteArray buildCsv_() const;
bool buildXlsx_(const QString& path) const;
void seedDebugFrames_();

View File

@@ -41,6 +41,10 @@ static void matIdentity(float m[16]) {
m[0] = m[5] = m[10] = m[15] = 1;
}
static QVector3D toColorVec(const QColor& color) {
return QVector3D(color.redF(), color.greenF(), color.blueF());
}
GLWidget::GLWidget(QWidget *parent)
: QOpenGLWidget(parent) {
setMinimumSize(640, 480);
@@ -191,6 +195,30 @@ void GLWidget::setShowBg(bool on = true) {
update();
}
void GLWidget::setColorLow(const QColor& color) {
const QVector3D next = toColorVec(color);
if (m_colorLow == next)
return;
m_colorLow = next;
update();
}
void GLWidget::setColorMid(const QColor& color) {
const QVector3D next = toColorVec(color);
if (m_colorMid == next)
return;
m_colorMid = next;
update();
}
void GLWidget::setColorHigh(const QColor& color) {
const QVector3D next = toColorVec(color);
if (m_colorHigh == next)
return;
m_colorHigh = next;
update();
}
void GLWidget::initializeGL() {
initializeOpenGLFunctions();
@@ -211,6 +239,14 @@ void GLWidget::initializeGL() {
matIdentity(m_proj);
}
void GLWidget::initGeometry_() {
initDotTexture_();
initBackgroundGeometry_();
initPanelGeometry_();
initDotGeometry_();
initRoomGeometry_();
}
void GLWidget::resizeGL(int w, int h) {
glViewport(0, 0, w, h);
}
@@ -293,8 +329,11 @@ void GLWidget::paintGL() {
// uMinV/uMaxV: 传感值范围,用于 fragment shader 把 value 映射成颜色
m_dotsProg->setUniformValue("uMinV", float(m_min));
m_dotsProg->setUniformValue("uMaxV", float(m_max));
m_dotsProg->setUniformValue("uColorLow", m_colorLow);
m_dotsProg->setUniformValue("uColorMid", m_colorMid);
m_dotsProg->setUniformValue("uColorHigh", m_colorHigh);
const int hasData = (m_hasData.load() || m_dotTex == 0) ? 1 : 0;
m_dotsProg->setUniformValue("uHasData", 0);
m_dotsProg->setUniformValue("uHasData", hasData);
m_dotsProg->setUniformValue("uCameraPos", m_cameraPos);
m_dotsProg->setUniformValue("uDotTex", 0);
if (m_dotTex) {
@@ -427,6 +466,7 @@ void GLWidget::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
QVector3D world;
const int index = pickDotIndex_(event->pos(), &world);
qInfo() << "clicked index: " << index;
if (index >= 0) {
float value = 0.0f;
int row = 0;
@@ -1098,3 +1138,17 @@ void GLWidget::setShowGrid(bool on) {
m_showGrid = on;
update();
}
void GLWidget::setRow(int row) {
row = qMax(0, row);
if (m_rows == row)
return;
setSpec(row, m_cols, m_pitch, m_dotRadius);
}
void GLWidget::setCol(int col) {
col = qMax(0, col);
if (m_cols == col)
return;
setSpec(m_rows, col, m_pitch, m_dotRadius);
}

View File

@@ -14,6 +14,7 @@
#include <QMatrix4x4>
#include <QVector3D>
#include <QString>
#include <QColor>
#include <atomic>
#include <qtmetamacros.h>
#include <qvectornd.h>
@@ -26,6 +27,8 @@ struct Ray {
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core {
Q_OBJECT
Q_PROPERTY(float yaw READ yaw WRITE setYaw NOTIFY yawChanged)
Q_PROPERTY(int row READ row WRITE setRow)
Q_PROPERTY(int col READ col WRITE setCol)
// Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid NOTIFY showGridChanged)
public:
enum RenderMode {
@@ -59,7 +62,8 @@ public:
bool showGrid() const { return m_showGrid; }
int row() const { return m_rows; }
int col() const { return m_cols; }
public slots:
// 值域范围,用于 shader 里把 value 映射到颜色(绿->红)
@@ -70,6 +74,11 @@ public slots:
void setLightMode(bool on);
void setShowBg(bool on);
void setShowGrid(bool on);
void setCol(int col);
void setRow(int row);
void setColorLow(const QColor& color);
void setColorMid(const QColor& color);
void setColorHigh(const QColor& color);
signals:
void yawChanged();
@@ -85,6 +94,7 @@ protected:
void wheelEvent(QWheelEvent *event) override;
private:
void initGeometry_();
void initPanelGeometry_();
void initDotGeometry_();
void initBackgroundGeometry_();
@@ -154,6 +164,9 @@ private:
unsigned int m_bgVbo = 0;
bool m_lightMode = true;
bool m_showBg = true;
QVector3D m_colorLow{0.10f, 0.75f, 1.00f};
QVector3D m_colorMid{0.10f, 0.95f, 0.35f};
QVector3D m_colorHigh{1.00f, 0.22f, 0.10f};
// MVP = Projection * View * Model。
// 这里 panel/dots 顶点基本已经是“世界坐标”,所以我们用 proj*view 即可model 先省略)。

View File

@@ -18,8 +18,6 @@ SerialBackend::SerialBackend(QObject* parent)
, m_decodeThread(&m_packetQueue, &m_frameQueue) {
m_request.dataLength = 24;
m_spec.model = QStringLiteral("PZR-A");
m_spec.rows = 3;
m_spec.cols = 4;
auto codec = std::make_shared<PiezoresistiveACodec>();
auto decoder = std::make_shared<PiezoresistiveADecoder>();
@@ -270,6 +268,16 @@ void SerialBackend::feedBytes(const QByteArray& data) {
m_readThread.enqueueBytes(data);
}
void SerialBackend::setSensorWidth(int w) {
m_spec.cols = w;
syncSendConfig_();
}
void SerialBackend::setSensorHeight(int h) {
m_spec.rows = h;
syncSendConfig_();
}
void SerialBackend::drainFrames_() {
if (!m_frameCallback)
return;

View File

@@ -69,6 +69,13 @@ public:
Q_INVOKABLE void requestOnce();
Q_INVOKABLE void feedBytes(const QByteArray& data);
int sensorWidth() const { return m_spec.cols; }
int sensorHeight() const { return m_spec.rows; }
public slots:
void setSensorWidth(int w);
void setSensorHeight(int h);
signals:
void portNameChanged();
void baudRateChanged();

View File

@@ -32,8 +32,8 @@ struct SensorRequest {
struct SensorSpec {
QString model;
QString version;
int rows = 0;
int cols = 0;
int rows = 12;
int cols = 7;
float pitch = 0.0f;
float dotRadius = 0.0f;
float rangeMin = 0.0f;