颜色映射图例,规格尺寸修改
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 先省略)。
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user