- Auto-scroll standard aktiv
- Tag-Filter wird gespeichert - Tag Monitor kann bei einem Tag jetzt pro Key mehrere Werte in einer Tabelle anzeigen - Bug für Clear behoben
This commit is contained in:
@@ -14,7 +14,7 @@ struct SerialConfig {
|
||||
QSerialPort::Parity parity = QSerialPort::NoParity;
|
||||
QSerialPort::StopBits stopBits = QSerialPort::OneStop;
|
||||
QSerialPort::FlowControl flowControl = QSerialPort::NoFlowControl;
|
||||
QString logFilePath; // empty = no logging
|
||||
QString logFilePath;
|
||||
};
|
||||
|
||||
class ConnectDialog : public QDialog
|
||||
@@ -23,7 +23,6 @@ class ConnectDialog : public QDialog
|
||||
|
||||
public:
|
||||
explicit ConnectDialog(QWidget *parent = nullptr);
|
||||
|
||||
SerialConfig config() const;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
#include <QSplitter>
|
||||
#include <QThread>
|
||||
#include <QTabWidget>
|
||||
#include <QSplitter>
|
||||
#include <QStatusBar>
|
||||
#include <QToolBar>
|
||||
#include <QLabel>
|
||||
#include <QAction>
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QSet>
|
||||
|
||||
#include "serialworker.h"
|
||||
#include "rawview.h"
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "tagwidget.h"
|
||||
#include "videowidget.h"
|
||||
#include "connectdialog.h"
|
||||
#include <QSet>
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -29,6 +28,9 @@ public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void onConnectClicked();
|
||||
void onDisconnectClicked();
|
||||
@@ -43,9 +45,6 @@ private slots:
|
||||
void configureTagFilter();
|
||||
void showAbout();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void setupToolBar();
|
||||
@@ -55,34 +54,29 @@ private:
|
||||
void saveSettings();
|
||||
void restoreSettings();
|
||||
|
||||
// Worker & thread
|
||||
QThread *m_thread = nullptr;
|
||||
SerialWorker *m_worker = nullptr;
|
||||
|
||||
// Views
|
||||
RawView *m_rawView = nullptr;
|
||||
TableView *m_tableView = nullptr;
|
||||
TagWidget *m_tagWidget = nullptr;
|
||||
VideoWidget *m_videoWidget= nullptr;
|
||||
|
||||
// Status bar widgets
|
||||
QLabel *m_portLabel = nullptr;
|
||||
QLabel *m_stateLabel = nullptr;
|
||||
QLabel *m_reconnectLabel = nullptr;
|
||||
QLabel *m_portLabel = nullptr;
|
||||
QLabel *m_stateLabel = nullptr;
|
||||
QLabel *m_reconnectLabel = nullptr;
|
||||
|
||||
// Toolbar widgets
|
||||
QCheckBox *m_autoReconnectCb = nullptr;
|
||||
QSpinBox *m_reconnectIntervalSb= nullptr;
|
||||
QCheckBox *m_autoReconnectCb = nullptr;
|
||||
QSpinBox *m_reconnectIntervalSb = nullptr;
|
||||
|
||||
// Actions
|
||||
QAction *m_connectAction = nullptr;
|
||||
QAction *m_disconnectAction = nullptr;
|
||||
|
||||
// Splitters (saved/restored via QSettings)
|
||||
QSplitter *m_mainSplitter = nullptr;
|
||||
QSplitter *m_rightSplitter = nullptr;
|
||||
QSplitter *m_mainSplitter = nullptr;
|
||||
QSplitter *m_rightSplitter = nullptr;
|
||||
|
||||
SerialConfig m_lastConfig;
|
||||
QSet<QString> m_suppressedTags; // tags hidden from Raw view
|
||||
bool m_shutdownDone = false;
|
||||
SerialConfig m_lastConfig;
|
||||
QSet<QString> m_suppressedTags; // tags hidden from Raw view
|
||||
bool m_shutdownDone = false;
|
||||
};
|
||||
|
||||
@@ -7,14 +7,12 @@
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QSpinBox>
|
||||
#include <QSet>
|
||||
#include <QRegularExpression>
|
||||
|
||||
// RawView shows the raw UART output in a QPlainTextEdit with:
|
||||
// - Timestamp prefix on every line (hh:mm:ss.zzz)
|
||||
// - Configurable tag suppression (lines whose tag is in the filter set are hidden)
|
||||
// - Unlimited history, H+V scrolling, incremental search
|
||||
// - Copy-to-clipboard and Clear buttons
|
||||
// RawView shows the raw UART output with timestamps, search, tag suppression,
|
||||
// unlimited history, H+V scrolling and a copy-to-clipboard button.
|
||||
// Auto-scroll is ON by default.
|
||||
class RawView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -23,7 +21,6 @@ public:
|
||||
explicit RawView(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
// suppressedTags: set of uppercase tag names whose lines should NOT appear here
|
||||
void appendLine(const QString &line, const QSet<QString> &suppressedTags = {});
|
||||
void clear();
|
||||
|
||||
@@ -43,7 +40,7 @@ private:
|
||||
QPushButton *m_copyBtn = nullptr;
|
||||
QCheckBox *m_autoScrollCb = nullptr;
|
||||
QLabel *m_lineCountLbl = nullptr;
|
||||
bool m_autoScroll = true;
|
||||
bool m_autoScroll = true; // ON by default
|
||||
int m_lineCount = 0;
|
||||
|
||||
static const QRegularExpression s_tagRe;
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
// SerialWorker lives in its own QThread and owns the QSerialPort.
|
||||
// It emits newLine() for every complete line received,
|
||||
// tagDetected() when a line contains a recognised tag like [WDG],
|
||||
// and clearScreen() when the ANSI clear-screen sequence \033[2J is received.
|
||||
// Auto-reconnect: if the port drops unexpectedly (e.g. USB cable pulled),
|
||||
// the worker retries every reconnectIntervalMs until success or closePort().
|
||||
// and clearScreen() when an ANSI clear-screen sequence is received.
|
||||
// Auto-reconnect: if the port drops unexpectedly, the worker retries
|
||||
// every reconnectIntervalMs until success or closePort().
|
||||
class SerialWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -21,9 +21,8 @@ public:
|
||||
explicit SerialWorker(QObject *parent = nullptr);
|
||||
~SerialWorker();
|
||||
|
||||
// Auto-reconnect configuration (call before openPort, or any time)
|
||||
void setAutoReconnect(bool enabled) { m_autoReconnect = enabled; }
|
||||
void setReconnectInterval(int ms) { m_reconnectIntervalMs = ms; }
|
||||
void setAutoReconnect(bool enabled) { m_autoReconnect = enabled; }
|
||||
void setReconnectInterval(int ms) { m_reconnectIntervalMs = ms; }
|
||||
|
||||
public slots:
|
||||
void openPort(const QString &portName, qint32 baudRate,
|
||||
@@ -31,17 +30,17 @@ public slots:
|
||||
QSerialPort::Parity parity,
|
||||
QSerialPort::StopBits stopBits,
|
||||
QSerialPort::FlowControl flowControl);
|
||||
void closePort(); // explicit user disconnect – stops reconnect
|
||||
void closePort();
|
||||
void setLogFile(const QString &path);
|
||||
void stopLogging();
|
||||
|
||||
signals:
|
||||
void newLine(const QString &line);
|
||||
void tagDetected(const QString &tag, const QString &value);
|
||||
void clearScreen(); // emitted when \033[2J\033[H (or \033[2J alone) is received
|
||||
void clearScreen();
|
||||
void portOpened();
|
||||
void portClosed();
|
||||
void reconnecting(int attempt); // fired each retry attempt
|
||||
void reconnecting(int attempt);
|
||||
void errorOccurred(const QString &message);
|
||||
|
||||
private slots:
|
||||
@@ -54,23 +53,22 @@ private:
|
||||
void processLine(const QString &line);
|
||||
void scheduleReconnect();
|
||||
|
||||
QSerialPort *m_port = nullptr;
|
||||
QFile *m_logFile = nullptr;
|
||||
QSerialPort *m_port = nullptr;
|
||||
QFile *m_logFile = nullptr;
|
||||
QTextStream *m_logStream = nullptr;
|
||||
QString m_buffer;
|
||||
QByteArray m_scanTail; // carries partial ANSI sequences across reads
|
||||
|
||||
// Reconnect state
|
||||
QTimer *m_reconnectTimer = nullptr;
|
||||
bool m_autoReconnect = true;
|
||||
bool m_userDisconnected = false; // true only after explicit closePort()
|
||||
int m_reconnectIntervalMs = 2000;
|
||||
int m_reconnectAttempt = 0;
|
||||
QTimer *m_reconnectTimer = nullptr;
|
||||
bool m_autoReconnect = true;
|
||||
bool m_userDisconnected = false;
|
||||
int m_reconnectIntervalMs = 2000;
|
||||
int m_reconnectAttempt = 0;
|
||||
|
||||
// Saved config for reconnect
|
||||
QString m_portName;
|
||||
qint32 m_baudRate = 115200;
|
||||
QSerialPort::DataBits m_dataBits = QSerialPort::Data8;
|
||||
QSerialPort::Parity m_parity = QSerialPort::NoParity;
|
||||
QSerialPort::StopBits m_stopBits = QSerialPort::OneStop;
|
||||
QSerialPort::FlowControl m_flowControl= QSerialPort::NoFlowControl;
|
||||
qint32 m_baudRate = 115200;
|
||||
QSerialPort::DataBits m_dataBits = QSerialPort::Data8;
|
||||
QSerialPort::Parity m_parity = QSerialPort::NoParity;
|
||||
QSerialPort::StopBits m_stopBits = QSerialPort::OneStop;
|
||||
QSerialPort::FlowControl m_flowControl = QSerialPort::NoFlowControl;
|
||||
};
|
||||
|
||||
@@ -10,13 +10,6 @@
|
||||
#include <QComboBox>
|
||||
|
||||
// TableView interprets UART lines as delimited data and shows them in a table.
|
||||
// The first line that matches the column count is used as the header row if it
|
||||
// starts with '#'. Otherwise column names are auto-generated (Col 1, Col 2, …).
|
||||
//
|
||||
// Supported delimiters: comma, semicolon, tab, pipe, space
|
||||
// To use: send lines like:
|
||||
// #time,temp,voltage,current
|
||||
// 1234,23.5,3.3,0.42
|
||||
class TableView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -37,16 +30,16 @@ private:
|
||||
void parseHeaders(const QString &line);
|
||||
bool tryAppendRow(const QString &line);
|
||||
|
||||
QTableWidget *m_table = nullptr;
|
||||
QComboBox *m_delimCombo = nullptr;
|
||||
QLineEdit *m_maxRowsEdit = nullptr;
|
||||
QPushButton *m_clearBtn = nullptr;
|
||||
QCheckBox *m_autoScrollCb = nullptr;
|
||||
QTableWidget *m_table = nullptr;
|
||||
QComboBox *m_delimCombo = nullptr;
|
||||
QLineEdit *m_maxRowsEdit = nullptr;
|
||||
QPushButton *m_clearBtn = nullptr;
|
||||
QCheckBox *m_autoScrollCb = nullptr;
|
||||
|
||||
QStringList m_headers;
|
||||
QList<QStringList> m_rows; // raw data for rebuild
|
||||
int m_maxRows = 500;
|
||||
char m_delimiter = ',';
|
||||
QList<QStringList> m_rows;
|
||||
int m_maxRows = 500;
|
||||
char m_delimiter = ',';
|
||||
bool m_autoScroll = true;
|
||||
bool m_headersSet = false;
|
||||
};
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
#include <QPushButton>
|
||||
#include <QTableWidget>
|
||||
#include <QStringList>
|
||||
#include <QMap>
|
||||
|
||||
// TagPanel displays the most recent data for a single tag (e.g. [WDG]).
|
||||
// Key=value pairs are shown in a table that updates in-place.
|
||||
// A "Copy" button copies the current values to the clipboard.
|
||||
// No timestamp is shown here – timestamps belong in the Raw view.
|
||||
// If a value contains ';', it is treated as a LIST: the key is shown once
|
||||
// and each list element gets its own row below it (key cell left empty
|
||||
// for the continuation rows, like a merged-cell look).
|
||||
// Example: Devs=trackdisk.device;input.device
|
||||
// Devs trackdisk.device
|
||||
// input.device
|
||||
class TagPanel : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -29,10 +34,16 @@ private slots:
|
||||
private:
|
||||
void parseKeyValue(const QString &value);
|
||||
void showRaw(const QString &value);
|
||||
void ensureRow(const QString &key);
|
||||
// Replaces all rows belonging to `key` with one row per value in `values`.
|
||||
void setKeyValues(const QString &key, const QStringList &values);
|
||||
|
||||
QString m_tag;
|
||||
QTableWidget *m_table = nullptr;
|
||||
QLabel *m_rawLabel = nullptr;
|
||||
QStringList m_keys;
|
||||
|
||||
// Track which table rows belong to which key, so repeated updates can
|
||||
// replace the previous rows instead of appending duplicates.
|
||||
// Maps key -> (first row index, row count)
|
||||
struct KeyRange { int firstRow; int count; };
|
||||
QMap<QString, KeyRange> m_keyRanges;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
@@ -25,6 +25,6 @@ public slots:
|
||||
void removeTag(const QString &tag);
|
||||
|
||||
private:
|
||||
QVBoxLayout *m_panelLayout = nullptr;
|
||||
QVBoxLayout *m_panelLayout = nullptr;
|
||||
QMap<QString, TagPanel*> m_panels;
|
||||
};
|
||||
|
||||
@@ -24,8 +24,8 @@ signals:
|
||||
void errorOccurred(const QString &message);
|
||||
|
||||
private:
|
||||
void stop(); // joins m_captureThread, closes device – safe from any thread
|
||||
void captureLoop(); // runs inside m_captureThread
|
||||
void stop();
|
||||
void captureLoop();
|
||||
bool openDevice(const QString &device, int width, int height);
|
||||
void closeDevice();
|
||||
bool initMmap();
|
||||
@@ -45,5 +45,5 @@ private:
|
||||
int m_nBuffers = 0;
|
||||
|
||||
std::atomic<bool> m_running{false};
|
||||
std::thread m_captureThread; // dedicated capture thread – not the Qt event thread
|
||||
std::thread m_captureThread;
|
||||
};
|
||||
|
||||
@@ -7,18 +7,9 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include "v4l2worker.h"
|
||||
|
||||
// VideoWidget shows a live preview of a V4L2 capture device.
|
||||
// It lives in the right-side panel, below the Tag Monitor.
|
||||
// Features:
|
||||
// - Device selector (auto-enumerates /dev/video*)
|
||||
// - Start / Stop capture
|
||||
// - Freeze-frame toggle
|
||||
// - Screenshot (saves current frame as PNG/JPG)
|
||||
// - Frame is scaled to fit the widget while keeping aspect ratio
|
||||
class VideoWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -28,12 +19,11 @@ public:
|
||||
~VideoWidget();
|
||||
|
||||
public:
|
||||
// Called by MainWindow::closeEvent to guarantee orderly shutdown before
|
||||
// widget destruction. Safe to call multiple times.
|
||||
void shutdown();
|
||||
void shutdown(); // safe to call multiple times, called from MainWindow::closeEvent
|
||||
|
||||
public slots:
|
||||
void onNewFrame(const QImage &frame);
|
||||
void clearFrame(); // blanks the preview (used on ANSI clear-screen)
|
||||
|
||||
private slots:
|
||||
void onStartStop();
|
||||
@@ -51,21 +41,18 @@ private:
|
||||
void stopCapture();
|
||||
void updateScaled();
|
||||
|
||||
// Worker
|
||||
QThread *m_thread = nullptr;
|
||||
V4l2Worker *m_worker = nullptr;
|
||||
|
||||
// Current frame
|
||||
QImage m_frame;
|
||||
QImage m_scaled;
|
||||
bool m_frozen = false;
|
||||
bool m_running = false;
|
||||
bool m_shutdownDone = false;
|
||||
|
||||
// UI
|
||||
QComboBox *m_deviceCombo = nullptr;
|
||||
QPushButton *m_startBtn = nullptr;
|
||||
QPushButton *m_freezeBtn = nullptr;
|
||||
QPushButton *m_screenshotBtn= nullptr;
|
||||
QLabel *m_infoLabel = nullptr;
|
||||
QComboBox *m_deviceCombo = nullptr;
|
||||
QPushButton *m_startBtn = nullptr;
|
||||
QPushButton *m_freezeBtn = nullptr;
|
||||
QPushButton *m_screenshotBtn = nullptr;
|
||||
QLabel *m_infoLabel = nullptr;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user