diff --git a/README.md b/README.md index 75ff1d6..3ce0f9e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@

QtSerialMonitor

- license - downloads - latest release + license + downloads + latest release

@@ -12,10 +12,10 @@ Universal serial monitor with data plotting capabilities, based on [Qt](https:// **Features:** - - In/out serial data terminal with command history, - UDP network protocol support, -- Advanced data plotter with multiple graphs support and basic data filtering - uses [QCustomPlot](https://www.qcustomplot.com/), +- Resizable UI widgets, +- Data plotter with multiple graphs support and basic data filtering - uses [QCustomPlot](https://www.qcustomplot.com/), - Printer support, ability to save graph as image, - Read/write ".txt" data logs, - many more... @@ -26,14 +26,12 @@ Universal serial monitor with data plotting capabilities, based on [Qt](https:// **Work in progress:** - + - Linux compatibility - 3D Orientation Demo - for IMU testing (user will be able to toggle between chart view and a simple 3D scene containing an object rotating accordingly to received roll, pitch and yaw values, representing the sensor's orientation), - - "What’s this ?" popups for less obvious widgets, - - Improvements, fixes etc. ---- diff --git a/src/QtSerialMonitor.pro b/src/QtSerialMonitor.pro index dc97911..e197e52 100644 --- a/src/QtSerialMonitor.pro +++ b/src/QtSerialMonitor.pro @@ -30,7 +30,9 @@ DEFINES += QT_DEPRECATED_WARNINGS CONFIG += c++11 SOURCES += \ + codeeditor.cpp \ filereader.cpp \ + highlighter.cpp \ infodialog.cpp \ logger.cpp \ main.cpp \ @@ -42,8 +44,10 @@ SOURCES += \ HEADERS += \ ../../../../../../Downloads/QCustomPlot.tar/qcustomplot/qcustomplot.h \ + codeeditor.h \ config.h \ filereader.h \ + highlighter.h \ infodialog.h \ logger.h \ mainwindow.h \ @@ -64,7 +68,8 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target DISTFILES += \ - QtSM.ico + QtSM.ico \ + TODO RESOURCES += \ 3dres.qrc diff --git a/src/TODO b/src/TODO new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/TODO @@ -0,0 +1 @@ + diff --git a/src/codeeditor.cpp b/src/codeeditor.cpp new file mode 100644 index 0000000..eedf64f --- /dev/null +++ b/src/codeeditor.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "codeeditor.h" + +CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent) +{ + lineNumberArea = new LineNumberArea(this); + + connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth); + connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea); + connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine); + + updateLineNumberAreaWidth(0); +} + +int CodeEditor::lineNumberAreaWidth() +{ + int digits = 1; + int max = qMax(1, blockCount()); + + while (max >= 10) + { + max /= 10; + ++digits; + } + + int space = 10 + fontMetrics().horizontalAdvance(" ") * digits; + + return space; +} + +void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */) +{ + setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); +} + +void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) +{ + if (dy) + lineNumberArea->scroll(0, dy); + else + lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height()); + + if (rect.contains(viewport()->rect())) + updateLineNumberAreaWidth(0); +} + +void CodeEditor::resizeEvent(QResizeEvent *e) +{ + QPlainTextEdit::resizeEvent(e); + + QRect cr = contentsRect(); + lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); +} + +void CodeEditor::highlightCurrentLine() +{ + QList extraSelections; + QTextEdit::ExtraSelection selection; + QColor lineColor = QColor(Qt::yellow).lighter(160); + + selection.format.setBackground(lineColor); + selection.format.setProperty(QTextFormat::FullWidthSelection, true); + selection.cursor = textCursor(); + selection.cursor.clearSelection(); + extraSelections.append(selection); + + setExtraSelections(extraSelections); + + selection.cursor.clearSelection(); + +} + +void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) +{ + QPainter painter(lineNumberArea); + painter.fillRect(event->rect(), Qt::lightGray); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int)blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int)blockBoundingRect(block).height(); + + while (block.isValid() && top <= event->rect().bottom()) + { + if (block.isVisible() && bottom >= event->rect().top()) + { + QString number = QString::number(blockNumber + 1); + painter.setPen(Qt::black); + painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(), Qt::AlignLeft, number); + } + + block = block.next(); + top = bottom; + bottom = top + (int)blockBoundingRect(block).height(); + ++blockNumber; + } +} diff --git a/src/codeeditor.h b/src/codeeditor.h new file mode 100644 index 0000000..ee33a3a --- /dev/null +++ b/src/codeeditor.h @@ -0,0 +1,60 @@ +#ifndef CODEEDITOR_H +#define CODEEDITOR_H + +#include + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QResizeEvent; +class QSize; +class QWidget; +QT_END_NAMESPACE + +class LineNumberArea; + +class CodeEditor : public QPlainTextEdit +{ + Q_OBJECT + +public: + CodeEditor(QWidget *parent = nullptr); + + void lineNumberAreaPaintEvent(QPaintEvent *event); + int lineNumberAreaWidth(); + +protected: + void resizeEvent(QResizeEvent *event) override; + +private slots: + void updateLineNumberAreaWidth(int newBlockCount); + void highlightCurrentLine(); + void updateLineNumberArea(const QRect &, int); + +private: + QWidget *lineNumberArea; +}; + +class LineNumberArea : public QWidget +{ +public: + LineNumberArea(CodeEditor *editor) : QWidget(editor) + { + codeEditor = editor; + } + + QSize sizeHint() const override + { + return QSize(codeEditor->lineNumberAreaWidth(), 0); + } + +protected: + void paintEvent(QPaintEvent *event) override + { + codeEditor->lineNumberAreaPaintEvent(event); + } + +private: + CodeEditor *codeEditor; +}; + +#endif diff --git a/src/config.h b/src/config.h index dba2bed..96ef134 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG_H #define CONFIG_H -#define VERSION "1.2" +#define VERSION "1.3" #define CHANGELOG_TEXT "" //"Changelog - version " VERSION ": \n" @@ -9,4 +9,8 @@ "Welcome to QtSerialMonitor, \n" \ "Press F1 to activate \"What's this\" mode. In this mode, clicked widget \r" \ "will display an explanation about its function. \n" + +#define RADIO_BUTTON_UPDATE_SERIAL_DEVICES_ON_INTERVAL 100 +#define SERIAL_DEVICE_CHECK_TIMER_INTERVAL 500 + #endif // CONFIG_H diff --git a/src/highlighter.cpp b/src/highlighter.cpp new file mode 100644 index 0000000..7051eac --- /dev/null +++ b/src/highlighter.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "highlighter.h" + +Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) +{ + HighlightingRule rule; + + keywordFormat.setForeground(Qt::darkBlue); + keywordFormat.setFontWeight(QFont::Bold); + // const QString keywordPatterns[] = { + // QStringLiteral("\\bchar\\b"), QStringLiteral("\\bclass\\b"), QStringLiteral("\\bconst\\b"), + // QStringLiteral("\\bdouble\\b"), QStringLiteral("\\benum\\b"), QStringLiteral("\\bexplicit\\b"), + // QStringLiteral("\\bfriend\\b"), QStringLiteral("\\binline\\b"), QStringLiteral("\\bint\\b"), + // QStringLiteral("\\blong\\b"), QStringLiteral("\\bnamespace\\b"), QStringLiteral("\\boperator\\b"), + // QStringLiteral("\\bprivate\\b"), QStringLiteral("\\bprotected\\b"), QStringLiteral("\\bpublic\\b"), + // QStringLiteral("\\bshort\\b"), QStringLiteral("\\bsignals\\b"), QStringLiteral("\\bsigned\\b"), + // QStringLiteral("\\bslots\\b"), QStringLiteral("\\bstatic\\b"), QStringLiteral("\\bstruct\\b"), + // QStringLiteral("\\btemplate\\b"), QStringLiteral("\\btypedef\\b"), QStringLiteral("\\btypename\\b"), + // QStringLiteral("\\bunion\\b"), QStringLiteral("\\bunsigned\\b"), QStringLiteral("\\bvirtual\\b"), + // QStringLiteral("\\bvoid\\b"), QStringLiteral("\\bvolatile\\b"), QStringLiteral("\\bbool\\b") + // }; + // for (const QString &pattern : keywordPatterns) { + // rule.pattern = QRegularExpression(pattern); + // rule.format = keywordFormat; + // highlightingRules.append(rule); + // } + + classFormat.setFontWeight(QFont::Bold); + classFormat.setForeground(Qt::darkMagenta); + rule.pattern = QRegularExpression(QStringLiteral("\\bQ[A-Za-z]+\\b")); + rule.format = classFormat; + highlightingRules.append(rule); + + singleLineCommentFormat.setForeground(Qt::red); + rule.pattern = QRegularExpression(QStringLiteral("//[^\n]*")); + rule.format = singleLineCommentFormat; + highlightingRules.append(rule); + + multiLineCommentFormat.setForeground(Qt::red); + + quotationFormat.setForeground(Qt::darkGreen); + rule.pattern = QRegularExpression(QStringLiteral("\".*\"")); + rule.format = quotationFormat; + highlightingRules.append(rule); + + functionFormat.setFontItalic(true); + functionFormat.setForeground(Qt::blue); + rule.pattern = QRegularExpression(QStringLiteral("\\b[A-Za-z0-9_]+(?=\\()")); + rule.format = functionFormat; + highlightingRules.append(rule); + + commentStartExpression = QRegularExpression(QStringLiteral("/\\*")); + commentEndExpression = QRegularExpression(QStringLiteral("\\*/")); +} + +void Highlighter::highlightBlock(const QString &text) +{ + for (const HighlightingRule &rule : qAsConst(highlightingRules)) + { + QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text, 0, + QRegularExpression::MatchType::PartialPreferCompleteMatch, + QRegularExpression::MatchOption::NoMatchOption); + if (matchIterator.hasNext()) + { + QRegularExpressionMatch match = matchIterator.next(); + setFormat(match.capturedStart(), match.capturedLength(), rule.format); + } + } + setCurrentBlockState(0); + + int startIndex = 0; + if (previousBlockState() != 1) + startIndex = text.indexOf(commentStartExpression); + + while (startIndex >= 0) + { + QRegularExpressionMatch match = commentEndExpression.match(text, startIndex); + int endIndex = match.capturedStart(); + int commentLength = 0; + if (endIndex == -1) + { + setCurrentBlockState(1); + commentLength = text.length() - startIndex; + } + else + { + commentLength = endIndex - startIndex + match.capturedLength(); + } + setFormat(startIndex, commentLength, multiLineCommentFormat); + startIndex = text.indexOf(commentStartExpression, startIndex + commentLength); + } +} diff --git a/src/highlighter.h b/src/highlighter.h new file mode 100644 index 0000000..ef3ee17 --- /dev/null +++ b/src/highlighter.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HIGHLIGHTER_H +#define HIGHLIGHTER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QTextDocument; +QT_END_NAMESPACE + +//! [0] +class Highlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + Highlighter(QTextDocument *parent = nullptr); + +protected: + void highlightBlock(const QString &text) override; + +private: + struct HighlightingRule + { + QRegularExpression pattern; + QTextCharFormat format; + }; + QVector highlightingRules; + + QRegularExpression commentStartExpression; + QRegularExpression commentEndExpression; + + QTextCharFormat keywordFormat; + QTextCharFormat classFormat; + QTextCharFormat singleLineCommentFormat; + QTextCharFormat multiLineCommentFormat; + QTextCharFormat quotationFormat; + QTextCharFormat functionFormat; +}; +//! [0] + +#endif // HIGHLIGHTER_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3990a57..8b0ad0c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -21,6 +21,21 @@ MainWindow::~MainWindow() delete ui; } +void MainWindow::closeEvent(QCloseEvent *event) +{ + // if (ui->pushButtonSerialConnect->isChecked() || ui->pushButtonUDPConnect->isChecked()) + + if (serial.isOpen() || networkUDP.isOpen()) + { + QMessageBox::StandardButton resBtn = QMessageBox::question(this, "About to exit...", tr("Connection open. Are you sure ? \n"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (resBtn == QMessageBox::No) + event->ignore(); + else + event->accept(); + } +} + void MainWindow::on_aboutToQuitSlot() { settingsSaveAll(); @@ -43,14 +58,27 @@ void MainWindow::setupGUI() this->setWindowTitle(this->windowTitle() + " " + VERSION); - ui->textBrowserLogs->document()->setMaximumBlockCount(ui->spinBoxMaxLines->value()); + // ui->textBrowserLogs + { + ui->textBrowserLogs->document()->setMaximumBlockCount(ui->spinBoxMaxLines->value()); + + if (ui->checkBoxWrapText->isChecked() == true) + ui->textBrowserLogs->setLineWrapMode(QPlainTextEdit::LineWrapMode::WidgetWidth); + else + ui->textBrowserLogs->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap); - foreach (auto item, QSerialPortInfo::standardBaudRates()) - ui->comboBoxBaudRates->addItem(QString::number(item)); + // highlighter = new Highlighter(ui->textBrowserLogs->document()); + } - connect(ui->comboBoxSend->lineEdit(), SIGNAL(returnPressed()), this, SLOT(on_comboBoxSendReturnPressedSlot())); + // ui->comboBoxBaudRates + { + foreach (auto item, QSerialPortInfo::standardBaudRates()) + ui->comboBoxBaudRates->addItem(QString::number(item)); - ui->comboBoxBaudRates->setCurrentIndex(ui->comboBoxBaudRates->count() - 3); // TODO SETTINGS ! + ui->comboBoxBaudRates->setCurrentIndex(ui->comboBoxBaudRates->count() - 3); // TODO SETTINGS ! + } + + connect(ui->comboBoxSend->lineEdit(), SIGNAL(returnPressed()), this, SLOT(on_comboBoxSendReturnPressedSlot())); ui->comboBoxTracerStyle->addItem("Crosshair"); ui->comboBoxTracerStyle->addItem("Circle"); @@ -105,7 +133,7 @@ void MainWindow::setupGUI() if (ui->checkBoxAutoRefresh->isChecked()) { - serialDeviceCheckTimer->start(500); + serialDeviceCheckTimer->start(SERIAL_DEVICE_CHECK_TIMER_INTERVAL); ui->pushButtonRefresh->setEnabled(false); } else @@ -484,8 +512,8 @@ void MainWindow::processTable(QStringList labels, QList values) void MainWindow::on_printIntroChangelog() // TODO { ui->pushButtonTextLogToggle->setChecked(false); - ui->textBrowserLogs->append(INTRO_TEXT); - ui->textBrowserLogs->append("\n" CHANGELOG_TEXT); + ui->textBrowserLogs->appendPlainText(INTRO_TEXT); + ui->textBrowserLogs->appendPlainText("\n" CHANGELOG_TEXT); // ui->textBrowserLogs->horizontalScrollBar()->setValue(0); } @@ -792,14 +820,12 @@ void MainWindow::on_chartMouseMoveHandler(QMouseEvent *event) void MainWindow::on_updateSerialDeviceList() { - QList devices = QSerialPortInfo::availablePorts(); + QList devices = serial.getAvailiblePorts(); QList portNames; static QList portNamesOld; foreach (auto item, devices) - { portNames.append(item.portName()); - } if ((devices.count() >= 1) && (!(portNames.toSet().intersects(portNamesOld.toSet())) || (portNames.count() != portNamesOld.count()))) { @@ -813,13 +839,13 @@ void MainWindow::on_updateSerialDeviceList() else if ((devices.count() < 1) && !ui->comboBoxDevices->itemText(0).startsWith("No COM devices")) { ui->comboBoxDevices->clear(); - ui->comboBoxDevices->addItem("No COM devices detected :("); + ui->comboBoxDevices->addItem("No serial devices detected :("); ui->comboBoxDevices->setCurrentIndex(ui->comboBoxDevices->count() - 1); } portNamesOld = portNames; - this->radioButtonTimer->start(100); + this->radioButtonTimer->start(RADIO_BUTTON_UPDATE_SERIAL_DEVICES_ON_INTERVAL); ui->radioButtonDeviceUpdate->setChecked(true); } @@ -839,7 +865,7 @@ void MainWindow::addLog(QString text) if (ui->checkBoxShowTime->isChecked()) text = currentDateTime + text; - ui->textBrowserLogs->append(text); + ui->textBrowserLogs->appendPlainText(text); // ui->textBrowserLogs->insertPlainText(text); // ui->textBrowserLogs->moveCursor(QTextCursor::MoveOperation::End, QTextCursor::MoveMode::MoveAnchor); @@ -847,7 +873,7 @@ void MainWindow::addLog(QString text) } } -void MainWindow::addLogBytes(QString prefix, QByteArray bytes, bool hexToBinary) +void MainWindow::addLogBytes(QByteArray bytes, bool hexToBinary) { if (ui->pushButtonTextLogToggle->isChecked() == false) { @@ -856,10 +882,9 @@ void MainWindow::addLogBytes(QString prefix, QByteArray bytes, bool hexToBinary) QString bytesText; if (hexToBinary == false) - bytesText = prefix + bytes.toHex(' '); + bytesText = bytes.toHex(' '); else { - bytesText.append(prefix); for (auto i = 0; i < bytes.size(); ++i) { bytesText.append(QString::number(bytes[i], 2) + ' '); @@ -869,7 +894,7 @@ void MainWindow::addLogBytes(QString prefix, QByteArray bytes, bool hexToBinary) if (ui->checkBoxShowTime->isChecked()) bytesText = currentDateTime + bytesText; - ui->textBrowserLogs->append(bytesText); + ui->textBrowserLogs->appendPlainText(bytesText); } } @@ -897,15 +922,15 @@ void MainWindow::on_processSerial() if (ui->comboBoxFormat->currentIndex() == 0 && serialInput.isEmpty() == false) { - addLog("Serial <<\t" + serialInput); + addLog(serialInput); } else if (ui->comboBoxFormat->currentIndex() == 1 && serialInput.length() > 0) { - addLogBytes("Serial (HEX) <<\t", serialInput.toUtf8()); + addLogBytes(serialInput.toUtf8()); } else if (ui->comboBoxFormat->currentIndex() == 2 && serialInput.length() > 0) { - addLogBytes("Serial (BIN) <<\t", serialInput.toUtf8(), true); + addLogBytes(serialInput.toUtf8(), true); } if (serialInput.isEmpty() == false) @@ -946,15 +971,15 @@ void MainWindow::on_processUDP() if (ui->comboBoxFormat->currentIndex() == 0 && udpInput.isEmpty() == false) { - addLog("UDP <<\t" + udpInput); + addLog(udpInput); } else if (ui->comboBoxFormat->currentIndex() == 1 && udpInput.length() > 0) { - addLogBytes("UDP (HEX) <<\t", udpInput.toUtf8()); + addLogBytes(udpInput.toUtf8()); } else if (ui->comboBoxFormat->currentIndex() == 2 && udpInput.length() > 0) { - addLogBytes("UDP (BIN) <<\t", udpInput.toUtf8(), true); + addLogBytes(udpInput.toUtf8(), true); } if (udpInput.isEmpty() == false) @@ -992,7 +1017,7 @@ void MainWindow::sendUDPDatagram(QString message) networkUDP.write(message, QHostAddress(ui->lineEditUDPTargetIP->text()), ui->spinBoxUDPTargetPort->value()); } - addLog("UDP >>\t" + message); + // addLog("UDP >>\t" + message); } void MainWindow::processChart(QStringList labelList, QList numericDataList, QList timeStampsList) @@ -1171,9 +1196,7 @@ void MainWindow::keyPressEvent(QKeyEvent *event) void MainWindow::sendSerial(QString message) { - if (serial.send(message)) - this->addLog("Serial >>\t" + message); - else + if (!serial.send(message)) this->addLog("App >>\t Unable to send! Serial port closed !"); } @@ -1268,7 +1291,7 @@ void MainWindow::on_checkBoxAutoRefresh_toggled(bool checked) if (checked == true) { ui->pushButtonRefresh->setEnabled(false); - serialDeviceCheckTimer->start(500); + serialDeviceCheckTimer->start(SERIAL_DEVICE_CHECK_TIMER_INTERVAL); } else { @@ -1330,9 +1353,9 @@ void MainWindow::on_highlighLog(QString searchString) void MainWindow::on_checkBoxWrapText_toggled(bool checked) { if (checked) - ui->textBrowserLogs->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); + ui->textBrowserLogs->setLineWrapMode(QPlainTextEdit::LineWrapMode::WidgetWidth); else - ui->textBrowserLogs->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap); + ui->textBrowserLogs->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap); } void MainWindow::on_checkBoxEnableTracer_toggled(bool checked) @@ -1528,9 +1551,7 @@ void MainWindow::on_pushButtonSerialConnect_toggled(bool checked) return; } - // clearGraphData(true); - - QString parsedPortName = ui->comboBoxDevices->currentText().mid(ui->comboBoxDevices->currentText().indexOf("COM"), ui->comboBoxDevices->currentText().indexOf(")") - 1); + QString parsedPortName = QSerialPortInfo::availablePorts().at(ui->comboBoxDevices->currentIndex()).portName(); qint32 parsedBaudRate = ui->comboBoxBaudRates->currentText().toInt(); QString dataBits = ui->comboBoxDataBits->currentText(); QString stopBits = ui->comboBoxStopBits->currentText(); @@ -2158,17 +2179,10 @@ void MainWindow::on_comboBoxClockSource_currentIndexChanged(int index) } } -void MainWindow::closeEvent(QCloseEvent *event) +void MainWindow::on_comboBoxFormat_currentIndexChanged(int index) { - // if (ui->pushButtonSerialConnect->isChecked() || ui->pushButtonUDPConnect->isChecked()) - - if (serial.isOpen() || networkUDP.isOpen()) - { - QMessageBox::StandardButton resBtn = QMessageBox::question(this, "About to exit...", tr("Connection open. Are you sure ? \n"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - if (resBtn == QMessageBox::No) - event->ignore(); - else - event->accept(); - } + if (index == 0) + ui->comboBoxTextProcessing->setEnabled(true); + else + ui->comboBoxTextProcessing->setEnabled(false); } diff --git a/src/mainwindow.h b/src/mainwindow.h index a2f2bdb..f3b665c 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -9,6 +9,7 @@ #include "qcustomplot.h" #include "serial.h" #include "infodialog.h" +#include "highlighter.h" #include #include @@ -85,6 +86,7 @@ private slots: void on_checkBoxWrapText_toggled(bool checked); void on_clearGraphSelection(); void on_comboBoxClockSource_currentIndexChanged(int index); + void on_comboBoxFormat_currentIndexChanged(int index); void on_comboBoxGraphDisplayMode_currentIndexChanged(const QString &arg1); void on_comboBoxLoggingMode_currentIndexChanged(int index); void on_comboBoxSendReturnPressedSlot(); @@ -154,8 +156,8 @@ private slots: QTimer *udpStringProcessingTimer; Serial serial; Ui::MainWindow *ui; + Highlighter *highlighter; void addLog(QString text); - void addLogBytes(QString prefix, QByteArray bytes, bool hexToBinary = false); void chartPrintPreview(); void clearGraphData(bool replot); void clearGraphs(bool replot); @@ -176,6 +178,7 @@ private slots: void setupGUI(); void setupTable(); void writeLogToFile(QString rawLine, QStringList labelList, QList dataList, QList timeList); + void addLogBytes(QByteArray bytes, bool hexToBinary = false); protected: void keyPressEvent(QKeyEvent *event); }; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index a2c10f1..b8636a4 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1519,25 +1519,16 @@ 0 - + 0 0 - - - Consolas - 9 - - <html><head/><body><p>This is the main text browser. Received messages and notification will be displayed here.</p></body></html> - - QTextEdit::NoWrap - true @@ -1547,52 +1538,46 @@ - - + + - + 0 0 - - Disable + + <html><head/><body><p>Highlights the words in the main text window that matches the contents of this textbox</p></body></html> - + + Text to highlight... + + true - - + + - + 0 0 - <html><head/><body><p>Choose message display format:</p><p>- text</p><p>- hex (base 16)</p><p>- binary (base 2)</p></body></html> + <html><head/><body><p>If checked, pressed key will be immidietly sent to the device via Serial or UDP, depending on the active control section tab as long as the <span style=" font-weight:600;">text window remains focused</span>.</p><p>This is very useful for creating a rapidly accesible control interface via serial i.e, you could quickly regulate motor's PWM output by holding &quot;+&quot; or &quot;-&quot; keys, instead of typing &quot;set value ... &quot; every time you need to make a change.</p></body></html> + + + Send Key + + + true - - - TXT - - - - - HEX - - - - - BIN - - - - + + 0 @@ -1600,12 +1585,25 @@ - <html><head/><body><p>Highlights the words in the main text window that matches the contents of this textbox</p></body></html> + <html><head/><body><p>If checked, received message will be displayed with system clock time in which it was received.</p></body></html> - - Text to highlight... + + Timestamp - + + + + + + + 0 + 0 + + + + Disable + + true @@ -1685,42 +1683,35 @@ - - - - - 0 - 0 - - - - <html><head/><body><p>If checked, pressed key will be immidietly sent to the device via Serial or UDP, depending on the active control section tab as long as the <span style=" font-weight:600;">text window remains focused</span>.</p><p>This is very useful for creating a rapidly accesible control interface via serial i.e, you could quickly regulate motor's PWM output by holding &quot;+&quot; or &quot;-&quot; keys, instead of typing &quot;set value ... &quot; every time you need to make a change.</p></body></html> - - - Send Key - - - true - - - - - + + - + 0 0 - <html><head/><body><p>If checked, received message will be displayed with system clock time in which it was received.</p></body></html> - - - Timestamp + <html><head/><body><p>Choose message display format:</p><p><span style=" font-weight:600;">- text</span></p><p><span style=" font-weight:600;">- hex (base 16)</span></p><p><span style=" font-weight:600;">- binary (base 2)</span></p></body></html> + + + TXT + + + + + HEX + + + + + BIN + + - + @@ -1728,6 +1719,9 @@ 0 + + <html><head/><body><p>Choose text post-processing:</p><p><span style=" font-weight:600;">- None</span></p><p><span style=" font-weight:600;">- Trim - remove whitespace form the start and the end of each line.</span></p><p><span style=" font-weight:600;">- Simplify - removes whitespace form the start and the end, and that has each sequence of internal whitespace replaced with a single space.</span></p></body></html> + None @@ -2284,7 +2278,7 @@ p, li { white-space: pre-wrap; } - <html><head/><body><p>Sets the missing samples threshold value. If process string doesnt contain related data n times in a row it is then automatically deleted.</p></body></html> + <html><head/><body><p>Sets the missing samples threshold value. If processed string doesnt contain one new data for the already present graph n times in a row it is then this graph will be automatically deleted. This is useful when switching between diffrent steaming data sets.</p></body></html> Qt::AlignCenter @@ -2673,6 +2667,11 @@ p, li { white-space: pre-wrap; }
qcustomplot.h
1 + + CodeEditor + QTextBrowser +
codeeditor.h
+
tabWidgetControlSection diff --git a/src/serial.cpp b/src/serial.cpp index 5359e99..54dbf1f 100644 --- a/src/serial.cpp +++ b/src/serial.cpp @@ -179,6 +179,11 @@ bool Serial::isOpen() return serialDevice->isOpen(); } +QList Serial::getAvailiblePorts() +{ + return QSerialPortInfo::availablePorts(); +} + int Serial::getAvailiblePortsCount() { return QSerialPortInfo::availablePorts().count(); diff --git a/src/serial.h b/src/serial.h index 9a5a904..5fc983b 100644 --- a/src/serial.h +++ b/src/serial.h @@ -27,6 +27,7 @@ class Serial : public QObject bool send(QString message); bool setReadMode(int mode); int getAvailiblePortsCount(); + QList getAvailiblePorts(); QString getSerialInfo(); QString getString(bool clearBuffer = true); void clearAll(bool clearHardwareBuffers = false);