QT5.9.9的串口上位机例程

首先安装QT5.9.9

这里时必须要用邮箱注册一个账号的,之前的版本是可以跳过这个步骤的,注册完在上面登录你的账号密码就可以下一步了。

在下一步到选择安装的组件,直接选择MinGW这个,选择这个就不用在电脑上面装VS了,我只勾选了这一个,其他的暂时用不到,然后下一步直接等到安装完成。

 可以在用试着新建一个HelloWorld,具体操作参考QT学习之路2:《Qt 学习之路 2》目录 - DevBean Tech World

,基本操作也在QT学习之路2上看一下,然后用一个大神的串口上位机的例程做一些简单操作,熟悉一下。

打开所用串口例程的.pro工程文件,会提示你configure project,你直接点一下就ok,自动帮你配置好。

 然后你可以在主程序里面修改一下上位机的版本信息等

 我还想换一个程序的图标,然后我在:齿轮图标_站长素材这里下载了一个.ico格式的图标。

把原有文件夹下面的图片和.rc文件都删掉,换上新的图标,.rc文档里面的内容为:IDI_ICON1 ICON DISCARDABLE "main.ico"

这个文件夹下面本来还有一个.qrc文件,这个文件需要新建一个,具体方法是:

右击项目,点Add New..,出现下面界面,选择Qt,Qt Resource File然后新建一个名称放在工程目录下,这个就建好了

建好后把你图片的.ico文件放到这个目录下。

 然后你重新运行一下,应用程序的图标就更改了,在再把ui界面修改一下

找个串口线,把TX,RX接一起测试一下。确认是好用的

如果有需要用qt的上位机软件工程代码,可以在下面评论告诉我你的邮箱,我看到会直接发到你的邮箱。

 主函数

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    a.setStyle("fusion");

    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow文件:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    createTimers();
    createChart();
    setupTable();
    setupGUI();

    settingsLoadAll();

    infoDialog.setFixedSize(800, 600);

    connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(on_aboutToQuitSlot()));
}

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()
{
    qDebug() << "on_aboutToQuitSlot";
    settingsSaveAll();
}

void MainWindow::createTimers()
{
    this->serialDeviceCheckTimer = new QTimer(this);
    this->radioButtonTimer = new QTimer(this);
    this->serialStringProcessingTimer = new QTimer(this);
    this->udpStringProcessingTimer = new QTimer(this);

    connect(serialDeviceCheckTimer, SIGNAL(timeout()), this, SLOT(on_updateSerialDeviceList()));
    connect(radioButtonTimer, &QTimer::timeout, this, [=]()
            { ui->radioButtonDeviceUpdate->setChecked(false); });
}

void MainWindow::setupGUI()
{
    // ----------------------- MainWindow ----------------------- //
    {
        connect(ui->comboBoxSend->lineEdit(), SIGNAL(returnPressed()), this, SLOT(on_comboBoxSendReturnPressedSlot()));
        this->setWindowTitle(this->windowTitle() + " " + VERSION);
    }
    // ---------------------------------------------------------- //

    // ----------------------- 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);

        //   highlighter = new Highlighter(ui->textBrowserLogs->document());
    }
    // ---------------------------------------------------------------- //

    // ----------------------- standardBaudRates ----------------------- //
    {
        foreach (auto item, serial.getAvailibleBaudRates())
            ui->comboBoxBaudRates->addItem(QString::number(item));

        ui->comboBoxBaudRates->setCurrentIndex(ui->comboBoxBaudRates->count() / 2); // select middle
    }
    // ----------------------------------------------------------------- //

    if (ui->checkBoxAutoRefresh->isChecked())
    {
        serialDeviceCheckTimer->start(SERIAL_DEVICE_CHECK_TIMER_INTERVAL);
        ui->pushButtonRefresh->setEnabled(false);
    }
    else
    {
        ui->pushButtonRefresh->setEnabled(true);
    }

    // ----------------------- comboBoxGraphDisplayMode ----------------------- //
    {
        if (ui->comboBoxGraphDisplayMode->currentIndex() == 0)
            ui->lineEditCustomParsingRules->setEnabled(false);
        else
            ui->lineEditCustomParsingRules->setEnabled(true);
    }
    // ----------------------------------------------------------------- //

    ui->comboBoxExternalTimeFormat->addItem("[ms]");
    ui->comboBoxExternalTimeFormat->setCurrentIndex(0);
    ui->lineEditLoadFilePath->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
    ui->lineEditSaveFileName->setText("Log.txt");
    ui->lineEditSaveLogPath->setText(qApp->applicationDirPath() + "/Logs");
    ui->splitterGraphTable->setSizes({this->height(), 0});

    emit on_checkBoxAutoLogging_toggled(ui->checkBoxAutoLogging->isChecked());
    emit on_checkBoxShowLegend_toggled(ui->checkBoxShowLegend->isChecked());
    emit on_comboBoxClockSource_currentIndexChanged(ui->comboBoxClockSource->currentIndex());

    on_updateSerialDeviceList();
}

void MainWindow::createChart()
{
    // ui->widgetChart->setOpenGl(false);
    ui->splitterReceivedData->setSizes(QList<int>({200, 400})); // default
    ui->widgetChart->setInteractions(QCP::iRangeZoom |
                                     QCP::iRangeDrag |
                                     QCP::iSelectPlottables |
                                     QCP::iSelectLegend);

    QSharedPointer<QCPAxisTickerTime> xTicker(new QCPAxisTickerTime);
    QSharedPointer<QCPAxisTicker> yTicker(new QCPAxisTicker);
    xTicker->setTimeFormat("%h:%m:%s:%z");
    xTicker->setTickCount(5);
    yTicker->setTickCount(10);
    yTicker->setTickStepStrategy(QCPAxisTicker::TickStepStrategy::tssReadability);
    ui->widgetChart->xAxis->setTicker(xTicker);
    ui->widgetChart->yAxis->setTicker(yTicker);
    ui->widgetChart->axisRect()->setupFullAxesBox(true);
    ui->widgetChart->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignBottom | Qt::AlignLeft);
    ui->widgetChart->legend->setVisible(true);
    ui->widgetChart->legend->setFont(QFont("MS Shell Dlg 2", 8, QFont::Thin, false));
    ui->widgetChart->legend->setIconSize(15, 10);
    ui->widgetChart->legend->setSelectableParts(QCPLegend::SelectablePart::spItems); // legend box shall not be selectable, only legend items
    ui->widgetChart->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
    ui->widgetChart->xAxis->setRange(-1.0, ui->spinBoxScrollingTimeRange->value());
    ui->widgetChart->yAxis->setRange(-1.0, 1.0);

    connect(ui->widgetChart, SIGNAL(mouseDoubleClick(QMouseEvent *)), this, SLOT(on_chartMouseDoubleClickHandler(QMouseEvent *)));
    connect(ui->widgetChart, SIGNAL(mousePress(QMouseEvent *)), this, SLOT(on_chartMousePressHandler(QMouseEvent *)));
    connect(ui->widgetChart, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(on_chartMouseMoveHandler(QMouseEvent *)));

    connect(ui->widgetChart, SIGNAL(selectionChangedByUser()), this, SLOT(on_chartSelectionChanged()));
    connect(ui->widgetChart, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_chartContextMenuRequest(QPoint)));
    connect(ui->widgetChart, SIGNAL(beforeReplot()), this, SLOT(on_chartBeforeReplotSlot()));

    //  ui->widgetChart->setNotAntialiasedElements(QCP::aeAll);

    if (ui->checkBoxEnableTracer->isChecked())
    {
        createChartTracer();
        connect(ui->widgetChart, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(on_tracerShowPointValue(QMouseEvent *)));
    }

    ui->textBrowserLogs->setHighlightEnabled(false);
    emit on_comboBoxLogFormat_currentIndexChanged(ui->comboBoxLogFormat->currentIndex());
}

void MainWindow::setupTable()
{
    ui->tableWidgetParsedData->clear();
    ui->tableWidgetParsedData->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
    ui->tableWidgetParsedData->setColumnCount(2);
    ui->tableWidgetParsedData->setHorizontalHeaderItem(0, new QTableWidgetItem("Name"));
    ui->tableWidgetParsedData->setHorizontalHeaderItem(1, new QTableWidgetItem("Current Value"));
    //    ui->tableWidgetParsedData->setHorizontalHeaderItem(2, new QTableWidgetItem("Max"));
    //    ui->tableWidgetParsedData->setHorizontalHeaderItem(3, new QTableWidgetItem("Min"));

    ui->tableWidgetLogTable->clear();
    ui->tableWidgetLogTable->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
    ui->tableWidgetLogTable->setColumnCount(1);
    ui->tableWidgetLogTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Time:"));
    ui->tableWidgetLogTable->setSortingEnabled(true);
}

void MainWindow::settingsLoadAll()
{
    QSettings appSettings(QSettings::Format::IniFormat,
                          QSettings::Scope::UserScope,
                          "Qt Vitrea", "Qt Vitrea"); // Settings

    // ----- Info ----- //
    {
        if (appSettings.value("Info/version").value<QString>() != VERSION)
        {
            on_printIntroChangelog();
        }

        if (appSettings.value("Info/organizationName").value<QString>() != appSettings.organizationName() ||
            appSettings.value("Info/applicationName").value<QString>() != appSettings.applicationName())
        {
            qDebug() << "Abort loading settings ! organizationName or applicationName incorrect. Config file might be missing.";
            addLog("App >>\t Error loading settings. Config file incorrect !", true);
            return;
        }
    }
    // ------------------------- //

    // ----- Window size ----- //
    {
        QStringList windowSize = appSettings.value("layout/windowSize.size").value<QString>().split(QRegExp("\\s+"), QString::SplitBehavior::SkipEmptyParts);

        if (!windowSize.isEmpty())
            this->resize(windowSize.first().trimmed().toInt(), windowSize.last().trimmed().toInt());
    }
    // ------------------------- //

    // ----- splitterSizes ----- //
    {
        QStringList splitterSizes;

        splitterSizes = appSettings.value("layout/splitterReceivedData.sizes").value<QString>().split(QRegExp("\\s+"), QString::SplitBehavior::SkipEmptyParts);
        if (!splitterSizes.isEmpty())
            ui->splitterReceivedData->setSizes(QList<int>({splitterSizes.first().trimmed().toInt(), splitterSizes.last().trimmed().toInt()}));

        splitterSizes = appSettings.value("layout/splitterGraphTable.sizes").value<QString>().split(QRegExp("\\s+"), QString::SplitBehavior::SkipEmptyParts);
        if (!splitterSizes.isEmpty())
            ui->splitterGraphTable->setSizes(QList<int>({splitterSizes.first().trimmed().toInt(), splitterSizes.last().trimmed().toInt()}));

        if (ui->splitterGraphTable->sizes().last() > 0)
            ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::DownArrow);
        else
            ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::UpArrow);
    }
    // ------------------------- //

    // ----- comboBoxSendItems ----- //
    {
        ui->comboBoxSend->addItems(QStringList(appSettings.value("data/comboBoxSendHistory").value<QString>().split(QRegExp("\\n+"), Qt::SplitBehaviorFlags::SkipEmptyParts)));
        ui->comboBoxSend->lineEdit()->clear();
    }
    // ------------------------- //

    // ----- Other GUI Elements ----- //
    {
        ui->checkBoxAppendDate->setChecked(appSettings.value("GUI_Elements/checkBoxAppendDate.isChecked").value<bool>());
        ui->checkBoxAutoLogging->setChecked(appSettings.value("GUI_Elements/checkBoxAutoLogging.isChecked").value<bool>());
        ui->checkBoxAutoRefresh->setChecked(appSettings.value("GUI_Elements/checkBoxAutoRefresh.isChecked").value<bool>());
        ui->checkBoxAutoRescaleY->setChecked(appSettings.value("GUI_Elements/checkBoxAutoRescaleY.isChecked").value<bool>());
        ui->checkBoxAutoSaveBuffer->setChecked(appSettings.value("GUI_Elements/checkBoxAutoSaveBuffer.isChecked", true).value<bool>());
        ui->checkBoxAutoScrollLogTable->setChecked(appSettings.value("GUI_Elements/checkBoxAutoScrollLogTable.isChecked", true).value<bool>());
        ui->checkBoxAutoSizeColumnsLogTable->setChecked(appSettings.value("GUI_Elements/checkBoxAutoSizeColumnsLogTable.isChecked", true).value<bool>());
        ui->checkBoxAutoTrack->setChecked(appSettings.value("GUI_Elements/checkBoxAutoTrack.isChecked").value<bool>());
        ui->checkBoxDTR->setChecked(appSettings.value("GUI_Elements/checkBoxDTR.isChecked").value<bool>());
        ui->checkBoxEnableTracer->setChecked(appSettings.value("GUI_Elements/checkBoxEnableTracer.isChecked").value<bool>());
        ui->checkBoxRAMClearChart->setChecked(appSettings.value("GUI_Elements/checkBoxRAMClearChart.isChecked").value<bool>());
        ui->checkBoxScrollLogEnableSorting->setChecked(appSettings.value("GUI_Elements/checkBoxScrollLogEnableSorting.isChecked", true).value<bool>());
        ui->checkBoxScrollToButtom->setChecked(appSettings.value("GUI_Elements/checkBoxScrollToButtom.isChecked").value<bool>());
        ui->checkBoxSendKey->setChecked(appSettings.value("GUI_Elements/checkBoxSendKey.isChecked").value<bool>());
        ui->checkBoxShowControlChars->setChecked(appSettings.value("GUI_Elements/checkBoxShowControlChars.isChecked", false).value<bool>());
        ui->checkBoxSimplifyLog->setChecked(appSettings.value("GUI_Elements/checkBoxSimplifyLog.isChecked").value<bool>());
        ui->checkBoxTableAutoResize->setChecked(appSettings.value("GUI_Elements/checkBoxTableAutoResize.isChecked", true).value<bool>());
        ui->checkBoxTruncateFileOnSave->setChecked(appSettings.value("GUI_Elements/checkBoxTruncateFileOnSave.isChecked", false).value<bool>());
        ui->checkBoxWrapText->setChecked(appSettings.value("GUI_Elements/checkBoxWrapText.isChecked").value<bool>());
        ui->comboBoxAddTextMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxAddTextMode.currentIndex").value<int>());
        ui->comboBoxBaudRates->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxBaudRates.currentIndex").value<int>());
        ui->comboBoxClockSource->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxClockSource.currentIndex").value<int>());
        ui->comboBoxDataBits->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxDataBits.currentIndex").value<int>());
        ui->comboBoxExternalTimeFormat->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxExternalTimeFormat.currentIndex").value<int>());
        ui->comboBoxFlowControl->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxFlowControl.currentIndex").value<int>());
        ui->comboBoxGraphDisplayMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxGraphDisplayMode.currentIndex").value<int>());
        ui->comboBoxLineEnding->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxLineEnding.currentIndex").value<int>());
        ui->comboBoxLoggingMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxLoggingMode.currentIndex").value<int>());
        ui->comboBoxMessagesDisplayMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxMessagesDisplayMode.currentIndex").value<int>());
        ui->comboBoxParity->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxParity.currentIndex").value<int>());
        ui->comboBoxRAMLoadMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxRAMLoadMode.currentIndex").value<int>());
        ui->comboBoxRAMSaveMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxRAMSaveMode.currentIndex").value<int>());
        ui->comboBoxSerialReadMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxSerialReadMode.currentIndex").value<int>());
        ui->comboBoxStopBits->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxStopBits.currentIndex").value<int>());
        ui->comboBoxTextProcessing->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxTextProcessing.currentIndex").value<int>());
        ui->comboBoxTracerStyle->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxTracerStyle.currentIndex").value<int>());
        ui->comboBoxUDPReceiveMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxUDPReceiveMode.currentIndex").value<int>());
        ui->comboBoxUDPSendMode->setCurrentIndex(appSettings.value("GUI_Elements/comboBoxUDPSendMode.currentIndex").value<int>());
        ui->lineEditCustomParsingRules->setText(appSettings.value("data/lineEditCustomParsingRules.text").value<QString>());
        ui->lineEditExternalClockLabel->setText(appSettings.value("data/lineEditExternalClockLabel.text").value<QString>());
        ui->lineEditLoadFilePath->setText(appSettings.value("data/lineEditLoadFilePath.text").value<QString>());
        ui->lineEditSaveFileName->setText(appSettings.value("data/lineEditSaveFileName.text").value<QString>());
        ui->lineEditSaveLogPath->setText(appSettings.value("data/lineEditSaveLogPath.text").value<QString>());
        ui->lineEditUDPTargetIP->setText(appSettings.value("data/lineEditUDPTargetIP.text").value<QString>());
        ui->pushButtonEnablePlot->setChecked(appSettings.value("GUI_Elements/pushButtonEnablePlot.isChecked", false).value<bool>());
        ui->pushButtonEnableTableLog->setChecked(appSettings.value("GUI_Elements/pushButtonEnableTableLog.isChecked", true).value<bool>());
        ui->spinBoxMaxGraphs->setValue(appSettings.value("layout/spinBoxMaxGraphs.value").value<int>());
        ui->spinBoxMaxLines->setValue(appSettings.value("layout/spinBoxMaxLines.value").value<int>());
        ui->spinBoxMaxRowsLogTable->setValue(appSettings.value("layout/spinBoxMaxRowsLogTable.value").value<int>());
        ui->spinBoxMaxTimeRange->setValue(appSettings.value("layout/spinBoxMaxTimeRange.value").value<int>());
        ui->spinBoxProcessingDelay->setValue(appSettings.value("layout/spinBoxProcessingDelay.value").value<int>());
        ui->spinBoxRemoveOldLabels->setValue(appSettings.value("layout/spinBoxRemoveOldLabels.value").value<int>());
        ui->spinBoxScrollingTimeRange->setValue(appSettings.value("layout/spinBoxScrollingTimeRange.value").value<int>());
        ui->spinBoxUDPReceivePort->setValue(appSettings.value("layout/spinBoxUDPReceivePort.value").value<int>());
        ui->spinBoxUDPTargetPort->setValue(appSettings.value("layout/spinBoxUDPTargetPort.value").value<int>());
        ui->tabWidgetControlSection->setCurrentIndex(appSettings.value("GUI_Elements/tabWidgetControlSection.currentIndex").value<int>());
        ui->tabWidgetTableView->setCurrentIndex(appSettings.value("GUI_Elements/tabWidgetTableView.currentIndex").value<int>());

        if (ui->lineEditLoadFilePath->text().isEmpty() == false)
        {
            QFile file(ui->lineEditLoadFilePath->text());
            QList<QTime> timeRange = fileReader.getFileTimeRange(&file);
            ui->timeEditMinParsingTime->setTime(timeRange.first());
            ui->timeEditMaxParsingTime->setTime(timeRange.last());
        }

        qDebug() << "Loaded settings";
    }
}

void MainWindow::settingsSaveAll()
{
    QSettings appSettings(QSettings::Format::IniFormat,
                          QSettings::Scope::UserScope,
                          "Qt Vitrea", "Qt Vitrea"); // Settings

    // ----- Info ----- //
    {
        appSettings.setValue("Info/version", VERSION);
        appSettings.setValue("Info/organizationName", appSettings.organizationName());
        appSettings.setValue("Info/applicationName", appSettings.applicationName());
    }
    // ------------------------- //

    // ----- Window size ----- //
    {
        QString windowSize;
        windowSize = QString::number(this->size().width()) + " " + QString::number(this->size().height());
        appSettings.setValue("layout/windowSize.size", windowSize);
    }
    // ------------------------- //

    // ----- splitterSizes ----- //
    {
        QString splitterSizes;

        splitterSizes = QString::number(ui->splitterReceivedData->sizes().first()) + " " + QString::number(ui->splitterReceivedData->sizes().last());
        appSettings.setValue("layout/splitterReceivedData.sizes", splitterSizes);

        splitterSizes = QString::number(ui->splitterGraphTable->sizes().first()) + " " + QString::number(ui->splitterGraphTable->sizes().last());
        appSettings.setValue("layout/splitterGraphTable.sizes", splitterSizes);
    }
    // ------------------------- //

    // ----- GUI Elements ----- //
    {
        appSettings.setValue("data/lineEditCustomParsingRules.text", ui->lineEditCustomParsingRules->text());
        appSettings.setValue("data/lineEditExternalClockLabel.text", ui->lineEditExternalClockLabel->text());
        appSettings.setValue("data/lineEditLoadFilePath.text", ui->lineEditLoadFilePath->text());
        appSettings.setValue("data/lineEditSaveFileName.text", ui->lineEditSaveFileName->text());
        appSettings.setValue("data/lineEditSaveLogPath.text", ui->lineEditSaveLogPath->text());
        appSettings.setValue("data/lineEditUDPTargetIP.text", ui->lineEditUDPTargetIP->text());
        appSettings.setValue("GUI_Elements/checkBoxAppendDate.isChecked", ui->checkBoxAppendDate->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoLogging.isChecked", ui->checkBoxAutoLogging->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoRefresh.isChecked", ui->checkBoxAutoRefresh->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoRescaleY.isChecked", ui->checkBoxAutoRescaleY->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoSaveBuffer.isChecked", ui->checkBoxAutoSaveBuffer->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoScrollLogTable.isChecked", ui->checkBoxAutoScrollLogTable->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoSizeColumnsLogTable.isChecked", ui->checkBoxAutoSizeColumnsLogTable->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxAutoTrack.isChecked", ui->checkBoxAutoTrack->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxDTR.isChecked", ui->checkBoxDTR->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxEnableTracer.isChecked", ui->checkBoxEnableTracer->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxRAMClearChart.isChecked", ui->checkBoxRAMClearChart->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxScrollLogEnableSorting.isChecked", ui->checkBoxScrollLogEnableSorting->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxScrollToButtom.isChecked", ui->checkBoxScrollToButtom->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxSendKey.isChecked", ui->checkBoxSendKey->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxShowControlChars.isChecked", ui->checkBoxShowControlChars->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxSimplifyLog.isChecked", ui->checkBoxSimplifyLog->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxTableAutoResize.isChecked", ui->checkBoxTableAutoResize->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxTruncateFileOnSave.isChecked", ui->checkBoxTruncateFileOnSave->isChecked());
        appSettings.setValue("GUI_Elements/checkBoxWrapText.isChecked", ui->checkBoxWrapText->isChecked());
        appSettings.setValue("GUI_Elements/comboBoxAddTextMode.currentIndex", ui->comboBoxAddTextMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxBaudRates.currentIndex", ui->comboBoxBaudRates->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxClockSource.currentIndex", ui->comboBoxClockSource->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxDataBits.currentIndex", ui->comboBoxDataBits->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxExternalTimeFormat.currentIndex", ui->comboBoxExternalTimeFormat->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxFlowControl.currentIndex", ui->comboBoxFlowControl->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxGraphDisplayMode.currentIndex", ui->comboBoxGraphDisplayMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxLineEnding.currentIndex", ui->comboBoxLineEnding->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxLoggingMode.currentIndex", ui->comboBoxLoggingMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxMessagesDisplayMode.currentIndex", ui->comboBoxMessagesDisplayMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxParity.currentIndex", ui->comboBoxParity->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxRAMLoadMode.currentIndex", ui->comboBoxRAMLoadMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxRAMSaveMode.currentIndex", ui->comboBoxRAMSaveMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxSerialReadMode.currentIndex", ui->comboBoxSerialReadMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxStopBits.currentIndex", ui->comboBoxStopBits->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxTextProcessing.currentIndex", ui->comboBoxTextProcessing->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxTracerStyle.currentIndex", ui->comboBoxTracerStyle->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxUDPReceiveMode.currentIndex", ui->comboBoxUDPReceiveMode->currentIndex());
        appSettings.setValue("GUI_Elements/comboBoxUDPSendMode.currentIndex", ui->comboBoxUDPSendMode->currentIndex());
        appSettings.setValue("GUI_Elements/pushButtonEnablePlot.isChecked", ui->pushButtonEnablePlot->isChecked());
        appSettings.setValue("GUI_Elements/tabWidgetControlSection.currentIndex", ui->tabWidgetControlSection->currentIndex());
        appSettings.setValue("GUI_Elements/tabWidgetTableView.currentIndex", ui->tabWidgetTableView->currentIndex());
        appSettings.setValue("layout/pushButtonEnableTableLog.isChecked", ui->pushButtonEnableTableLog->isChecked());
        appSettings.setValue("layout/spinBoxMaxGraphs.value", ui->spinBoxMaxGraphs->value());
        appSettings.setValue("layout/spinBoxMaxLines.value", ui->spinBoxMaxLines->value());
        appSettings.setValue("layout/spinBoxMaxRowsLogTable.value", ui->spinBoxMaxRowsLogTable->value());
        appSettings.setValue("layout/spinBoxMaxTimeRange.value", ui->spinBoxMaxTimeRange->value());
        appSettings.setValue("layout/spinBoxProcessingDelay.value", ui->spinBoxProcessingDelay->value());
        appSettings.setValue("layout/spinBoxRemoveOldLabels.value", ui->spinBoxRemoveOldLabels->value());
        appSettings.setValue("layout/spinBoxScrollingTimeRange.value", ui->spinBoxScrollingTimeRange->value());
        appSettings.setValue("layout/spinBoxUDPReceivePort.value", ui->spinBoxUDPReceivePort->value());
        appSettings.setValue("layout/spinBoxUDPTargetPort.value", ui->spinBoxUDPTargetPort->value());
    }
    // ------------------------- //

    // ----- comboBoxSendItems ----- //
    {
        QString comboBoxSendItems;
        for (auto i = 0; i < ui->comboBoxSend->count(); ++i)
            comboBoxSendItems.append(ui->comboBoxSend->itemText(i) + "\n");
        qDebug() << comboBoxSendItems;
        appSettings.setValue("data/comboBoxSendHistory", comboBoxSendItems);
    }
    // ------------------------- //

    appSettings.sync();

    qDebug() << "Save all settings";
}

void MainWindow::processTable(QStringList labels, QList<double> values)
{
    static QList<int> tableMissingCount;

    foreach (auto label, labels)
    {
        if (ui->tableWidgetParsedData->findItems(label, Qt::MatchFlag::MatchExactly).count() < 1)
        {
            ui->tableWidgetParsedData->insertRow(ui->tableWidgetParsedData->rowCount());

            QTableWidgetItem *newLabel = new QTableWidgetItem(label);
            ui->tableWidgetParsedData->setItem(ui->tableWidgetParsedData->rowCount() - 1, 0, newLabel);
            tableMissingCount.append(0);
        }

        for (auto i = 0; i < ui->tableWidgetParsedData->rowCount(); ++i)
        {
            if (ui->tableWidgetParsedData->item(i, 0)->text() == label)
            {
                QTableWidgetItem *newValue = new QTableWidgetItem(QString::number(values[labels.indexOf(label)]));
                ui->tableWidgetParsedData->setItem(i, 1, newValue);
                break;
            }
        }

        if (ui->spinBoxRemoveOldLabels->value() > 0)
        {
            for (auto i = 0; i < ui->tableWidgetParsedData->rowCount(); ++i)
            {
                if (ui->tableWidgetParsedData->item(i, 0)->text() == label)
                    tableMissingCount[i] = 0;
                else
                    tableMissingCount[i]++;

                if (tableMissingCount[i] > ui->spinBoxRemoveOldLabels->value())
                {
                    ui->tableWidgetParsedData->removeRow(i);
                    tableMissingCount.removeAt(i);
                }
            }
        }
    }

    if (ui->checkBoxTableAutoResize->isChecked())
    {
        ui->tableWidgetParsedData->resizeColumnsToContents();
        ui->tableWidgetParsedData->resizeRowsToContents();
    }
}

void MainWindow::processLogTable(QList<long> timeTable, QStringList labelTable, QList<double> valueTable)
{
    if (ui->pushButtonEnableTableLog->isChecked())
        return;

    //    static QList<int> tableMissingCount;
    unsigned long oldRowCount = ui->tableWidgetLogTable->rowCount();

    QStringList firstRow;
    for (auto i = 0; i < ui->tableWidgetLogTable->columnCount(); ++i)
        firstRow.append(ui->tableWidgetLogTable->horizontalHeaderItem(i)->text().trimmed());

    bool resizeFlag = false;

    foreach (auto label, labelTable)
    {
        if (firstRow.contains(label) == false) // optimise
        {
            ui->tableWidgetLogTable->setColumnCount(ui->tableWidgetLogTable->columnCount() + 1);
            ui->tableWidgetLogTable->setHorizontalHeaderItem(ui->tableWidgetLogTable->columnCount() - 1, new QTableWidgetItem(label.trimmed()));

            firstRow.clear();
            for (auto i = 0; i < ui->tableWidgetLogTable->columnCount(); ++i)
                firstRow.append(ui->tableWidgetLogTable->horizontalHeaderItem(i)->text().trimmed());

            resizeFlag = true;
        }
        else
        {
            if (oldRowCount == ui->tableWidgetLogTable->rowCount())
                ui->tableWidgetLogTable->setRowCount(oldRowCount + 1);

            ui->tableWidgetLogTable->setItem(oldRowCount, 0, new QTableWidgetItem(QTime::fromMSecsSinceStartOfDay(timeTable[labelTable.indexOf(label)]).toString(parser.searchTimeFormatList[0])));
            ui->tableWidgetLogTable->setItem(oldRowCount, firstRow.indexOf(label), new QTableWidgetItem(QString::number(valueTable[labelTable.indexOf(label)])));
        }
    }

    if (ui->checkBoxAutoScrollLogTable->isChecked())
        ui->tableWidgetLogTable->scrollToBottom();

    if (ui->checkBoxAutoSizeColumnsLogTable->isChecked() && resizeFlag)
        ui->tableWidgetLogTable->resizeColumnsToContents();

    if (ui->spinBoxMaxRowsLogTable->value() > 0)
    {
        while (ui->tableWidgetLogTable->rowCount() > ui->spinBoxMaxRowsLogTable->value())
        {
            ui->tableWidgetLogTable->removeRow(0);
        }
    }
}

void MainWindow::on_printIntroChangelog() // TODO
{
    ui->pushButtonTextLogToggle->setChecked(false);
    ui->textBrowserLogs->appendPlainText(INTRO_TEXT);
    ui->textBrowserLogs->appendPlainText("\n" CHANGELOG_TEXT);

    //    ui->textBrowserLogs->horizontalScrollBar()->setValue(0);
}

void MainWindow::on_comboBoxSendReturnPressedSlot()
{
    sendMessageLineEdit(ui->tabWidgetControlSection->currentIndex());
}

void MainWindow::sendMessageLineEdit(int mode)
{
    if (mode == 0)
        sendSerial(ui->comboBoxSend->currentText());
    else if (mode == 1)
        sendUDPDatagram(ui->comboBoxSend->currentText());
    else
    {
        sendSerial(ui->comboBoxSend->currentText()); // TODO
        sendUDPDatagram(ui->comboBoxSend->currentText());
    }

    if (ui->comboBoxMessagesDisplayMode->currentIndex() == 0)
        addLog("\n >> " + ui->comboBoxSend->currentText(), ui->comboBoxAddTextMode->currentIndex());

    ui->comboBoxSend->setCurrentText("");
    ui->comboBoxSend->model()->sort(0, Qt::SortOrder::AscendingOrder); // sort alphabetically
}

// Auto-scroll X and rescale Y
void MainWindow::on_chartBeforeReplotSlot()
{
    static bool isChartEmpty = false;
    if (ui->widgetChart->graphCount() > 0 && isChartEmpty == true)
    {
        isChartEmpty = false;
        ui->widgetChart->rescaleAxes(); // center plot if new graphs added to empty chart
    }
    else if (ui->widgetChart->graphCount() == 0)
    {
        isChartEmpty = true;
    }

    if (ui->checkBoxAutoTrack->isChecked() && ui->widgetChart->graphCount() > 0)
    {
        if (parser.getListTimeStamp().count() > 0)
        {
            ui->widgetChart->xAxis->setRange((parser.getListTimeStamp().last() / 1000.0) + (ui->spinBoxScrollingTimeRange->value() * 0.05),
                                             ui->spinBoxScrollingTimeRange->value(), Qt::AlignRight);
        }

        if (ui->checkBoxAutoRescaleY->isChecked())
        {
            bool atLeastOneGraphVisible = false, atLeastOneGraphIsNotFlat = false;

            for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
            {
                if (ui->widgetChart->graph(i)->visible())
                {
                    atLeastOneGraphVisible = true;

                    bool foundRange = false;
                    QCPRange valueRange = ui->widgetChart->graph(i)->data().data()->valueRange(foundRange);

                    if (foundRange == true && abs(valueRange.upper - valueRange.lower) >= 0.1)
                        atLeastOneGraphIsNotFlat = true;
                }
            }

            if (atLeastOneGraphVisible && atLeastOneGraphIsNotFlat)
            {
                ui->widgetChart->yAxis->rescale(true);
                ui->widgetChart->yAxis->scaleRange(1.20); // leave some space at the top and buttom of the chart.
            }
        }
    }
    else
    {
        // do nothing ?
    }
}

void MainWindow::on_chartContextMenuRequest(QPoint pos) // right click on chart
{
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);

    if (ui->widgetChart->legend->selectTest(pos, false) >= 0) // context menu on legend requested
    {
        on_clearGraphSelection();

        for (auto i = 0; i < ui->widgetChart->legend->itemCount(); ++i)
        {
            if (ui->widgetChart->legend->item(i)->selectTest(pos, false) >= 0)
            {
                ui->widgetChart->legend->item(i)->setSelected(true);
                menu->addAction("Show excusively", this, SLOT(on_showSelectedGraphExclusively()));

                if (ui->widgetChart->graph(i)->visible())
                    menu->addAction("Hide graph", this, SLOT(on_hideSelectedGraph()));
                else
                    menu->addAction("Show graph", this, SLOT(on_showSelectedGraph()));
            }
        }
    }
    else
    {
        for (auto i = 0; i < ui->widgetChart->legend->itemCount(); ++i) // context menu requested eslewhere on the graph
        {
            if (ui->widgetChart->legend->item(i)->selected())
            {
                menu->addAction("Show excusively", this, SLOT(on_showSelectedGraphExclusively()));

                if (ui->widgetChart->graph(i)->visible())
                    menu->addAction("Hide graph", this, SLOT(on_hideSelectedGraph()));
                else
                    menu->addAction("Show graph", this, SLOT(on_showSelectedGraph()));
            }
        }
    }

    if (ui->widgetChart->graphCount() > 0)
    {
        menu->addAction("Show all graphs", this, SLOT(on_showAllGraphs()));
        menu->addAction("Hide all graphs", this, SLOT(on_hideAllGraphs()));
    }

    menu->popup(ui->widgetChart->mapToGlobal(pos));
    ui->widgetChart->replot();
}

void MainWindow::on_showSelectedGraphExclusively()
{
    on_hideAllGraphs();

    for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        if (ui->widgetChart->graph(i)->selected() || ui->widgetChart->legend->item(i)->selected())
        {
            ui->widgetChart->graph(i)->setVisible(true);
            ui->widgetChart->legend->item(i)->setTextColor(Qt::GlobalColor::black);
            ui->widgetChart->replot();

            return;
        }
    }
}

void MainWindow::on_showAllGraphs()
{
    for (auto i = 0; i < ui->widgetChart->legend->itemCount(); ++i)
    {
        ui->widgetChart->graph(i)->setVisible(true);
        ui->widgetChart->legend->item(i)->setTextColor(Qt::GlobalColor::black);
    }

    ui->widgetChart->replot();
}

void MainWindow::on_clearGraphSelection()
{
    for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        ui->widgetChart->legend->item(i)->setSelected(false);
    }
}

void MainWindow::on_hideSelectedGraph()
{
    for (auto i = 0; i < ui->widgetChart->legend->itemCount(); ++i)
    {
        if (ui->widgetChart->legend->item(i)->selected())
        {
            ui->widgetChart->graph(i)->setVisible(false);
            ui->widgetChart->legend->item(i)->setTextColor(Qt::GlobalColor::gray);
        }
    }

    on_clearGraphSelection();
    ui->widgetChart->replot();
}

void MainWindow::on_showSelectedGraph()
{
    for (auto i = 0; i < ui->widgetChart->legend->itemCount(); ++i)
    {
        if (ui->widgetChart->legend->item(i)->selected())
        {
            ui->widgetChart->graph(i)->setVisible(true);
            ui->widgetChart->legend->item(i)->setTextColor(Qt::GlobalColor::black);
        }
    }

    on_clearGraphSelection();
    ui->widgetChart->replot();
}

void MainWindow::on_hideAllGraphs()
{
    if (ui->widgetChart->graphCount() > 0)
    {
        for (auto i = 0; i < ui->widgetChart->legend->itemCount(); ++i)
        {
            ui->widgetChart->graph(i)->setVisible(false);
            ui->widgetChart->legend->item(i)->setTextColor(Qt::GlobalColor::gray);
        }

        ui->widgetChart->replot();
    }
}

void MainWindow::on_chartMouseDoubleClickHandler(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        ui->widgetChart->rescaleAxes(true);
        ui->widgetChart->yAxis->scaleRange(1.20);
    }
    else if (event->button() == Qt::MiddleButton)
    {
        ui->checkBoxAutoTrack->setChecked(!ui->checkBoxAutoTrack->isChecked());
    }

    ui->widgetChart->replot();
}

void MainWindow::on_chartSelectionChanged()
{
    // synchronize selection of graphs with selection of corresponding legend items:
    for (int i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        QCPGraph *selectedGraph = ui->widgetChart->graph(i);
        QCPPlottableLegendItem *selectedLegendItem = ui->widgetChart->legend->itemWithPlottable(selectedGraph);

        if (selectedLegendItem->selected() || selectedGraph->selected())
        {
            selectedLegendItem->setSelected(true);
            selectedGraph->setSelection(QCPDataSelection(selectedGraph->data()->dataRange()));
            return;
        }
    }
}

void MainWindow::on_tracerShowPointValue(QMouseEvent *event)
{
    if (ui->widgetChart->graphCount() < 1)
        return;

    // Get selected graph (in my case selected means the plot is selected from the legend)
    QCPGraph *graph = nullptr;
    for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        if (ui->widgetChart->graph(i)->selected())
        {
            graph = ui->widgetChart->graph(i);
            break;
        }
    }

    if (graph == nullptr)
        return;

    // Setup the item tracer
    this->phaseTracer->setGraph(graph);
    this->phaseTracer->setGraphKey(ui->widgetChart->xAxis->pixelToCoord(event->pos().x()));
    ui->widgetChart->replot();

    // **********Get the values from the item tracer's coords***********
    QPointF temp = this->phaseTracer->position->coords();

    // Show a tooltip which tells the values
    QToolTip::showText(event->globalPos(),
                       tr("<h4>%L1</h4>"
                          "<table>"
                          "<tr>"
                          "<td>X: %L2</td>"
                          "<td>  ,  </td>"
                          "<td>Y: %L3</td>"
                          "</tr>"
                          "</table>")
                           .arg(graph->name())
                           .arg(QTime::fromMSecsSinceStartOfDay(temp.x() * 1000).toString("hh:mm:ss:zzz"))
                           .arg(QString::number(temp.y(), 'f', 5)),
                       ui->widgetChart, ui->widgetChart->rect());
}

// Activates rect mode on middle button press
void MainWindow::on_chartMousePressHandler(QMouseEvent *event)
{
    if (event->button() == Qt::MiddleButton)
        ui->widgetChart->setSelectionRectMode(QCP::srmZoom);
    else
        ui->widgetChart->setSelectionRectMode(QCP::srmNone);

    ui->widgetChart->replot();
}

void MainWindow::on_chartMouseMoveHandler(QMouseEvent *event)
{
    static int distance = 0, lastPos = 0;

    if (event->buttons() & Qt::LeftButton)
    {
        distance += event->pos().x() - lastPos;
        lastPos = event->pos().x();

        //    qDebug() << "distance: " + QString::number(distance);

        if (abs(distance) > 200)
        {
            ui->checkBoxAutoTrack->setChecked(false);
        }
    }
    else
    {
        distance = 0;
    }
}

void MainWindow::on_updateSerialDeviceList()
{
    QList<QSerialPortInfo> devices = serial.getAvailiblePorts();
    QList<QString> portNames;
    static QList<QString> portNamesOld;

    foreach (auto item, devices)
        portNames.append(item.portName());

    if ((devices.count() >= 1) && (!(portNames.toSet().intersects(portNamesOld.toSet())) || (portNames.count() != portNamesOld.count())))
    {
        ui->comboBoxDevices->clear();

        for (auto i = 0; i < devices.count(); ++i)
        {
            ui->comboBoxDevices->addItem("(" + devices.at(i).portName() + ")" + " " + devices.at(i).description());
        }
    }
    else if ((devices.count() < 1) && !ui->comboBoxDevices->itemText(0).startsWith("No COM devices"))
    {
        ui->comboBoxDevices->clear();
        ui->comboBoxDevices->addItem("No serial devices detected :(");
        ui->comboBoxDevices->setCurrentIndex(ui->comboBoxDevices->count() - 1);
    }

    portNamesOld = portNames;

    this->radioButtonTimer->start(RADIO_BUTTON_UPDATE_SERIAL_DEVICES_ON_INTERVAL);
    ui->radioButtonDeviceUpdate->setChecked(true);
}

void MainWindow::on_pushButtonRefresh_clicked()
{
    qDebug() << "Refreshing serial device list...";
    this->addLog("App >>\t Searching for COM ports...", true);
    this->on_updateSerialDeviceList();
}

void MainWindow::addLog(QString text, bool appendAsLine)
{
    if (ui->pushButtonTextLogToggle->isChecked() == false)
    {
        QString currentDateTime = QDateTime::currentDateTime().toString("hh:mm:ss:zzz ");

        if (ui->checkBoxShowTime->isChecked())
            text = currentDateTime + text;

        // Replace control chars
        if (ui->checkBoxShowControlChars->isChecked())
            text = controlCharactersVisibleConvert(text);

        if (!appendAsLine)
        {
            int sliderPosVertical = ui->textBrowserLogs->verticalScrollBar()->value();
            int sliderPosHorizontal = ui->textBrowserLogs->horizontalScrollBar()->value();

            ui->textBrowserLogs->moveCursor(QTextCursor::MoveOperation::End, QTextCursor::MoveMode::MoveAnchor);
            ui->textBrowserLogs->insertPlainText(text);

            ui->textBrowserLogs->horizontalScrollBar()->setValue(sliderPosHorizontal);

            if (!ui->checkBoxScrollToButtom->isChecked())
                ui->textBrowserLogs->verticalScrollBar()->setValue(sliderPosVertical);
            else
                ui->textBrowserLogs->verticalScrollBar()->setValue(ui->textBrowserLogs->verticalScrollBar()->maximum());
        }
        else
        {
            ui->textBrowserLogs->appendPlainText(text);
        }
    }
}

QString MainWindow::controlCharactersVisibleConvert(QString text)
{
    if (text.replace("\r\n", "\\r\\n\r\n").isEmpty())
    {
        text.replace("\r", "\\r\r");
        text.replace("\n", "\\n\n");
    }

    text.replace("\t", "\\t\t");
    text.replace(' ', QChar(183));

    return text;
}

void MainWindow::addLogBytes(QByteArray bytes, bool hexToBinary, bool appendAsLine)
{
    if (ui->pushButtonTextLogToggle->isChecked() == false)
    {
        QString currentDateTime = QDateTime::currentDateTime().toString("hh:mm:ss:zzz ");

        QString bytesText;

        if (hexToBinary == false)
            bytesText = bytes.toHex(' ');
        else
        {
            for (auto i = 0; i < bytes.size(); ++i)
            {
                bytesText.append(QString::number(bytes[i], 2) + ' ');
            }
        }

        if (ui->checkBoxShowTime->isChecked())
            bytesText = currentDateTime + bytesText;

        if (!appendAsLine)
        {
            int sliderPosVertical = ui->textBrowserLogs->verticalScrollBar()->value();
            int sliderPosHorizontal = ui->textBrowserLogs->horizontalScrollBar()->value();

            ui->textBrowserLogs->moveCursor(QTextCursor::MoveOperation::End, QTextCursor::MoveMode::MoveAnchor);
            ui->textBrowserLogs->insertPlainText(bytesText);

            ui->textBrowserLogs->horizontalScrollBar()->setValue(sliderPosHorizontal);

            if (!ui->checkBoxScrollToButtom->isChecked())
                ui->textBrowserLogs->verticalScrollBar()->setValue(sliderPosVertical);
            else
                ui->textBrowserLogs->verticalScrollBar()->setValue(ui->textBrowserLogs->verticalScrollBar()->maximum());
        }
        else
        {
            ui->textBrowserLogs->appendPlainText(bytesText);
        }
    }
}

void MainWindow::processLogWrite(QString rawLine, QStringList labelList, QList<double> dataList, QList<long> timeList)
{
    if (ui->pushButtonLogging->isChecked()) // Write log into file
    {
        if (ui->comboBoxLogFormat->currentIndex() == 1)
        {
            if (ui->comboBoxLoggingMode->currentIndex() == 0)
                fileLogger.writeLogTXTLine(rawLine, ui->checkBoxSimplifyLog->isChecked());
            else if (ui->comboBoxLoggingMode->currentIndex() == 1)
                fileLogger.writeLogTXTParsedData(labelList, dataList);
        }
        else if (ui->comboBoxLogFormat->currentIndex() == 0)
        {
            fileLogger.writeLogCSV(labelList, dataList);
        }
    }
}

void MainWindow::on_processSerial()
{
    QString serialInput = serial.getString();
    serial.clearAll();

    if (ui->comboBoxTextProcessing->currentIndex() == 1) // Append text to textBrowser
        serialInput = serialInput.trimmed();
    else if (ui->comboBoxTextProcessing->currentIndex() == 2)
        serialInput = serialInput.simplified();

    if (ui->comboBoxFormat->currentIndex() == 0 && serialInput.isEmpty() == false)
    {
        addLog(serialInput, ui->comboBoxAddTextMode->currentIndex());
    }
    else if (ui->comboBoxFormat->currentIndex() == 1 && serialInput.length() > 0)
    {
        addLogBytes(serialInput.toUtf8(), false, ui->comboBoxAddTextMode->currentIndex());
    }
    else if (ui->comboBoxFormat->currentIndex() == 2 && serialInput.length() > 0)
    {
        addLogBytes(serialInput.toUtf8(), true, ui->comboBoxAddTextMode->currentIndex());
    }

    if (serialInput.isEmpty() == false)
    {
        parser.parse(serialInput, ui->comboBoxClockSource->currentIndex() == 0, ui->comboBoxClockSource->currentIndex() == 1, ui->lineEditExternalClockLabel->text()); // Parse string - split into labels + numeric data
        QStringList labelList = parser.getStringListLabels();
        QList<double> numericDataList = parser.getListNumericValues();
        QList<long> timeStamps = parser.getListTimeStamp();

        this->processChart(labelList, numericDataList, timeStamps);
        this->processLogWrite(serialInput, labelList, numericDataList, timeStamps);
        this->saveToRAM(labelList, numericDataList, timeStamps, ui->comboBoxRAMSaveMode->currentIndex(), serialInput);
        this->processTable(labelList, numericDataList);
        this->processLogTable(timeStamps, labelList, numericDataList);

        //        QFuture<void> future = QtConcurrent::run([=]() // Cool !
        //        {

        //        });
    }
}

void MainWindow::clearGraphData(bool replot)
{
    qDebug() << "clear graph data!";
    for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        ui->widgetChart->graph(i)->data().data()->clear();
    }

    if (replot)
        ui->widgetChart->replot();
}

void MainWindow::on_processUDP()
{
    QString udpInput = networkUDP.readString();
    networkUDP.clearAll();

    if (ui->comboBoxTextProcessing->currentIndex() == 1)
        udpInput = udpInput.trimmed();
    else if (ui->comboBoxTextProcessing->currentIndex() == 2)
        udpInput = udpInput.simplified();

    if (ui->comboBoxFormat->currentIndex() == 0 && udpInput.isEmpty() == false)
    {
        addLog(udpInput);
    }
    else if (ui->comboBoxFormat->currentIndex() == 1 && udpInput.length() > 0)
    {
        addLogBytes(udpInput.toUtf8(), false, ui->comboBoxAddTextMode->currentIndex());
    }
    else if (ui->comboBoxFormat->currentIndex() == 2 && udpInput.length() > 0)
    {
        addLogBytes(udpInput.toUtf8(), true, ui->comboBoxAddTextMode->currentIndex());
    }

    if (udpInput.isEmpty() == false)
    {
        parser.parse(udpInput, ui->comboBoxClockSource->currentIndex() == 0, ui->comboBoxClockSource->currentIndex() == 1, ui->lineEditExternalClockLabel->text()); // Parse string - split into labels + numeric data
        QStringList labelList = parser.getStringListLabels();
        QList<double> numericDataList = parser.getListNumericValues();
        QList<long> timeStamps = parser.getListTimeStamp();

        this->processLogWrite(udpInput, labelList, numericDataList, timeStamps);
        this->processChart(labelList, numericDataList, timeStamps);
        this->saveToRAM(labelList, numericDataList, timeStamps, ui->comboBoxRAMSaveMode->currentIndex(), udpInput);
        this->processTable(labelList, numericDataList); // Fill tableWidget
        this->processLogTable(timeStamps, labelList, numericDataList);
    }
}

void MainWindow::sendUDPDatagram(QString message)
{
    if (!networkUDP.isOpen())
    {
        addLog("App >>\t Unable to send - port closed.", true);
        return;
    }

    if (ui->comboBoxLineEnding->currentIndex() == 1)
    {
        message = message + "\n";
    }
    else if (ui->comboBoxLineEnding->currentIndex() == 2)
    {
        message = message + "\r";
    }
    else if (ui->comboBoxLineEnding->currentIndex() == 3)
    {
        message = message + "\r\n";
    }

    if (ui->comboBoxUDPSendMode->currentText().contains("Broadcast", Qt::CaseSensitivity::CaseInsensitive))
    {
        networkUDP.write(message, QHostAddress::Broadcast, ui->spinBoxUDPTargetPort->value());
    }
    else if (ui->comboBoxUDPSendMode->currentText().contains("LocalHost", Qt::CaseSensitivity::CaseInsensitive))
    {
        networkUDP.write(message, QHostAddress::LocalHost, ui->spinBoxUDPTargetPort->value());
    }
    else
    {
        networkUDP.write(message, QHostAddress(ui->lineEditUDPTargetIP->text()), ui->spinBoxUDPTargetPort->value());
    }

    // addLog("UDP >>\t" + message);
}

void MainWindow::processChart(QStringList labelList, QList<double> numericDataList, QList<long> timeStampsList)
{
    if (timeStampsList.count() == 0 || labelList.count() == 0 || numericDataList.count() == 0 || ui->pushButtonEnablePlot->isChecked())
        return;

    // Execute plotter

    foreach (auto label, labelList)
    {
        bool canAddGraph = true;
        if (ui->widgetChart->graphCount() > 0)
        {
            for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
            {
                if (ui->widgetChart->graph(i)->name() == label)
                {
                    canAddGraph = false;
                    break;
                }
            }
        }

        if (canAddGraph && ui->widgetChart->graphCount() < ui->spinBoxMaxGraphs->value() &&

            ((ui->comboBoxGraphDisplayMode->currentIndex() == 0) ||

             (ui->comboBoxGraphDisplayMode->currentIndex() == 1 &&
              ui->lineEditCustomParsingRules->text().simplified().contains(label, Qt::CaseSensitivity::CaseSensitive)) ||

             (ui->comboBoxGraphDisplayMode->currentIndex() == 2 &&
              !ui->lineEditCustomParsingRules->text().simplified().contains(label, Qt::CaseSensitivity::CaseSensitive))))
        {
            ui->widgetChart->addGraph();
            ui->widgetChart->graph()->setName(label);

            ui->widgetChart->graph()->selectionDecorator()->setPen(QPen(Qt::black, 1.0, Qt::PenStyle::DashLine));
            while (ui->widgetChart->graph()->lineStyle() == (QCPGraph::LineStyle::lsImpulse))  //
                ui->widgetChart->graph()->setLineStyle((QCPGraph::LineStyle)(rand() % 5 + 1)); // random line except for impulse
            while (ui->widgetChart->graph()->scatterStyle().shape() == QCPScatterStyle::ScatterShape::ssNone)
                ui->widgetChart->graph()->setScatterStyle(QCPScatterStyle((QCPScatterStyle::ScatterShape)(rand() % 14 + 1))); // random scatter

            int hue = 0; // start with red
            for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
            {
                ui->widgetChart->graph(i)->setPen(QPen(QColor::fromHsv(hue, 255, 200))); // random color
                hue += 360 / ui->widgetChart->graphCount();
            }

            missingCount.append(0); // Add missing counter to the list (corresponds to each graph index)
        }
    }

    for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        for (auto j = 0; j < labelList.count(); ++j)
        {
            if (labelList[j] == ui->widgetChart->graph(i)->name()) // If label matches the graphs name - we can start add points to it
            {
                if (timeStampsList[j] > 0) // if == 0 then we assume that no external clock was parsed propely
                    ui->widgetChart->graph(i)->addData(timeStampsList[j] / 1000.0, numericDataList[j]);

                // break; // no break - slower but if we keep checking for duplicates, we can add multiple points at once
            }
        }

        if (ui->spinBoxMaxTimeRange->value() > 0)
            ui->widgetChart->graph(i)->data().data()->removeBefore((timeStampsList.last() / 1000.0) - ui->spinBoxMaxTimeRange->value()); // Remove old points

        if (ui->spinBoxRemoveOldLabels->value() > 0)
        {
            if (labelList.contains(ui->widgetChart->graph(i)->name()))
                missingCount[i] = 0;
            else
                missingCount[i]++;

            if (missingCount[i] > ui->spinBoxRemoveOldLabels->value())
            {
                ui->widgetChart->removeGraph(i);
                missingCount.removeAt(i); // Remove entry for this label
            }
        }
    }

    ui->widgetChart->replot();
}

void MainWindow::on_setSelectedLabels(QList<QString> *labels, bool customRules)
{
    qDebug() << "on_setSelectedLabels";

    if (!customRules)
    {
        if (labels->count() < 1)
        {
            on_showAllGraphs();
            ui->lineEditCustomParsingRules->clear();
        }
        else
        {
            on_hideAllGraphs();

            if (ui->widgetChart->graphCount() > 0)
            {
                for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
                {
                    if (labels->contains(ui->widgetChart->graph(i)->name()))
                    {
                        ui->widgetChart->legend->item(i)->setSelected(true);
                        on_showSelectedGraph();
                    }
                }
            }
        }
    }
    else
    {
        ui->comboBoxGraphDisplayMode->setCurrentIndex(1);
    }

    QString labelsToAppend;
    foreach (QString item, *labels)
        labelsToAppend.append(item + " ");

    ui->lineEditCustomParsingRules->setText(labelsToAppend);
    emit on_lineEditCustomParsingRules_editingFinished();
}

void MainWindow::clearGraphs(bool replot)
{
    ui->widgetChart->clearGraphs();

    if (replot)
        ui->widgetChart->replot();
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (ui->textBrowserLogs->hasFocus())
    {
        if (event->key() == Qt::Key_Alt)
        {
            qDebug() << "Scrolling to buttom !" + QString::number(event->key());

            ui->textBrowserLogs->verticalScrollBar()->setValue(ui->textBrowserLogs->verticalScrollBar()->maximum());
        }
        else if (ui->checkBoxSendKey->isChecked())
        {
            sendMessageKeyEvent(event);

            if (ui->comboBoxMessagesDisplayMode->currentIndex() == 0)
                addLog(">>\n\t" + event->text(), ui->comboBoxAddTextMode->currentIndex());
        }
    }
    else if (ui->widgetChart->hasFocus())
    {
        if (event->key() == Qt::Key_T)
        {
            ui->checkBoxEnableTracer->toggle();
        }
        else if (event->key() == Qt::Key_X)
        {
            ui->checkBoxAutoTrack->toggle();
        }
        else if (event->key() == Qt::Key_Y)
        {
            ui->checkBoxAutoRescaleY->toggle();
        }
        else if (event->key() == Qt::Key_L)
        {
            ui->checkBoxShowLegend->toggle();
        }
    }

    if (event->key() == Qt::Key::Key_F1)
    {
        QWhatsThis::enterWhatsThisMode();
    }
}

void MainWindow::sendMessageKeyEvent(QKeyEvent *event)
{
    if (ui->tabWidgetControlSection->currentIndex() == 0)
        sendSerial(event->text());
    else if (ui->tabWidgetControlSection->currentIndex() == 1)
        sendUDPDatagram(event->text());
    else
    {
        sendSerial(event->text());
        sendUDPDatagram(event->text());
    }
}

void MainWindow::sendSerial(QString message) // TODO - move to serial - error via signal/slot
{
    if (ui->comboBoxLineEnding->currentIndex() == 1)
    {
        message = message + "\n";
    }
    else if (ui->comboBoxLineEnding->currentIndex() == 2)
    {
        message = message + "\r";
    }
    else if (ui->comboBoxLineEnding->currentIndex() == 3)
    {
        message = message + "\r\n";
    }

    if (!serial.send(message))
        this->addLog("App >>\t Unable to send! Serial port closed !", true);
}

void MainWindow::saveToRAM(QStringList newlabelList, QList<double> newDataList, QList<long> newTimeList, bool saveText, QString text)
{
    if (ui->checkBoxAutoSaveBuffer->isChecked() == false)
        return;

    if (text.isEmpty() == false && saveText == true)
        parser.appendSetToMemory(newlabelList, newDataList, newTimeList, text);
    else
        parser.appendSetToMemory(newlabelList, newDataList, newTimeList);
}

void MainWindow::loadFromRAM(bool loadText)
{
    QStringList RAMLabels = parser.getLabelStorage();
    QList<double> RAMData = parser.getDataStorage();
    QList<long> RAMTime = parser.getTimeStorage();

    if (loadText)
    {
        QStringList RAMText = parser.getTextList();
        foreach (auto line, RAMText)
            addLog("Mem >>\t" + line);
    }

    if (RAMLabels.isEmpty() || RAMData.isEmpty() || RAMTime.isEmpty())
        return;

    this->processChart(RAMLabels, RAMData, RAMTime);
    this->processTable(RAMLabels, RAMData);
    this->processLogTable(RAMTime, RAMLabels, RAMData);
}

void MainWindow::on_checkBoxAutoRefresh_toggled(bool checked)
{
    if (checked == true)
    {
        ui->pushButtonRefresh->setEnabled(false);
        serialDeviceCheckTimer->start(SERIAL_DEVICE_CHECK_TIMER_INTERVAL);
    }
    else
    {
        ui->pushButtonRefresh->setEnabled(true);
        serialDeviceCheckTimer->stop();
    }
}

void MainWindow::on_pushButtonClear_clicked()
{
    ui->textBrowserLogs->clear();
}

void MainWindow::on_lineEditHighlight_returnPressed()
{
    on_highlighLog(ui->lineEditHighlight->text());
}

void MainWindow::on_highlighLog(QString searchString)
{
    QTextDocument *document = ui->textBrowserLogs->document();
    QTextCursor highlightCursor(document);

    // clear formatting !
    highlightCursor.setPosition(document->characterCount() - 1, QTextCursor::KeepAnchor);
    highlightCursor.mergeCharFormat(QTextCharFormat());
    highlightCursor.setPosition(0, QTextCursor::MoveAnchor);

    if (!searchString.isEmpty())
    {
        QTextCharFormat colorFormat = highlightCursor.charFormat();

        colorFormat.setFontLetterSpacing(110);
        colorFormat.setForeground(Qt::black);
        colorFormat.setFontWeight(QFont::Bold);
        colorFormat.setBackground(Qt::gray);

        while (!highlightCursor.isNull() && !highlightCursor.atEnd())
        {
            highlightCursor = document->find(searchString, highlightCursor);

            if (!highlightCursor.isNull())
            {
                highlightCursor.movePosition(QTextCursor::NextCell, QTextCursor::KeepAnchor);
                highlightCursor.mergeCharFormat(colorFormat);
            }
        }
    }
}

void MainWindow::on_checkBoxWrapText_toggled(bool checked)
{
    if (checked)
        ui->textBrowserLogs->setLineWrapMode(QPlainTextEdit::LineWrapMode::WidgetWidth);
    else
        ui->textBrowserLogs->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap);
}

void MainWindow::on_checkBoxEnableTracer_toggled(bool checked)
{
    if (checked)
    {
        createChartTracer();
        connect(ui->widgetChart, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(on_tracerShowPointValue(QMouseEvent *)));
    }
    else
    {
        disconnect(ui->widgetChart, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(on_tracerShowPointValue(QMouseEvent *)));
        delete phaseTracer;
        phaseTracer = nullptr;
    }

    ui->widgetChart->replot();
}

void MainWindow::createChartTracer()
{
    // add the phase tracer (red circle) which sticks to the graph data:
    this->phaseTracer = new QCPItemTracer(ui->widgetChart);
    this->phaseTracer->setInterpolating(false);

    if (ui->comboBoxTracerStyle->currentText().contains("Crosshair", Qt::CaseSensitivity::CaseInsensitive))
        this->phaseTracer->setStyle(QCPItemTracer::tsCrosshair);
    else
        this->phaseTracer->setStyle(QCPItemTracer::tsCircle);

    this->phaseTracer->setPen(QPen(Qt::red));
    this->phaseTracer->setBrush(Qt::red);
    this->phaseTracer->setSize(8);
}

void MainWindow::on_pushButtonClearAll_clicked()
{
    ui->widgetChart->xAxis->setRange(-1.0, ui->spinBoxScrollingTimeRange->value());
    ui->widgetChart->yAxis->setRange(-1.0, 1.0);

    parser.restartChartTimer();
    parser.clearExternalClock();
    clearGraphs(true);

    ui->tableWidgetParsedData->clearContents();
    ui->tableWidgetParsedData->setRowCount(0);
    ui->tableWidgetLogTable->setRowCount(0);
    ui->tableWidgetLogTable->setColumnCount(1);

    if (ui->checkBoxRAMClearChart->isChecked())
        parser.clearStorage();
}

void MainWindow::on_pushButtonEnablePlot_toggled(bool checked)
{
    if (checked)
        ui->pushButtonEnablePlot->setText("Enable");
    else
        ui->pushButtonEnablePlot->setText("Disable");
}

void MainWindow::on_pushButtonSend_clicked()
{
    if (ui->comboBoxSend->findText(ui->comboBoxSend->currentText()) < 0)
        ui->comboBoxSend->addItem(ui->comboBoxSend->currentText()); // add to history (here we have to do it manually)

    on_comboBoxSendReturnPressedSlot();
}

void MainWindow::on_pushButtonClearHistory_clicked()
{
    ui->comboBoxSend->clear();
}

void MainWindow::on_pushButtonClearGraphs_clicked()
{
    clearGraphData(true);
}

void MainWindow::on_lineEditCustomParsingRules_editingFinished()
{
    ui->lineEditCustomParsingRules->setText(ui->lineEditCustomParsingRules->text().replace(" || ", " "));
    ui->lineEditCustomParsingRules->setText(ui->lineEditCustomParsingRules->text().simplified().replace(" ", " || "));
    clearGraphs(true);
    loadFromRAM(false);
}

void MainWindow::on_spinBoxMaxGraphs_valueChanged(int arg1)
{
    QPalette *paletteRed = new QPalette();
    paletteRed->setColor(QPalette::Text, Qt::GlobalColor::red);

    if (arg1 > 10)
        ui->spinBoxMaxGraphs->setPalette(*paletteRed);
    else
        ui->spinBoxMaxGraphs->setPalette(QPalette());

    this->clearGraphs(false);
    this->loadFromRAM(false);

    ui->widgetChart->replot();
}

void MainWindow::on_spinBoxMaxLines_valueChanged(int arg1)
{
    ui->textBrowserLogs->document()->setMaximumBlockCount(arg1);
}

void MainWindow::on_checkBoxAutoTrack_toggled(bool checked)
{
    if (checked)
    {
        ui->spinBoxScrollingTimeRange->setEnabled(true);
        ui->checkBoxAutoRescaleY->setEnabled(true);
    }
    else
    {
        ui->spinBoxScrollingTimeRange->setEnabled(false);
        ui->checkBoxAutoRescaleY->setEnabled(false);
    }
}

void MainWindow::on_spinBoxProcessingDelay_valueChanged(int arg1)
{
    int newInterval = qMax(1, arg1); // Never 0 ! Couses CPU run like crazy !

    serialStringProcessingTimer->setInterval(newInterval);
    udpStringProcessingTimer->setInterval(newInterval);

    QPalette *paletteRed = new QPalette();
    paletteRed->setColor(QPalette::Text, Qt::GlobalColor::red);

    if (newInterval > ui->spinBoxProcessingDelay->minimum())
        ui->spinBoxProcessingDelay->setPalette(*paletteRed);
    else
        ui->spinBoxProcessingDelay->setPalette(QPalette());
}

void MainWindow::on_comboBoxTracerStyle_currentIndexChanged(const QString &arg1)
{
    if (ui->checkBoxEnableTracer->isChecked())
    {
        if (phaseTracer != nullptr)
        {
            disconnect(ui->widgetChart, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(on_tracerShowPointValue(QMouseEvent *)));
            delete phaseTracer;
            phaseTracer = nullptr;
        }

        createChartTracer();
        connect(ui->widgetChart, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(on_tracerShowPointValue(QMouseEvent *)));

        ui->widgetChart->replot();
    }
}

void MainWindow::on_pushButtonSerialConnect_toggled(bool checked)
{
    bool inputError = false;

    if (checked)
    {
        if (serial.getAvailiblePorts().count() < 1)
        {
            addLog("App >>\t No devices available", true);
            addLog("App >>\t Unable to open serial port!", true);
            ui->pushButtonSerialConnect->setChecked(false);
            inputError = true;
        }

        if (ui->comboBoxBaudRates->currentText().toInt() == 0)
        {
            addLog("App >>\t Invalid baud rate!", true);
            inputError = true;
        }

        if (inputError)
            return;

        QString parsedPortName = QSerialPortInfo::availablePorts().at(ui->comboBoxDevices->currentIndex()).portName();
        uint parsedBaudRate = ui->comboBoxBaudRates->currentText().toInt();
        QString dataBits = ui->comboBoxDataBits->currentText();
        QString stopBits = ui->comboBoxStopBits->currentText();
        QString parity = ui->comboBoxParity->currentText();
        QString flowControl = ui->comboBoxFlowControl->currentText();

        if (serial.begin(parsedPortName, parsedBaudRate, dataBits, parity, stopBits, flowControl, ui->checkBoxDTR->isChecked()))
        {
            serialStringProcessingTimer->start(ui->spinBoxProcessingDelay->value());
            parser.restartChartTimer();

            if (ui->pushButtonLogging->isChecked() && fileLogger.isOpen() == false) // logger on standby ?
                fileLogger.beginLog(ui->lineEditSaveLogPath->text(), ui->checkBoxAutoLogging->isChecked(), ui->lineEditSaveFileName->text(), ui->checkBoxTruncateFileOnSave->isChecked());

            connect(serialStringProcessingTimer, SIGNAL(timeout()), this, SLOT(on_processSerial()));

            addLog("App >>\t Serial port opened. " + serial.getSerialInfo() + " DTR: " + QString::number(ui->checkBoxDTR->isChecked()), true);
            ui->pushButtonSerialConnect->setText("Disconnect");
        }
        else
        {
            ui->pushButtonSerialConnect->setChecked(false);
            addLog("App >>\t Unable to open serial port!", true);
        }
    }
    else
    {
        serialStringProcessingTimer->stop();

        disconnect(serialStringProcessingTimer, SIGNAL(timeout()), this, SLOT(on_processSerial()));

        if (serial.end())
        {
            addLog("App >>\t Connection closed.", true);
            ui->pushButtonSerialConnect->setText("Connect");
        }
        else
        {
            ui->pushButtonSerialConnect->setChecked(true);
            addLog("App >>\t ERROR: Unable to close cennection !", true);
        }

        if (!ui->pushButtonSerialConnect->isChecked() && !ui->pushButtonUDPConnect->isChecked())
        {
            fileLogger.closeFile();
        }
    }
}

void MainWindow::on_actionSave_As_triggered()
{
    qDebug() << "Click save file !";
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save Log"), "", tr("(*.txt);;All Files (*)"));

    if (fileName.isEmpty())
    {
        addLog("App >>\t Saving file aborted - filename not specified.", true);
        return;
    }
    else
    {
        QFile file(fileName);
        if (!file.open(QIODevice::WriteOnly))
        {
            QMessageBox::information(this, tr("Unable to open file"), file.errorString());
            return;
        }

        QTextStream out(&file);
        out << ui->textBrowserLogs->toPlainText();
    }
}

void MainWindow::on_actionFull_chart_triggered()
{
    ui->splitterReceivedData->setSizes(QList<int>({0, this->width()}));
}

void MainWindow::on_actionFull_text_view_triggered()
{
    ui->splitterReceivedData->setSizes(QList<int>({this->width(), 0}));
}

void MainWindow::on_action50_50_view_triggered()
{
    ui->splitterReceivedData->setSizes(QList<int>({this->width() / 2, this->width() / 2}));
}

void MainWindow::on_actionWhat_s_new_triggered()
{
    on_printIntroChangelog();
}

void MainWindow::on_actionQuit_triggered()
{
    this->close();
}

void MainWindow::on_actionPlotter_triggered()
{
    ui->stackedWidgetGraphView->setCurrentIndex(0);
}

void MainWindow::on_action3D_orientation_triggered()
{
    //  ui->stackedWidget->setCurrentIndex(1); // WIP
}

void MainWindow::on_printPlot(QPrinter *printer)
{
    printer->setPageSize(QPrinter::PageSize::A4);
    QCPPainter painter(printer);
    QRectF pageRect = printer->pageRect(QPrinter::DevicePixel);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, true);
    int plotWidth = ui->widgetChart->viewport().width(), plotHeight = ui->widgetChart->viewport().height();
    double scale = pageRect.width() / (float)plotWidth;

    painter.setMode(QCPPainter::pmNoCaching);
    painter.scale(scale, scale);

    ui->widgetChart->toPainter(&painter, plotWidth, plotHeight);
}

void MainWindow::on_printLog(QPrinter *printer)
{
    ui->textBrowserLogs->print(printer);
}

void MainWindow::on_checkBoxShowLegend_toggled(bool checked)
{
    ui->widgetChart->legend->setVisible(checked);
    ui->widgetChart->replot();
}

void MainWindow::on_actionSave_graph_as_triggered()
{
}

void MainWindow::on_actionPrint_Graph_triggered()
{
    QPrinter chartPrinter;
    chartPrinter.setFullPage(true);

    QPrintPreviewDialog previewDialog(&chartPrinter);
    previewDialog.setWindowFlags(Qt::Window);

    connect(&previewDialog, SIGNAL(paintRequested(QPrinter *)), SLOT(on_printPlot(QPrinter *)));
    previewDialog.exec();
}

void MainWindow::on_actionPrint_log_triggered()
{
    QPrinter logPrinter;

    QPrintPreviewDialog previewLogDialog(&logPrinter);
    previewLogDialog.setWindowFlags(Qt::Window);

    connect(&previewLogDialog, SIGNAL(paintRequested(QPrinter *)), SLOT(on_printLog(QPrinter *)));
    previewLogDialog.exec();
}

void MainWindow::on_pushButtonUDPConnect_toggled(bool checked)
{
    if (checked)
    {
        bool status = false;
        if (ui->comboBoxUDPReceiveMode->currentText().contains("LocalHost"))
        {
            status = networkUDP.begin(QHostAddress::LocalHost, ui->spinBoxUDPReceivePort->value());
        }
        else if (ui->comboBoxUDPReceiveMode->currentText().contains("Any"))
        {
            status = networkUDP.begin(QHostAddress::Any, ui->spinBoxUDPReceivePort->value());
        }
        else if (ui->comboBoxUDPReceiveMode->currentText().contains("SpecialAddress"))
        {
            status = networkUDP.begin(QHostAddress(ui->lineEditUDPTargetIP->text()), ui->spinBoxUDPReceivePort->value());
        }

        if (status == true)
        {
            udpStringProcessingTimer->start(ui->spinBoxProcessingDelay->value());
            parser.restartChartTimer();

            if (ui->pushButtonLogging->isChecked() && fileLogger.isOpen() == false)
                fileLogger.beginLog(ui->lineEditSaveLogPath->text(), ui->checkBoxAutoLogging->isChecked(), ui->lineEditSaveFileName->text(), ui->checkBoxTruncateFileOnSave->isChecked());

            connect(udpStringProcessingTimer, SIGNAL(timeout()), this, SLOT(on_processUDP()));

            addLog("App >>\t UDP port opened.", true);

            ui->pushButtonUDPConnect->setText("Close Connection");
        }
        else
        {
            addLog("App >>\t UDP error. Unable to bind", true);
        }
    }
    else
    {
        if (networkUDP.end())
        {
            udpStringProcessingTimer->stop();

            addLog("App >>\t UDP port closed.", true);

            disconnect(udpStringProcessingTimer, SIGNAL(timeout()), this, SLOT(on_processUDP()));

            if (!ui->pushButtonSerialConnect->isChecked() && !ui->pushButtonUDPConnect->isChecked())
                fileLogger.closeFile();

            ui->pushButtonUDPConnect->setText("Open Connection");
        }
    }
}

void MainWindow::on_comboBoxUDPSendMode_currentIndexChanged(const QString &arg1)
{
    if (arg1.contains("SpecialAddress") || ui->comboBoxUDPReceiveMode->currentText().contains("SpecialAddress"))
        ui->lineEditUDPTargetIP->setEnabled(true);
    else
        ui->lineEditUDPTargetIP->setEnabled(false);
}

void MainWindow::on_comboBoxUDPReceiveMode_currentIndexChanged(const QString &arg1)
{
    if (ui->pushButtonUDPConnect->isChecked() == true)
    {
        if (ui->comboBoxUDPReceiveMode->currentText().contains("LocalHost"))
        {
            networkUDP.bind(QHostAddress::LocalHost, ui->spinBoxUDPReceivePort->value());
        }
        else if (ui->comboBoxUDPReceiveMode->currentText().contains("Any"))
        {
            networkUDP.bind(QHostAddress::Any, ui->spinBoxUDPReceivePort->value());
        }
        else if (ui->comboBoxUDPReceiveMode->currentText().contains("SpecialAddress"))
        {
            networkUDP.bind(QHostAddress(ui->lineEditUDPTargetIP->text()), ui->spinBoxUDPReceivePort->value());
        }
    }

    if (arg1.contains("SpecialAddress") || ui->comboBoxUDPSendMode->currentText().contains("SpecialAddress"))
        ui->lineEditUDPTargetIP->setEnabled(true);
    else
        ui->lineEditUDPTargetIP->setEnabled(false);
}

void MainWindow::on_pushButtonLogging_toggled(bool checked)
{
    if (checked)
    {
        if (ui->checkBoxAutoLogging->isChecked() == false && ui->lineEditSaveFileName->text().isEmpty())
        {
            addLog("App >>\t logger error - filename not specified !", true);
            ui->pushButtonLogging->setChecked(false);
            return;
        }

        if (ui->pushButtonSerialConnect->isChecked() || ui->pushButtonUDPConnect->isChecked())
        {
            if (!fileLogger.beginLog(ui->lineEditSaveLogPath->text(), ui->checkBoxAutoLogging->isChecked(), ui->lineEditSaveFileName->text()), ui->checkBoxTruncateFileOnSave->isChecked())
            {
                addLog("App >>\t logger error - unable to open File", true);
                ui->pushButtonLogging->setChecked(false);
                return;
            }
        }

        ui->comboBoxLogFormat->setEnabled(false);
        ui->pushButtonLogging->setText("Disable Logging");
    }
    else
    {
        ui->comboBoxLogFormat->setEnabled(true);
        ui->pushButtonLogging->setText("Enable Logging");
        fileLogger.closeFile();
    }
}

void MainWindow::on_checkBoxAutoLogging_toggled(bool checked)
{
    if (checked)
    {
        ui->pushButtonLogging->setEnabled(false);
        ui->lineEditSaveFileName->setEnabled(false);
        ui->pushButtonAddDateTime->setEnabled(false);
        ui->comboBoxLogFormat->setEnabled(false);

        ui->lineEditSaveFileName->setText("%DateTime%_Log" + ui->comboBoxLogFormat->currentText());

        ui->pushButtonLogging->setChecked(true); // Trigger begin log
        ui->pushButtonLogging->setText("Auto");
    }
    else
    {
        ui->pushButtonLogging->setEnabled(true);
        ui->lineEditSaveFileName->setEnabled(true);
        ui->pushButtonAddDateTime->setEnabled(true);

        ui->pushButtonLogging->setChecked(false);
        ui->pushButtonLogging->setText("Enable Logging");

        ui->lineEditSaveFileName->clear();
        ui->comboBoxLogFormat->setEnabled(true);
    }
}

void MainWindow::on_pushButtonSetDefaultLogPath_clicked()
{
    ui->lineEditSaveLogPath->setText(qApp->applicationDirPath() + "/Logs");
}

void MainWindow::on_pushButtonAddDateTime_clicked()
{
    ui->lineEditSaveFileName->setText(QDateTime::currentDateTime().toString("dd.MM.yyyy_hh:mm:ss:zzz_") + ui->lineEditSaveFileName->text());
}

void MainWindow::on_pushButtonLogPath_clicked()
{
    QString newPath;
    if (QDir(ui->lineEditSaveLogPath->text()).isReadable())
        newPath = QFileDialog::getExistingDirectory(this, "Set log path:", ui->lineEditSaveLogPath->text());
    else
        newPath = QFileDialog::getExistingDirectory(this, "Set log path:", QDir::homePath());

    if (newPath.isEmpty() == false)
        ui->lineEditSaveLogPath->setText(newPath);
}

void MainWindow::on_comboBoxSerialReadMode_currentIndexChanged(int index)
{
    serial.setReadMode(index);
}

void MainWindow::on_actionAbout_Qt_triggered()
{
    qApp->aboutQt();
}

void MainWindow::on_pushButtonClearTable_clicked()
{
    ui->tableWidgetParsedData->clearContents();
    ui->tableWidgetParsedData->setRowCount(0);
}

void MainWindow::on_tableWidgetParsedData_customContextMenuRequested(const QPoint &pos)
{
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);

    menu->addAction("Apply to custom display", this, [=]()
                    { emit on_pushButtonSetSelectedToGraph_clicked(); }); // C++ lambda expression

    menu->addAction("Copy as text", this, [=]()
                    {
                        QString copyText;
                        foreach (auto item, ui->tableWidgetParsedData->selectedItems())
                            copyText.append(item->text() + " ");
                        QApplication::clipboard()->setText(copyText);
                    }); // C++ lambda expression

    menu->addAction("Set as clock label", this, [=]()
                    { ui->lineEditExternalClockLabel->setText(ui->tableWidgetParsedData->selectedItems().first()->text()); }); // C++ lambda expression

    menu->addAction("Clear table contents", this, [=]()
                    {
                        ui->tableWidgetParsedData->clearContents();
                        ui->tableWidgetParsedData->setRowCount(0);
                    }); // C++ lambda expression

    menu->popup(ui->tableWidgetParsedData->mapToGlobal(pos));
}

void MainWindow::on_pushButtonSetSelectedToGraph_clicked()
{
    QStringList tableLabels;
    for (auto i = 0; i < ui->tableWidgetParsedData->rowCount(); ++i)
    {
        if (ui->tableWidgetParsedData->item(i, 0)->isSelected())
            tableLabels.append(ui->tableWidgetParsedData->item(i, 0)->text());
    }

    // clearGraphs(true);

    if (ui->spinBoxMaxGraphs->value() < tableLabels.count())
        ui->spinBoxMaxGraphs->setValue(tableLabels.count());

    on_setSelectedLabels(&tableLabels);
}

void MainWindow::on_toolButtonAdvancedGraphMenu_clicked()
{
    if (ui->toolButtonAdvancedGraphMenu->arrowType() == Qt::ArrowType::DownArrow)
    {
        ui->splitterGraphTable->setSizes({ui->groupBoxLog->height(), 0});
        ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::UpArrow);
    }
    else
    {
        ui->splitterGraphTable->setSizes({static_cast<int>(ui->groupBoxLog->height() * 0.75), static_cast<int>(ui->groupBoxLog->height() * 0.25)});
        ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::DownArrow);
    }
}

void MainWindow::on_splitterGraphTable_splitterMoved(int pos, int index)
{
    if (pos < ui->splitterGraphTable->height() - 30)
        ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::DownArrow);
    else
        ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::UpArrow);
}

void MainWindow::on_pushButtonTextLogToggle_toggled(bool checked)
{
    if (checked)
    {
        ui->pushButtonTextLogToggle->setText("Enable");
    }
    else
    {
        ui->pushButtonTextLogToggle->setText("Disable");
    }
}

void MainWindow::on_lineEditExternalClockLabel_editingFinished()
{
    ui->lineEditExternalClockLabel->setText(ui->lineEditExternalClockLabel->text().simplified());
}

void MainWindow::on_pushButtonLoadPath_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,
                                                    tr("Open Log File:"), "",
                                                    tr("(*.txt *.csv);;All Files (*)"));

    ui->lineEditLoadFilePath->setText(fileName);
    QFile file(fileName);

    if (!file.exists())
    {
        ui->lineEditFileInfo->setText("File doesnt exist !");
        return;
    }

    QList<QTime> timeRange = fileReader.getFileTimeRange(&file);
    ui->timeEditMinParsingTime->setTime(timeRange.first());
    ui->timeEditMaxParsingTime->setTime(timeRange.last());

    QString info = "Size: " + QString::number(file.size());
    ui->lineEditFileInfo->setText(info);
}

void MainWindow::on_processLoadedFileLine(QString *line, int *progressPercent)
{
    parser.parse(*line, true, true, ""); // Parse string - split into labels + numeric data
    QStringList labelList = parser.getStringListLabels();
    QList<double> numericDataList = parser.getListNumericValues();
    QList<long> timeStampList = parser.getListTimeStamp();

    this->processChart(labelList, numericDataList, timeStampList);
    this->processTable(labelList, numericDataList); // Fill tableWidget
    // this->saveToRAM(labelList, numericDataList, timeStampList);
    //  addLog(line);

    ui->progressBarLoadFile->setValue(*progressPercent);
}

void MainWindow::on_updateProgressBar(float *percent)
{
    ui->progressBarLoadFile->setValue(*percent);
}

void MainWindow::on_processLoadedFile(QString *text)
{
    disconnect(&fileReader, SIGNAL(textReady(QString *)), this, SLOT(on_processLoadedFile(QString *)));
    connect(&parser, SIGNAL(updateProgress(float *)), this, SLOT(on_updateProgressBar(float *)));

    parser.setReportProgress(true);
    parser.clearStorage();
    parser.setParsingTimeRange(ui->timeEditMinParsingTime->time(), ui->timeEditMaxParsingTime->time());

    // Select file type
    if (ui->comboBoxLogFormat->currentIndex() == 0)
        parser.parseCSV(*text, (bool)ui->comboBoxClockSource->currentIndex() == 1, ui->lineEditExternalClockLabel->text());
    else
        parser.parse(*text, false, true, "");

    // disconnect events, reset parser
    parser.resetTimeRange();
    parser.setReportProgress(false);
    disconnect(&parser, SIGNAL(updateProgress(float *)), this, SLOT(on_updateProgressBar(float *)));

    // Parsing
    {
        QStringList labelList = parser.getStringListLabels();
        QList<double> numericDataList = parser.getListNumericValues();
        QList<long> timeStampList = parser.getListTimeStamp();

        this->processChart(labelList, numericDataList, timeStampList);
        this->processTable(labelList, numericDataList);
        this->saveToRAM(labelList, numericDataList, timeStampList);
    }

    // Add text to log
    {
        if (ui->checkBoxAppendLoadedTextToLog->isChecked() == true)
            this->ui->textBrowserLogs->appendPlainText(*text);
    }
}

void MainWindow::on_pushButtonLoadRAMBuffer_clicked()
{
    if (ui->checkBoxRAMClearChart->isChecked())
        this->clearGraphData(false);

    loadFromRAM((bool)ui->comboBoxRAMLoadMode->currentIndex());
}

void MainWindow::on_pushButtonRAMClear_clicked()
{
    parser.clearStorage();
}

void MainWindow::on_pushButtonLoadFile_clicked()
{
    ui->progressBarLoadFile->setValue(0);

    if (ui->pushButtonLoadFile->text().contains("Load"))
    {
        this->clearGraphs(true);
        ui->pushButtonLoadFile->setText("Cancel");

        if (QDir(ui->lineEditLoadFilePath->text().trimmed()).isReadable())
        {
            QFile inputFile(ui->lineEditLoadFilePath->text().trimmed());
            connect(&fileReader, SIGNAL(textReady(QString *)), this, SLOT(on_processLoadedFile(QString *)));

            connect(&fileReader, &FileReader::fileReadFinished, this, [=]()
                    {
                        ui->pushButtonLoadFile->setText("Load File");
                        ui->progressBarLoadFile->setValue(100);
                    });

            if (fileReader.readAllAtOnce(&inputFile))
            {
                addLog("App >>\t Read file succesfully... ", true);
            }
            else
            {
                addLog("App >>\t Error - invalid file !", true);
                ui->pushButtonLoadFile->setText("Load File");
                ui->progressBarLoadFile->setValue(0);
            }
        }
        else
        {
            addLog("App >>\t Error - invalid file path !", true);
            ui->pushButtonLoadFile->setText("Load File");
            ui->progressBarLoadFile->setValue(0);
        }
    }
    else
    {
        parser.abort();
        disconnect(&fileReader, SIGNAL(textReady(QString *)), this, SLOT(on_processLoadedFile(QString *)));
        ui->pushButtonLoadFile->setText("Load File");
        ui->progressBarLoadFile->setValue(0);
    }
}

void MainWindow::on_lineEditLoadFilePath_customContextMenuRequested(const QPoint &pos)
{
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);

    menu->addAction("Open file", this, [=]()
                    { QDesktopServices::openUrl(QUrl(ui->lineEditLoadFilePath->text())); }); // C++ lambda expression

    menu->addAction("Open folder", this, [=]()
                    { QDesktopServices::openUrl(QUrl(QDir(QFileInfo(ui->lineEditLoadFilePath->text()).absoluteDir()).absolutePath())); }); // C++ lambda expression

    menu->popup(ui->lineEditLoadFilePath->mapToGlobal(pos));
}

void MainWindow::on_pushButtonSaveRAMBuffer_clicked()
{
    //QStringList newlabelList, QList<double> newDataList, QList<long> newTimeList, QString text)

    QStringList newlabelList;
    QList<double> newDataList;
    QList<long> newTimeList;
    QString text = "";

    for (auto i = 0; i < ui->widgetChart->graphCount(); ++i)
    {
        newlabelList.append(ui->widgetChart->graph(i)->name());

        QCPGraphDataContainer::const_iterator begin = ui->widgetChart->graph(i)->data()->at(ui->widgetChart->graph(i)->data()->dataRange().begin()); // get range begin iterator from index
        QCPGraphDataContainer::const_iterator end = ui->widgetChart->graph(i)->data()->at(ui->widgetChart->graph(i)->data()->dataRange().end());     // get range end iterator from index

        for (QCPGraphDataContainer::const_iterator it = begin; it != end; ++it)
        {
            newTimeList.append(it->key);
            newDataList.append(it->value);
        }
    }

    if (ui->comboBoxRAMSaveMode->currentIndex() == 1)
        text.append(ui->textBrowserLogs->toPlainText());

    parser.appendSetToMemory(newlabelList, newDataList, newTimeList, text);
}

void MainWindow::on_actionInfo_triggered()
{
    // infoDialog.setModal(true);
    infoDialog.show();
}

void MainWindow::on_comboBoxLoggingMode_currentIndexChanged(int index)
{
    if (index == 0)
        ui->checkBoxSimplifyLog->setEnabled(true);
    else if (index == 1)
        ui->checkBoxSimplifyLog->setEnabled(false);
}

void MainWindow::on_actionWhats_this_triggered()
{
    QWhatsThis::enterWhatsThisMode();
}

void MainWindow::on_lineEditSaveFileName_editingFinished()
{
    if (ui->comboBoxLogFormat->currentText() == ".txt")
    {
        if (ui->lineEditSaveFileName->text().endsWith(".txt") == false)
            ui->lineEditSaveFileName->setText(ui->lineEditSaveFileName->text().append(".txt")); // auto-complete filename with .txt extension
    }
    else if (ui->comboBoxLogFormat->currentText() == ".csv")
    {
        if (ui->lineEditSaveFileName->text().endsWith(".csv") == false)
            ui->lineEditSaveFileName->setText(ui->lineEditSaveFileName->text().append(".csv")); // auto-complete filename with .txt extension
    }
}

void MainWindow::on_checkBoxAutoSaveBuffer_toggled(bool checked)
{
    parser.clearStorage();
    ui->pushButtonSaveParserMemory->setEnabled(!checked);
}

void MainWindow::on_comboBoxClockSource_currentIndexChanged(int index)
{
    if (index == 1)
    {
        ui->lineEditExternalClockLabel->setEnabled(true);
        ui->comboBoxExternalTimeFormat->setEnabled(true);
    }
    else
    {
        ui->lineEditExternalClockLabel->setEnabled(false);
        ui->comboBoxExternalTimeFormat->setEnabled(false);
    }
}

void MainWindow::on_comboBoxFormat_currentIndexChanged(int index)
{
    if (index == 0)
        ui->comboBoxTextProcessing->setEnabled(true);
    else
        ui->comboBoxTextProcessing->setEnabled(false);
}

void MainWindow::on_toolButtonHideTable_clicked()
{
    ui->splitterGraphTable->setSizes({ui->splitterGraphTable->width(), 0});
    ui->toolButtonAdvancedGraphMenu->setArrowType(Qt::ArrowType::UpArrow);
}

void MainWindow::on_comboBoxAddTextMode_currentIndexChanged(int index)
{
    //    if (index == 1)
    //    {
    //        ui->radioButtonScrollToButtom->setCheckable(false);
    //         ui->radioButtonScrollToButtom->setChecked(false);
    //    }
    //    else
    //    {
    //        ui->radioButtonScrollToButtom->setCheckable(true);
    //    }
}

void MainWindow::on_actionHide_parser_data_triggered()
{
    ui->splitterGraphTable->setSizes({ui->splitterGraphTable->width(), 0});
}

void MainWindow::on_actionShow_parser_data_triggered()
{
    ui->splitterGraphTable->setSizes({ui->splitterGraphTable->width(), ui->splitterGraphTable->height() / 2});
}

void MainWindow::on_radioButtonScrollToButtom_clicked()
{
    ui->textBrowserLogs->verticalScrollBar()->setValue(ui->textBrowserLogs->verticalScrollBar()->maximum());
}

void MainWindow::on_comboBoxLogFormat_currentIndexChanged(int index)
{
    ui->comboBoxLoggingMode->setEnabled((bool)index);
    ui->checkBoxSimplifyLog->setEnabled((bool)index);
    // ui->pushButtonLoadFile->setEnabled((bool)index);

    if (ui->comboBoxLogFormat->currentText().contains("txt"))
        ui->lineEditSaveFileName->setText(ui->lineEditSaveFileName->text().replace("csv", "txt"));
    else if (ui->comboBoxLogFormat->currentText().contains("csv"))
        ui->lineEditSaveFileName->setText(ui->lineEditSaveFileName->text().replace("txt", "csv"));
}

void MainWindow::on_comboBoxGraphDisplayMode_currentIndexChanged(int index)
{
    clearGraphs(true);
    loadFromRAM(0);

    QPalette *paletteRed = new QPalette();
    paletteRed->setColor(QPalette::Text, Qt::GlobalColor::red);

    if (index == 0)
    {
        ui->lineEditCustomParsingRules->setEnabled(false);
        ui->labelParsingRules->setEnabled(false);
        ui->comboBoxGraphDisplayMode->setPalette(QPalette()); // Empty = default (OS specific !)
    }
    else
    {
        ui->lineEditCustomParsingRules->setEnabled(true);
        ui->labelParsingRules->setEnabled(true);
        ui->comboBoxGraphDisplayMode->setPalette(*paletteRed);
    }
}

void MainWindow::on_pushButtonClearLogTable_clicked()
{
    ui->tableWidgetLogTable->setRowCount(0);
    ui->tableWidgetLogTable->setColumnCount(1);
}

void MainWindow::on_checkBoxScrollLogEnableSorting_toggled(bool checked)
{
    ui->tableWidgetLogTable->setSortingEnabled(checked);
}

// https://stackoverflow.com/questions/27353026/qtableview-output-save-as-csv-or-txt
void MainWindow::exportTableLogToCSV(QTableView *table, QChar sep)
{
    QString filters("CSV files (*.csv);;All files (*.*)");
    QString defaultFilter("CSV files (*.csv)");
    QString fileName = QFileDialog::getSaveFileName(0, "Save file", QCoreApplication::applicationDirPath(),
                                                    filters, &defaultFilter);
    QFile file(fileName);

    QAbstractItemModel *model = table->model();

    if (file.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream data(&file);
        QStringList strList;
        for (int i = 0; i < model->columnCount(); ++i)
        {
            if (model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString().length() > 0)
                strList.append("\"" + model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString() + "\"");
            else
                strList.append("");
        }

        data << strList.join(sep) << "\n";

        for (int i = 0; i < model->rowCount(); ++i)
        {
            strList.clear();
            for (int j = 0; j < model->columnCount(); ++j)
            {
                if (model->data(model->index(i, j)).toString().length() > 0)
                    strList.append("\"" + model->data(model->index(i, j)).toString() + "\"");
                else
                    strList.append("");
            }
            data << strList.join(sep) + "\n";
        }
        file.close();
    }
}

void MainWindow::exportArraysToCSV(QStringList labelList, QList<QList<double>> dataColums, QChar sep)
{
    QString filters("CSV files (*.csv);;All files (*.*)");
    QString defaultFilter("CSV files (*.csv)");
    QString fileName = QFileDialog::getSaveFileName(0, "Save file", QCoreApplication::applicationDirPath(),
                                                    filters, &defaultFilter);
    QFile file(fileName);

    if (file.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream data(&file);
        QStringList strList;

        foreach (auto label, labelList)
        {
            if (label.length() > 0)
                strList.append("\"" + label + "\"");
            else
                strList.append("");
        }

        data << strList.join(sep) << "\n";

        int maxRowCount = 0;
        foreach (auto column, dataColums)
            maxRowCount = qMax(maxRowCount, column.count());

        for (int i = 0; i < maxRowCount; ++i) // rows
        {
            strList.clear();
            for (int j = 0; j < dataColums.count(); ++j) // columns
            {
                if (i < dataColums[j].count())
                    strList.append(QString::number(dataColums[j][i], 'f'));
                else
                    strList.append("\"\"");
            }
            data << strList.join(sep) + "\n";
        }
        file.close();
    }
}

void MainWindow::on_pushButtonExportLogTableToCSV_clicked()
{
    this->exportTableLogToCSV(ui->tableWidgetLogTable);
}

void MainWindow::on_pushButtonSerialLogScrollown_clicked()
{
    ui->textBrowserLogs->verticalScrollBar()->setValue(ui->textBrowserLogs->verticalScrollBar()->maximum());
}

void MainWindow::on_pushButtonEnableTableLog_toggled(bool checked)
{
    if (checked)
    {
        ui->pushButtonEnableTableLog->setChecked(true);
        ui->pushButtonEnableTableLog->setText("Enable");
    }
    else
    {
        ui->pushButtonEnableTableLog->setChecked(false);
        ui->pushButtonEnableTableLog->setText("Disable");
    }
}

void MainWindow::on_lineEditLoadFilePath_textChanged(const QString &arg1)
{
    QFile file(ui->lineEditLoadFilePath->text());

    if (!file.exists())
    {
        ui->lineEditFileInfo->setText("File doesnt exist !");
        return;
    }

    QList<QTime> timeRange = fileReader.getFileTimeRange(&file);
    ui->timeEditMinParsingTime->setTime(timeRange.first());
    ui->timeEditMaxParsingTime->setTime(timeRange.last());

    QString info = "Size: " + QString::number(file.size());
    ui->lineEditFileInfo->setText(info);
}

void MainWindow::on_actionFull_parser_data_triggered()
{
    ui->splitterGraphTable->setSizes({0, ui->splitterGraphTable->height()});
}

void MainWindow::on_actionImage_triggered()
{
    QString default_name = "export.png";
    QString filename = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::homePath() + "/" + default_name, tr("Image Files (*.png *.jpg *.bmp)"));

    if (filename.isEmpty())
        return;

    QImage bitmap(ui->widgetChart->size(), QImage::Format_ARGB32_Premultiplied);
    QPainter painter(&bitmap);
    ui->widgetChart->render(&painter, QPoint(), QRegion(), QWidget::DrawChildren);
    QImageWriter writer(filename, "png");
    writer.write(bitmap);
    qDebug() << "Wrote image to file";
}

//void MainWindow::on_actionto_csv_triggered()
//{

//    //    QStringList labelList = parser.getLabelStorage();

//    //    QStringList columnNames = labelList;
//    //    QList<QList<double>> columnsData;

//    //    QList<double> numericDataList = parser.getDataStorage();

//    //    columnNames.removeDuplicates();

//    //    for (auto i = 0; i < columnNames.count(); ++i)
//    //    {
//    //        columnsData.append(*new QList<double>);

//    //        while (labelList.contains(columnNames[i]))
//    //        {
//    //            columnsData[columnsData.count() - 1].append(numericDataList.takeAt(labelList.indexOf(columnNames[i])));
//    //            labelList.removeAt(labelList.indexOf(columnNames[i]));
//    //        }
//    //    }

//}

void MainWindow::on_actionto_csv_triggered()
{
    QStringList columnNames;
    QList<QList<double>> columnsData;

    parser.getCSVReadyData(&columnNames, &columnsData);

    this->exportArraysToCSV(columnNames, columnsData, ',');
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: Qt是一种非常流行的跨平台C++应用程序开发框架,用来架构适用于各种操作系统的桌面、内嵌以及移动平台应用程序,广泛应用于许多领域,例如电子商务、电子邮件、即时通讯等。 Qt框架配套了一份详尽的中文帮助手册,方便开发者们使用它来构建应用程序。而在Qt 5.9中,这份帮助手册更是得到了进一步的完善和更新,以适应更多的开发场景和使用需求。 这份中文帮助手册囊括了Qt框架中各个组件的详细使用说明,包括QML实用性例程Qt Quick、Qt元对象系统、Qt WebKit、状态机框架、Qt核心、Qt GUI、Qt Widgets、Qt SQL等。有时候,开发者们还将使用到一个更加具体的部分,例如Qt QML属性类型和Qt 3D框架等。 总体而言,Qt 5.9中文帮助手册提供了一份全面深入的指南,帮助开发者们更加轻松地学习和使用Qt框架,更好地开发出高质量的应用程序。对于有需要接触这一平台的人,在这份帮助手册中一定可以找到他们需要的所有信息和指导。 ### 回答2: Qt5.9是一款非常流行的跨平台C++应用程序开发框架,其提供了丰富的图形界面、网络通讯、数据库等功能模块,可用于开发多种应用程序。而为了方便开发者使用,Qt5.9中也提供了详细的中文帮助手册。 这份中文帮助手册覆盖了Qt5.9框架中的各个功能模块,包括框架概述、安装与配置、使用指南、常见问题解答等内容,涵盖了大量的函数、类和对象等API文档。也就是说,只要使用Qt5.9进行开发,开发者就可以通过这份中文帮助手册便捷地了解框架的使用和相关细节。 在使用Qt5.9开发过程中,中文帮助手册能够有效地提高开发人员在项目研发中的效率和质量。无论是初学者还是经验丰富的开发者,都可以迅速查找到具体的文档,深入理解Qt5.9框架的核心知识。同时,在出现问题时,开发者也可以通过手册中的常见问题解答,快速解决困扰自己的问题。 总之,Qt5.9中文帮助手册是一份非常实用的工具,对Qt5.9的学习和应用都有很大的帮助。对于需要使用该框架进行开发的人员来说,掌握和使用这份中文帮助手册是非常必要和重要的。 ### 回答3: Qt是一种C++开发框架,用于开发跨平台应用程序。它具有丰富的图形界面和基础功能库,可用于构建具有不同平台兼容性的各种应用程序。Qt5Qt框架的最新版本,其中包括了新的功能和更新的api。 在Qt5中,有一个有用的中文帮助手册,这对于想要了解Qt框架的人来说非常重要。该手册包含了大量的文档,其中包括了关于Qt库的详细信息和使用方法。一般情况下,这些文档是Qt官网上英文文档的翻译版本,也有一些本土化的文档。 在使用Qt开发应用程序时,通过查阅这个中文帮助手册,可以更快地掌握如何使用Qt的不同部分。该手册涵盖的主题包括Qt的基础知识、如何使用不同的Qt组件、如何使用不同的Qt类、如何使用Qt的UI设计工具等。它还提供了许多代码示例,以说明如何使用某个特定的Qt功能。 总之,Qt5中的中文帮助手册是一个十分重要和有用的资源,可以帮助开发人员更好地利用Qt框架来开发出高质量的跨平台应用程序。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值