#
# Copyright © 2019 Hasan Yavuz Özderya
# This file is part of serialplot.
# serialplot is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# serialplot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with serialplot. If not, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.2.2)
project(serialplot)
set(PROGRAM_NAME ${CMAKE_PROJECT_NAME} CACHE STRING "Output program name")
set(PROGRAM_DISPLAY_NAME "SerialPlot" CACHE STRING "Display name (menus etc) of the program")
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
# add local path for cmake modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
# Find the QtWidgets library
find_package(Qt5Widgets)
# If set, cmake will download Qwt over SVN, build and use it as a static library.
set(BUILD_QWT true CACHE BOOL "Download and build Qwt automatically.")
if (BUILD_QWT)
include(BuildQwt)
else (BUILD_QWT)
find_package(Qwt 6.1 REQUIRED)
endif (BUILD_QWT)
# includes
include_directories("./src"
${QWT_INCLUDE_DIR}
)
# wrap UI and resource files
qt5_wrap_ui(UI_FILES
src/mainwindow.ui
src/portcontrol.ui
src/about_dialog.ui
src/snapshotview.ui
src/commandpanel.ui
src/commandwidget.ui
src/dataformatpanel.ui
src/plotcontrolpanel.ui
src/recordpanel.ui
src/numberformatbox.ui
src/endiannessbox.ui
src/binarystreamreadersettings.ui
src/asciireadersettings.ui
src/framedreadersettings.ui
src/demoreadersettings.ui
src/updatecheckdialog.ui
src/datatextview.ui
if (WIN32)
qt5_add_resources(RES_FILES misc/icons.qrc misc/winicons.qrc)
else (WIN32)
qt5_add_resources(RES_FILES misc/icons.qrc)
endif (WIN32)
add_executable(${PROGRAM_NAME} WIN32
src/main.cpp
src/mainwindow.cpp
src/portcontrol.cpp
src/plot.cpp
src/zoomer.cpp
src/scrollzoomer.cpp
src/scrollbar.cpp
src/hidabletabwidget.cpp
src/scalepicker.cpp
src/scalezoomer.cpp
src/portlist.cpp
src/snapshot.cpp
src/snapshotview.cpp
src/snapshotmanager.cpp
src/plotsnapshotoverlay.cpp
src/commandpanel.cpp
src/commandwidget.cpp
src/commandedit.cpp
src/dataformatpanel.cpp
src/plotcontrolpanel.cpp
src/recordpanel.cpp
src/datarecorder.cpp
src/tooltipfilter.cpp
src/sneakylineedit.cpp
src/stream.cpp
src/streamchannel.cpp
src/channelinfomodel.cpp
src/ringbuffer.cpp
src/indexbuffer.cpp
src/linindexbuffer.cpp
src/readonlybuffer.cpp
src/framebufferseries.cpp
src/numberformatbox.cpp
src/endiannessbox.cpp
src/abstractreader.cpp
src/binarystreamreader.cpp
src/binarystreamreadersettings.cpp
src/asciireader.cpp
src/asciireadersettings.cpp
src/demoreader.cpp
src/demoreadersettings.cpp
src/framedreader.cpp
src/framedreadersettings.cpp
src/plotmanager.cpp
src/plotmenu.cpp
src/barplot.cpp
src/barchart.cpp
src/barscaledraw.cpp
src/numberformat.cpp
src/updatechecker.cpp
src/versionnumber.cpp
src/updatecheckdialog.cpp
src/samplepack.cpp
src/source.cpp
src/sink.cpp
src/samplecounter.cpp
src/ledwidget.cpp
src/datatextview.cpp
misc/windows_icon.rc
${UI_FILES}
${RES_FILES}
# Use the Widgets module from Qt 5.
target_link_libraries(${PROGRAM_NAME}
${QWT_LIBRARY}
qt5_use_modules(${PROGRAM_NAME} Widgets SerialPort Network)
add_dependencies(${PROGRAM_NAME} QWT)
endif ()
# set compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
# Enable C++11 support, fail if not supported
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
# default version
set(VERSION_STRING "0.10.0")
set(VERSION_REVISION "0")
# get revision number from mercurial and parse version string
include(GetVersion)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_STRING=\\\"${VERSION_STRING}\\\" ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_MAJOR=${VERSION_MAJOR} ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_MINOR=${VERSION_MINOR} ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_PATCH=${VERSION_PATCH} ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_REVISION=\\\"${VERSION_REVISION}\\\" ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROGRAM_NAME=\\\"${PROGRAM_NAME}\\\" ")
# add make run target
add_custom_target(run
COMMAND ${PROGRAM_NAME}
DEPENDS ${PROGRAM_NAME}
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
# installing
install(TARGETS ${PROGRAM_NAME} DESTINATION bin)
# for windows put libraries to install directory
file(GLOB WINDOWS_INSTALL_LIBRARIES
"${CMAKE_BINARY_DIR}/windows_install_libraries/*.*")
install(FILES ${WINDOWS_INSTALL_LIBRARIES} DESTINATION bin)
# prepare menu item and icon
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/misc/program_name.desktop.in"
"${CMAKE_BINARY_DIR}/${PROGRAM_NAME}.desktop")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/misc/program_name.png"
"${CMAKE_BINARY_DIR}/${PROGRAM_NAME}.png" COPYONLY)
set(DESKTOP_FILE ${CMAKE_BINARY_DIR}/${PROGRAM_NAME}.desktop)
set(ICON_FILE ${CMAKE_BINARY_DIR}/${PROGRAM_NAME}.png)
# install menu item and icon
if (UNIX)
install(FILES ${DESKTOP_FILE} DESTINATION share/applications/)
install(FILES ${ICON_FILE} DESTINATION share/icons/hicolor/256x256/apps/)
endif (UNIX)
# uninstalling
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
@ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
# testing
set(ENABLE_TESTS false CACHE BOOL "Build tests.")
if (ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
# packaging
include(BuildLinuxAppImage)
set(CPACK_GENERATOR "DEB")
elseif (WIN32)
set(CPACK_GENERATOR "NSIS")
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_NAME "${PROGRAM_NAME}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Small and simple software for plotting data from serial port")
set(CPACK_PACKAGE_CONTACT "Hasan Yavuz Özderya <hy@ozderya.net>")
set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH})
set(CPACK_STRIP_FILES TRUE)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5widgets5 (>= 5.2.1), libqt5svg5 (>= 5.2.1), libqt5serialport5 (>= 5.2.1), libc6 (>= 2.19)")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Small and simple software for plotting data from serial port
Supports binary data formats ([u]int8, [u]int16, [u]int32, float)
and ASCII (as CSV). Captured waveforms can be exported in CSV format.
Can also send simple user defined commands to serial port device.")
if (NOT QWT_USE_STATIC)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libqwt6-qt5 (>= 6.1.1)")
endif (NOT QWT_USE_STATIC)
set(CPACK_PACKAGE_EXECUTABLES "${PROGRAM_NAME}")
set(CPACK_PACKAGE_EXECUTABLES "${PROGRAM_NAME};${PROGRAM_DISPLAY_NAME}")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROGRAM_NAME}")
set(CPACK_CREATE_DESKTOP_LINKS "${PROGRAM_NAME}")
set(CPACK_NSIS_MODIFY_PATH "ON")
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/misc/serialplot.bmp")
string(REPLACE "/" "\\\\" CPACK_PACKAGE_ICON ${CPACK_PACKAGE_ICON})
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING")
set(CPACK_NSIS_MENU_LINKS
"https://bitbucket.org/hyOzd/serialplot" "SerialPlot source code on bitbucket.org")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL true)
# set debian package name
string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
if(DPKG_PROGRAM)
execute_process(
COMMAND ${DPKG_PROGRAM} --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CPACK_PACKAGE_FILE_NAME
"${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
else(DPKG_PROGRAM)
"${CPACK_PACKAGE_NAME_LOWERCASE}_${PROJECT_VERSION}-${PROJECT_VERSION_REVISION}_${CMAKE_SYSTEM_NAME}")
endif(DPKG_PROGRAM)
include(CPack)
/*
Copyright © 2017 Hasan Yavuz Özderya
This file is part of serialplot.
serialplot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
serialplot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with serialplot. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QApplication>
#include <QtGlobal>
#include <iostream>
#include "mainwindow.h"
#include "tooltipfilter.h"
#include "version.h"
MainWindow* pMainWindow;
MainWindow* pMainWindow = nullptr;
void messageHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg)
{
// TODO: don't call MainWindow::messageHandler if window is destroyed
pMainWindow->messageHandler(type, context, msg);
QString logString;
switch (type)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
case QtInfoMsg:
logString = "[Info] " + msg;
break;
#endif
case QtDebugMsg:
logString = "[Debug] " + msg;
case QtWarningMsg:
logString = "[Warning] " + msg;
case QtCriticalMsg:
logString = "[Error] " + msg;
case QtFatalMsg:
logString = "[Fatal] " + msg;
}
std::cerr << logString.toStdString() << std::endl;
if (pMainWindow != nullptr)
pMainWindow->messageHandler(type, logString, msg);
if (type == QtFatalMsg)
__builtin_trap();
int main(int argc, char *argv[])
QApplication a(argc, argv);
QApplication::setApplicationName(PROGRAM_NAME);
QApplication::setApplicationVersion(VERSION_STRING);
qInstallMessageHandler(messageHandler);
MainWindow w;
pMainWindow = &w;
ToolTipFilter ttf;
a.installEventFilter(&ttf);
// log application information
qDebug() << "SerialPlot" << VERSION_STRING;
qDebug() << "Revision" << VERSION_REVISION;
w.show();
return a.exec();
Copyright © 2019 Hasan Yavuz Özderya
#include "ui_mainwindow.h"
#include <QByteArray>
#include <QFileDialog>
#include <QMessageBox>
#include <QFile>
#include <QTextStream>
#include <QMenu>
#include <QDesktopServices>
#include <QMap>
#include <QtDebug>
#include <QCommandLineParser>
#include <QFileInfo>
#include <qwt_plot.h>
#include <limits.h>
#include <cmath>
#include <cstdlib>
#include <plot.h>
#include <barplot.h>
#include "framebufferseries.h"
#include "utils.h"
#include "defines.h"
#include "setting_defines.h"
#if defined(Q_OS_WIN) && defined(QT_STATIC)
#include <QtPlugin>
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
// TODO: depends on tab insertion order, a better solution would be to use object names
const QMap<int, QString> panelSettingMap({
{0, "Port"},
{1, "DataFormat"},
{2, "Plot"},
{3, "Commands"},
{4, "Record"},
{5, "TextView"},
{6, "Log"}
});
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
aboutDialog(this),
portControl(&serialPort),
secondaryPlot(NULL),
snapshotMan(this, &stream),
commandPanel(&serialPort),
dataFormatPanel(&serialPort),
recordPanel(&stream),
textView(&stream),
updateCheckDialog(this)
ui->setupUi(this);
plotMan = new PlotManager(ui->plotArea, &plotMenu, &stream);
ui->tabWidget->insertTab(0, &portControl, "Port");
ui->tabWidget->insertTab(1, &dataFormatPanel, "Data Format");
ui->tabWidget->insertTab(2, &plotControlPanel, "Plot");
ui->tabWidget->insertTab(3, &commandPanel, "Commands");
ui->tabWidget->insertTab(4, &recordPanel, "Record");
ui->tabWidget->insertTab(5, &textView, "Text View");
ui->tabWidget->setCurrentIndex(0);
auto tbPortControl = portControl.toolBar();
addToolBar(tbPortControl);
addToolBar(recordPanel.toolbar());
ui->plotToolBar->addAction(snapshotMan.takeSnapshotAction());
menuBar()->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu());
menuBar()->insertMenu(ui->menuHelp->menuAction(), commandPanel.menu());
connect(&commandPanel, &CommandPanel::focusRequested, [this]()
this->ui->tabWidget->setCurrentWidget(&commandPanel);
this->ui->tabWidget->showTabs();
tbPortControl->setObjectName("tbPortControl");
ui->plotToolBar->setObjectName("tbPlot");
setupAboutDialog();
// init view menu
ui->menuBar->insertMenu(ui->menuSecondary->menuAction(), &plotMenu);
plotMenu.addSeparator();
QMenu* tbMenu = plotMenu.addMenu("Toolbars");
tbMenu->addAction(ui->plotToolBar->toggleViewAction());
tbMenu->addAction(portControl.toolBar()->toggleViewAction());
// init secondary plot menu
auto group = new QActionGroup(this);
group->addAction(ui->actionVertical);
group->addAction(ui->actionHorizontal);
// init UI signals
// Secondary plot menu signals
connect(ui->actionBarPlot, &QAction::triggered,
this, &MainWindow::showBarPlot);
connect(ui->actionVertical, &QAction::triggered,
[this](bool checked)
if (checked) ui->splitter->setOrientation(Qt::Vertical);
connect(ui->actionHorizontal, &QAction::triggered,
if (checked) ui->splitter->setOrientation(Qt::Horizontal);
// Help menu signals
QObject::connect(ui->actionHelpAbout, &QAction::triggered,
&aboutDialog, &QWidget::show);
QObject::connect(ui->actionCheckUpdate, &QAction::triggered,
&updateCheckDialog, &QWidget::show);
QObject::connect(ui->actionReportBug, &QAction::triggered,
[](){QDesktopServices::openUrl(QUrl(BUG_REPORT_URL));});
// File menu signals
QObject::connect(ui->actionExportCsv, &QAction::triggered,
this, &MainWindow::onExportCsv);
QObject::connect(ui->actionSaveSettings, &QAction::triggered,
this, &MainWindow::onSaveSettings);
QObject::connect(ui->actionLoadSettings, &QAction::triggered,
this, &MainWindow::onLoadSettings);
ui->actionQuit->setShortcutContext(Qt::ApplicationShortcut);
QObject::connect(ui->actionQuit, &QAction::triggered,
this, &MainWindow::close);
// port control signals
QObject::connect(&portControl, &PortControl::portToggled,
this, &MainWindow::onPortToggled);
// plot control signals
connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
this, &MainWindow::onNumOfSamplesChanged);
plotMan, &PlotManager::setNumOfSamples);
connect(&plotControlPanel, &PlotControlPanel::yScaleChanged,
plotMan, &PlotManager::setYAxis);
connect(&plotControlPanel, &PlotControlPanel::xScaleChanged,
&stream, &Stream::setXAxis);
plotMan, &PlotManager::setXAxis);
connect(&plotControlPanel, &PlotControlPanel::plotWidthChanged,
plotMan, &PlotManager::setPlotWidth);
// plot toolbar signals
QObject::connect(ui->actionClear, SIGNAL(triggered(bool)),
this, SLOT(clearPlot()));
QObject::connect(snapshotMan.takeSnapshotAction(), &QAction::triggered,
plotMan, &PlotManager::flashSnapshotOverlay);
QObject::connect(ui->actionPause, &QAction::triggered,
&stream, &Stream::pause);
[this](bool enabled)
if (enabled && !recordPanel.recordPaused())
dataFormatPanel.pause(true);
else
dataFormatPanel.pause(false);
QObject::connect(&recordPanel, &RecordPanel::recordPausedChanged,
if (ui->actionPause->isChecked() && enabled)
connect(&serialPort, &QIODevice::aboutToClose,
&recordPanel, &RecordPanel::onPortClose);
// init plot
numOfSamples = plotControlPanel.numOfSamples();
stream.setNumSamples(numOfSamples);
plotControlPanel.setChannelInfoModel(stream.infoModel());
// init scales
stream.setXAxis(plotControlPanel.xAxisAsIndex(),
plotControlPanel.xMin(), plotControlPanel.xMax());
plotMan->setYAxis(plotControlPanel.autoScale(),
plotControlPanel.yMin(), plotControlPanel.yMax());
plotMan->setXAxis(plotControlPanel.xAxisAsIndex(),
plotMan->setNumOfSamples(numOfSamples);
plotMan->setPlotWidth(plotControlPanel.plotWidth());
// Init sps (sample per second) counter
spsLabel.setText("0sps");
spsLabel.setToolTip("samples per second (per channel)");
ui->statusBar->addPermanentWidget(&spsLabel);
connect(&sampleCounter, &SampleCounter::spsChanged,
this, &MainWindow::onSpsChanged);
// init demo
QObject::connect(ui->actionDemoMode, &QAction::toggled,
this, &MainWindow::enableDemo);
plotMan, &PlotManager::showDemoIndicator);
// init stream connections
connect(&dataFormatPanel, &DataFormatPanel::sourceChanged,
this, &MainWindow::onSourceChanged);
onSourceChanged(dataFormatPanel.activeSource());
// load default settings
QSettings settings("serialplot", "serialplot");
QSettings settings(PROGRAM_NAME, PROGRAM_NAME);
loadAllSettings(&settings);
handleCommandLineOptions(*QApplication::instance());
// ensure command panel has 1 command if none loaded
if (!commandPanel.numOfCommands())
commandPanel.newCommandAction()->trigger();
// Important: This should be after newCommandAction is triggered
// (above) we don't want user to be greeted with command panel on
// the very first run.
connect(commandPanel.newCommandAction(), &QAction::triggered, [this]()
MainWindow::~MainWindow()
if (serialPort.isOpen())
serialPort.close();
delete plotMan;
delete ui;
ui = NULL; // we check if ui is deleted in messageHandler
void MainWindow::closeEvent(QCloseEvent * event)
// save snapshots
if (!snapshotMan.isAllSaved())
auto clickedButton = QMessageBox::warning(
this, "Closing SerialPlot",
"There are un-saved snapshots. If you close you will loose the data.",
QMessageBox::Discard, QMessageBox::Cancel);
if (clickedButton == QMessageBox::Cancel)
event->ignore();
return;
// save settings
saveAllSettings(&settings);
settings.sync();
if (settings.status() != QSettings::NoError)
QString errorText;
if (settings.status() == QSettings::AccessError)
QString file = settings.fileName();
errorText = QString("Serialplot cannot save settings due to access error. \
This happens if you have run serialplot as root (with sudo for ex.) previously. \
Try fixing the permissions of file: %1, or just delete it.").arg(file);
errorText = QString("Serialplot cannot save settings due to unknown error: %1").\
arg(settings.status());
auto button = QMessageBox::critical(
NULL,
"Failed to save settings!", errorText,
QMessageBox::Cancel | QMessageBox::Ok);
if (button == QMessageBox::Cancel)
QMainWindow::closeEvent(event);
void MainWindow::setupAboutDialog()
Ui_AboutDialog uiAboutDialog;
uiAboutDialog.setupUi(&aboutDialog);
QObject::connect(uiAboutDialog.pbAboutQt, &QPushButton::clicked,
[](){ QApplication::aboutQt();});
QString aboutText = uiAboutDialog.lbAbout->text();
aboutText.replace("$VERSION_STRING$", VERSION_STRING);
aboutText.replace("$VERSION_REVISION$", VERSION_REVISION);
uiAboutDialog.lbAbout->setText(aboutText);
void MainWindow::onPortToggled(bool open)
// make sure demo mode is disabled
if (open && isDemoRunning()) enableDemo(false);
ui->actionDemoMode->setEnabled(!open);
void MainWindow::onSourceChanged(Source* source)
source->connectSink(&stream);
source->connectSink(&sampleCounter);
void MainWindow::clearPlot()
stream.clear();
plotMan->replot();
void MainWindow::onNumOfSamplesChanged(int value)
numOfSamples = value;
stream.setNumSamples(value);
void MainWindow::onSpsChanged(float sps)
int precision = sps < 1. ? 3 : 0;
spsLabel.setText(QString::number(sps, 'f', precision) + "sps");
bool MainWindow::isDemoRunning()
return ui->actionDemoMode->isChecked();
void MainWindow::enableDemo(bool enabled)
if (enabled)
if (!serialPort.isOpen())
dataFormatPanel.enableDemo(true);
ui->actionDemoMode->setChecked(false);
dataFormatPanel.enableDemo(false);
void MainWindow::showSecondary(QWidget* wid)
if (secondaryPlot != NULL)
secondaryPlot->deleteLater();
secondaryPlot = wid;
ui->splitter->addWidget(wid);
ui->splitter->setStretchFactor(0, 1);
ui->splitter->setStretchFactor(1, 0);
void MainWindow::hideSecondary()
if (secondaryPlot == NULL)
qFatal("Secondary plot doesn't exist!");
secondaryPlot = NULL;
void MainWindow::showBarPlot(bool show)
if (show)
auto plot = new BarPlot(&stream, &plotMenu);
plot->setYAxis(plotControlPanel.autoScale(),
plotControlPanel.yMin(),
plotControlPanel.yMax());
plot, &BarPlot::setYAxis);
showSecondary(plot);
hideSecondary();
void MainWindow::onExportCsv()
bool wasPaused = ui->actionPause->isChecked();
ui->actionPause->setChecked(true); // pause plotting
QString fileName = QFileDialog::getSaveFileName(this, tr("Export CSV File"));
if (fileName.isNull()) // user canceled export
ui->actionPause->setChecked(wasPaused);
Snapshot* snapshot = snapshotMan.makeSnapshot();
snapshot->save(fileName);
delete snapshot;
PlotViewSettings MainWindow::viewSettings() const
return plotMenu.viewSettings();
void MainWindow::messageHandler(QtMsgType type,
const QMessageLogContext &context,
const QString &logString,
if (ui != NULL) ui->ptLog->appendPlainText(logString);
if (ui != NULL)
ui->ptLog->appendPlainText(logString);
if (type != QtDebugMsg && ui != NULL)
ui->statusBar->showMessage(msg, 5000);
void MainWindow::saveAllSettings(QSettings* settings)
saveMWSettings(settings);
portControl.saveSettings(settings);
dataFormatPanel.saveSettings(settings);
stream.saveSettings(settings);
plotControlPanel.saveSettings(settings);
plotMenu.saveSettings(settings);
commandPanel.saveSettings(settings);
recordPanel.saveSettings(settings);
textView.saveSettings(settings);
updateCheckDialog.saveSettings(settings);
void MainWindow::loadAllSettings(QSettings* settings)
loadMWSettings(settings);
portControl.loadSettings(settings);
dataFormatPanel.loadSettings(settings);
stream.loadSettings(settings);
plotControlPanel.loadSettings(settings);
plotMenu.loadSettings(settings);
commandPanel.loadSettings(settings);
recordPanel.loadSettings(settings);
textView.loadSettings(settings);
updateCheckDialog.loadSettings(settings);
void MainWindow::saveMWSettings(QSettings* settings)
// save window geometry
settings->beginGroup(SettingGroup_MainWindow);
settings->setValue(SG_MainWindow_Size, size());
settings->setValue(SG_MainWindow_Pos, pos());
// save active panel
settings->setValue(SG_MainWindow_ActivePanel,
panelSettingMap.value(ui->tabWidget->currentIndex()));
// save panel minimization
settings->setValue(SG_MainWindow_HidePanels,
ui->tabWidget->hideAction.isChecked());
// save window maximized state
settings->setValue(SG_MainWindow_Maximized,
bool(windowState() & Qt::WindowMaximized));
// save toolbar/dockwidgets state
settings->setValue(SG_MainWindow_State, saveState());
settings->endGroup();
void MainWindow::loadMWSettings(QSettings* settings)
// load window geometry
resize(settings->value(SG_MainWindow_Size, size()).toSize());
move(settings->value(SG_MainWindow_Pos, pos()).toPoint());
// set active panel
QString tabSetting =
settings->value(SG_MainWindow_ActivePanel, QString()).toString();
ui->tabWidget->setCurrentIndex(
panelSettingMap.key(tabSetting, ui->tabWidget->currentIndex()));
// hide panels
ui->tabWidget->hideAction.setChecked(
settings->value(SG_MainWindow_HidePanels,
ui->tabWidget->hideAction.isChecked()).toBool());
// maximize window
if (settings->value(SG_MainWindow_Maximized).toBool())
showMaximized();
// load toolbar/dockwidgets state
restoreState(settings->value(SG_MainWindow_State).toByteArray());
void MainWindow::onSaveSettings()
QString fileName = QFileDialog::getSaveFileName(
this, tr("Save Settings"), QString(), "INI (*.ini)");
if (!fileName.isNull()) // user canceled
QSettings settings(fileName, QSettings::IniFormat);
void MainWindow::onLoadSettings()
QString fileName = QFileDialog::getOpenFileName(
this, tr("Load Settings"), QString(), "INI (*.ini)");
void MainWindow::handleCommandLineOptions(const QCoreApplication &app)
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions);
parser.setApplicationDescription("Small and simple software for plotting data from serial port in realtime.");
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption configOpt({"c", "config"}, "Load configuration from file.", "filename");
QCommandLineOption portOpt({"p", "port"}, "Set port name.", "port name");
QCommandLineOption baudrateOpt({"b" ,"baudrate"}, "Set port baud rate.", "baud rate");
QCommandLineOption openPortOpt({"o", "open"}, "Open serial port.");
parser.addOption(configOpt);
parser.addOption(portOpt);
parser.addOption(baudrateOpt);
parser.addOption(openPortOpt);
parser.process(app);
if (parser.isSet(configOpt))
QString fileName = parser.value(configOpt);
QFileInfo fileInfo(fileName);
if (fileInfo.exists() && fileInfo.isFile())
qCritical() << "Configuration file not exist. Closing application.";
std::exit(1);
if (parser.isSet(portOpt))
portControl.selectPort(parser.value(portOpt));
if (parser.isSet(baudrateOpt))
portControl.selectBaudrate(parser.value(baudrateOpt));
if (parser.isSet(openPortOpt))
portControl.openPort();
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QButtonGroup>
#include <QLabel>
#include <QString>
#include <QVector>
#include <QList>
#include <QSerialPort>
#include <QSignalMapper>
#include <QTimer>
#include <QColor>
#include <QSettings>
#include <qwt_plot_curve.h>
#include "portcontrol.h"
#include "commandpanel.h"
#include "dataformatpanel.h"
#include "plotcontrolpanel.h"
#include "recordpanel.h"
#include "ui_about_dialog.h"
#include "stream.h"
#include "snapshotmanager.h"
#include "plotmanager.h"
#include "plotmenu.h"
#include "updatecheckdialog.h"
#include "samplecounter.h"
#include "datatextview.h"
namespace Ui {
class MainWindow;
class MainWindow : public QMainWindow
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
PlotViewSettings viewSettings() const;
const QString &msg);
void messageHandler(QtMsgType type, const QString &logString, const QString &msg);
private:
Ui::MainWindow *ui;
QDialog aboutDialog;
void setupAboutDialog();
QSerialPort serialPort;
PortControl portControl;
unsigned int numOfSamples;
QList<QwtPlotCurve*> curves;
// ChannelManager channelMan;
Stream stream;
PlotManager* plotMan;
QWidget* secondaryPlot;
SnapshotManager snapshotMan;
SampleCounter sampleCounter;
QLabel spsLabel;
CommandPanel commandPanel;
DataFormatPanel dataFormatPanel;
RecordPanel recordPanel;
PlotControlPanel plotControlPanel;
PlotMenu plotMenu;
DataTextView textView;
UpdateCheckDialog updateCheckDialog;
void handleCommandLineOptions(const QCoreApplication &app);
/// Returns true if demo is running
bool isDemoRunning();
/// Display a secondary plot in the splitter, removing and
/// deleting previous one if it exists
void showSecondary(QWidget* wid);
/// Hide secondary plot
void hideSecondary();
/// Stores settings for all modules
void saveAllSettings(QSettings* settings);
/// Load settings for all modules
void loadAllSettings(QSettings* settings);
/// Stores main window settings into a `QSettings`
void saveMWSettings(QSettings* settings);
/// Loads main window settings from a `QSettings`
void loadMWSettings(QSettings* settings);
/// `QWidget::closeEvent` handler
void closeEvent(QCloseEvent * event);
private slots:
void onPortToggled(bool open);
void onSourceChanged(Source* source);
void onNumOfSamplesChanged(int value);
void clearPlot();
void onSpsChanged(float sps);
void enableDemo(bool enabled);
void showBarPlot(bool show);
void onExportCsv();
void onSaveSettings();
void onLoadSettings();
};
#endif // MAINWINDOW_H
#include "ui_portcontrol.h"
#include <QSerialPortInfo>
#include <QKeySequence>
#define TBPORTLIST_MINWIDTH (200)
// setting mappings
const QMap<QSerialPort::Parity, QString> paritySettingMap({
{QSerialPort::NoParity, "none"},
{QSerialPort::OddParity, "odd"},
{QSerialPort::EvenParity, "even"},
PortControl::PortControl(QSerialPort* port, QWidget* parent) :
QWidget(parent),
ui(new Ui::PortControl),
portToolBar("Port Toolbar"),
openAction("Open", this),
loadPortListAction("↺", this)
serialPort = port;
connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)),
this, SLOT(onPortError(QSerialPort::SerialPortError)));
// setup actions
openAction.setCheckable(true);
openAction.setShortcut(QKeySequence("F12"));
openAction.setToolTip("Open Port");
QObject::connect(&openAction, &QAction::triggered,
this, &PortControl::openActionTriggered);
loadPortListAction.setToolTip("Reload port list");
QObject::connect(&loadPortListAction, &QAction::triggered,
[this](bool checked){loadPortList();});
// setup toolbar
portToolBar.addWidget(&tbPortList);
portToolBar.addAction(&loadPortListAction);
portToolBar.addAction(&openAction);
// setup port selection widgets
tbPortList.setMinimumWidth(TBPORTLIST_MINWIDTH);
tbPortList.setModel(&portList);
ui->cbPortList->setModel(&portList);
QObject::connect(ui->cbPortList,
SELECT<int>::OVERLOAD_OF(&QComboBox::activated),
this, &PortControl::onCbPortListActivated);
QObject::connect(&tbPortList,
this, &PortControl::onTbPortListActivated);
SELECT<const QString&>::OVERLOAD_OF(&QComboBox::activated),
this, &PortControl::selectPort);
this, &PortControl::selectListedPort);
// setup buttons
ui->pbOpenPort->setDefaultAction(&openAction);
ui->pbReloadPorts->setDefaultAction(&loadPortListAction);
// setup baud rate selection widget
QObject::connect(ui->cbBaudRate,
this, &PortControl::selectBaudRate);
this, &PortControl::_selectBaudRate);
// setup parity selection buttons
parityButtons.addButton(ui->rbNoParity, (int) QSerialPort::NoParity);
parityButtons.addButton(ui->rbEvenParity, (int) QSerialPort::EvenParity);
parityButtons.addButton(ui->rbOddParity, (int) QSerialPort::OddParity);
QObject::connect(&parityButtons,
SELECT<int>::OVERLOAD_OF(&QButtonGroup::buttonClicked),
this, &PortControl::selectParity);
// setup data bits selection buttons
dataBitsButtons.addButton(ui->rb8Bits, (int) QSerialPort::Data8);
dataBitsButtons.addButton(ui->rb7Bits, (int) QSerialPort::Data7);
dataBitsButtons.addButton(ui->rb6Bits, (int) QSerialPort::Data6);
dataBitsButtons.addButton(ui->rb5Bits, (int) QSerialPort::Data5);
QObject::connect(&dataBitsButtons,
this, &PortControl::selectDataBits);
// setup stop bits selection buttons
stopBitsButtons.addButton(ui->rb1StopBit, (int) QSerialPort::OneStop);
stopBitsButtons.addButton(ui->rb2StopBit, (int) QSerialPort::TwoStop);
QObject::connect(&stopBitsButtons,
this, &PortControl::selectStopBits);
// setup flow control selection buttons
flowControlButtons.addButton(ui->rbNoFlowControl,
(int) QSerialPort::NoFlowControl);
flowControlButtons.addButton(ui->rbHardwareControl,
(int) QSerialPort::HardwareControl);
flowControlButtons.addButton(ui->rbSoftwareControl,
(int) QSerialPort::SoftwareControl);
QObject::connect(&flowControlButtons,
this, &PortControl::selectFlowControl);
// initialize signal leds
ui->ledDTR->setOn(true);
ui->ledRTS->setOn(true);
// connect output signals
connect(ui->pbDTR, &QPushButton::clicked, [this]()
// toggle DTR
ui->ledDTR->toggle();
if (serialPort->isOpen())
serialPort->setDataTerminalReady(ui->ledDTR->isOn());
connect(ui->pbRTS, &QPushButton::clicked, [this]()
// toggle RTS
ui->ledRTS->toggle();
serialPort->setRequestToSend(ui->ledRTS->isOn());
// setup pin update leds
ui->ledDCD->setColor(Qt::yellow);
ui->ledDSR->setColor(Qt::yellow);
ui->ledRI->setColor(Qt::yellow);
ui->ledCTS->setColor(Qt::yellow);
pinUpdateTimer.setInterval(1000); // ms
connect(&pinUpdateTimer, &QTimer::timeout, this, &PortControl::updatePinLeds);
loadPortList();
loadBaudRateList();
ui->cbBaudRate->setCurrentIndex(ui->cbBaudRate->findText("9600"));
PortControl::~PortControl()
void PortControl::loadPortList()
QString currentSelection = ui->cbPortList->currentData(PortNameRole).toString();
portList.loadPortList();
int index = portList.indexOf(currentSelection);
if (index >= 0)
ui->cbPortList->setCurrentIndex(index);
tbPortList.setCurrentIndex(index);
void PortControl::loadBaudRateList()
ui->cbBaudRate->clear();
for (auto baudRate : QSerialPortInfo::standardBaudRates())
ui->cbBaudRate->addItem(QString::number(baudRate));
void PortControl::selectBaudRate(QString baudRate)
void PortControl::_selectBaudRate(QString baudRate)
if (!serialPort->setBaudRate(baudRate.toInt()))
qCritical() << "Can't set baud rate!";
void PortControl::selectParity(int parity)
if(!serialPort->setParity((QSerialPort::Parity) parity))
qCritical() << "Can't set parity option!";
void PortControl::selectDataBits(int dataBits)
if(!serialPort->setDataBits((QSerialPort::DataBits) dataBits))
qCritical() << "Can't set numer of data bits!";
void PortControl::selectStopBits(int stopBits)
if(!serialPort->setStopBits((QSerialPort::StopBits) stopBits))
qCritical() << "Can't set number of stop bits!";
void PortControl::selectFlowControl(int flowControl)
if(!serialPort->setFlowControl((QSerialPort::FlowControl) flowControl))
qCritical() << "Can't set flow control option!";
void PortControl::togglePort()
pinUpdateTimer.stop();
serialPort->close();
qDebug() << "Closed port:" << serialPort->portName();
emit portToggled(false);
// we get the port name from the edit text, which may not be
// in the portList if user hasn't pressed Enter
// Also note that, portText may not be the `portName`
QString portText = ui->cbPortList->currentText();
QString portName;
int portIndex = portList.indexOf(portText);
if (portIndex < 0) // not in list, add to model and update the selections
portList.appendRow(new PortListItem(portText));
ui->cbPortList->setCurrentIndex(portList.rowCount()-1);
tbPortList.setCurrentIndex(portList.rowCount()-1);
portName = portText;
// get the port name from the data field
portName = static_cast<PortListItem*>(portList.item(portIndex))->portName();
serialPort->setPortName(ui->cbPortList->currentData(PortNameRole).toString());
// open port
if (serialPort->open(QIODevice::ReadWrite))
// set port settings
selectBaudRate(ui->cbBaudRate->currentText());
_selectBaudRate(ui->cbBaudRate->currentText());
selectParity((QSerialPort::Parity) parityButtons.checkedId());
selectDataBits((QSerialPort::DataBits) dataBitsButtons.checkedId());
selectStopBits((QSerialPort::StopBits) stopBitsButtons.checkedId());
selectFlowControl((QSerialPort::FlowControl) flowControlButtons.checkedId());
// set output signals
// update pin signals
updatePinLeds();
pinUpdateTimer.start();
qDebug() << "Opened port:" << serialPort->portName();
emit portToggled(true);
openAction.setChecked(serialPort->isOpen());
void PortControl::selectPort(QString portName)
void PortControl::selectListedPort(QString portName)
// portName may be coming from combobox
portName = portName.split(" ")[0];
QSerialPortInfo portInfo(portName);
if (portInfo.isNull())
qWarning() << "Device doesn't exists:" << portName;
// has selection actually changed
if (portName != serialPort->portName())
// if another port is already open, close it by toggling
togglePort();
// open new selection by toggling
QString PortControl::selectedPortName()
if (portIndex < 0) // not in the list yet
// return the displayed name as port name
return portText;
// get the port name from the 'port list'
return static_cast<PortListItem*>(portList.item(portIndex))->portName();
QToolBar* PortControl::toolBar()
return &portToolBar;
void PortControl::openActionTriggered(bool checked)
void PortControl::onCbPortListActivated(int index)
void PortControl::onTbPortListActivated(int index)
void PortControl::onPortError(QSerialPort::SerialPortError error)
#ifdef Q_OS_UNIX
// For suppressing "Invalid argument" errors that happens with pseudo terminals
auto isPtsInvalidArgErr = [this] () -> bool {
return serialPort->portName().contains("pts/") && serialPort->errorString().contains("Invalid argument");
switch(error)
case QSerialPort::NoError :
case QSerialPort::ResourceError :
qWarning() << "Port error: resource unavaliable; most likely device removed.";
qWarning() << "Closing port on resource error: " << serialPort->portName();
case QSerialPort::DeviceNotFoundError:
qCritical() << "Device doesn't exists: " << serialPort->portName();
case QSerialPort::PermissionError:
qCritical() << "Permission denied. Either you don't have \
required privileges or device is already opened by another process.";
case QSerialPort::OpenError:
qWarning() << "Device is already opened!";
case QSerialPort::NotOpenError:
qCritical() << "Device is not open!";
case QSerialPort::ParityError:
qCritical() << "Parity error detected.";
case QSerialPort::FramingError:
qCritical() << "Framing error detected.";
case QSerialPort::BreakConditionError:
qCritical() << "Break condition is detected.";
case QSerialPort::WriteError:
qCritical() << "An error occurred while writing data.";
case QSerialPort::ReadError:
qCritical() << "An error occurred while reading data.";
case QSerialPort::UnsupportedOperationError:
// Qt 5.5 gives "Invalid argument" with 'UnsupportedOperationError'
if (isPtsInvalidArgErr())
qCritical() << "Operation is not supported.";
case QSerialPort::TimeoutError:
qCritical() << "A timeout error occurred.";
case QSerialPort::UnknownError:
// Qt 5.2 gives "Invalid argument" with 'UnknownError'
qCritical() << "Unknown error! Error: " << serialPort->errorString();
default:
qCritical() << "Unhandled port error: " << error;
void PortControl::updatePinLeds(void)
auto pins = serialPort->pinoutSignals();
ui->ledDCD->setOn(pins & QSerialPort::DataCarrierDetectSignal);
ui->ledDSR->setOn(pins & QSerialPort::DataSetReadySignal);
ui->ledRI->setOn(pins & QSerialPort::RingIndicatorSignal);
ui->ledCTS->setOn(pins & QSerialPort::ClearToSendSignal);
QString PortControl::currentParityText()
return paritySettingMap.value(
(QSerialPort::Parity) parityButtons.checkedId());
QString PortControl::currentFlowControlText()
if (flowControlButtons.checkedId() == QSerialPort::HardwareControl)
return "hardware";
else if (flowControlButtons.checkedId() == QSerialPort::SoftwareControl)
return "software";
else // no parity
return "none";
int portIndex = portList.indexOfName(portName);
portList.appendRow(new PortListItem(portName));
portIndex = portList.rowCount()-1;
ui->cbPortList->setCurrentIndex(portIndex);
tbPortList.setCurrentIndex(portIndex);
selectListedPort(portName);
void PortControl::selectBaudrate(QString baudRate)
int baudRateIndex = ui->cbBaudRate->findText(baudRate);
if (baudRateIndex < 0)
ui->cbBaudRate->setCurrentText(baudRate);
ui->cbBaudRate->setCurrentIndex(baudRateIndex);
_selectBaudRate(baudRate);
void PortControl::openPort()
if (!serialPort->isOpen())
openAction.trigger();
void PortControl::saveSettings(QSettings* settings)
settings->beginGroup(SettingGroup_Port);
settings->setValue(SG_Port_SelectedPort, selectedPortName());
settings->setValue(SG_Port_BaudRate, ui->cbBaudRate->currentText());
settings->setValue(SG_Port_Parity, currentParityText());
settings->setValue(SG_Port_DataBits, dataBitsButtons.checkedId());
settings->setValue(SG_Port_StopBits, stopBitsButtons.checkedId());
settings->setValue(SG_Port_FlowControl, currentFlowControlText());
void PortControl::loadSettings(QSettings* settings)
// make sure the port is closed
if (serialPort->isOpen()) togglePort();
// set port name if it exists in the current list otherwise ignore
QString portName = settings->value(SG_Port_SelectedPort, QString()).toString();
if (!portName.isEmpty())
int index = portList.indexOfName(portName);
if (index > -1) ui->cbPortList->setCurrentIndex(index);
// load baud rate setting if it exists in baud rate list
QString baudSetting = settings->value(
SG_Port_BaudRate, ui->cbBaudRate->currentText()).toString();
int baudIndex = ui->cbBaudRate->findText(baudSetting);
if (baudIndex > -1) ui->cbBaudRate->setCurrentIndex(baudIndex);
// load parity setting
QString parityText =
settings->value(SG_Port_Parity, currentParityText()).toString();
QSerialPort::Parity paritySetting = paritySettingMap.key(
parityText, (QSerialPort::Parity) parityButtons.checkedId());
parityButtons.button(paritySetting)->setChecked(true);
// load number of bits
int dataBits = settings->value(SG_Port_Parity, dataBitsButtons.checkedId()).toInt();
int dataBits = settings->value(SG_Port_DataBits, dataBitsButtons.checkedId()).toInt();
if (dataBits >=5 && dataBits <= 8)
dataBitsButtons.button((QSerialPort::DataBits) dataBits)->setChecked(true);
// load stop bits
int stopBits = settings->value(SG_Port_StopBits, stopBitsButtons.checkedId()).toInt();
if (stopBits == QSerialPort::OneStop)
ui->rb1StopBit->setChecked(true);
else if (stopBits == QSerialPort::TwoStop)
ui->rb2StopBit->setChecked(true);
// load flow control
QString flowControlSetting =
settings->value(SG_Port_FlowControl, currentFlowControlText()).toString();
if (flowControlSetting == "hardware")
ui->rbHardwareControl->setChecked(true);
else if (flowControlSetting == "software")
ui->rbSoftwareControl->setChecked(true);
ui->rbNoFlowControl->setChecked(true);
#ifndef PORTCONTROL_H
#define PORTCONTROL_H
#include <QWidget>
#include <QStringList>
#include <QToolBar>
#include <QAction>
#include <QComboBox>
#include "portlist.h"
class PortControl;
class PortControl : public QWidget
explicit PortControl(QSerialPort* port, QWidget* parent = 0);
~PortControl();
QSerialPort* serialPort;
QToolBar* toolBar();
void selectPort(QString portName);
void selectBaudrate(QString baudRate);
void openPort();
/// Stores port settings into a `QSettings`
void saveSettings(QSettings* settings);
/// Loads port settings from a `QSettings`. If open serial port is closed.
void loadSettings(QSettings* settings);
Ui::PortControl *ui;
QButtonGroup parityButtons;
QButtonGroup dataBitsButtons;
QButtonGroup stopBitsButtons;
QButtonGroup flowControlButtons;
QToolBar portToolBar;
QAction openAction;
QAction loadPortListAction;
QComboBox tbPortList;
PortList portList;
/// Used to refresh pinout signal leds periodically
QTimer pinUpdateTimer;
/// Returns the currently selected (entered) "portName" in the UI
QString selectedPortName();
/// Returns currently selected parity as text to be saved in settings
QString currentParityText();
/// Returns currently selected flow control as text to be saved in settings
QString currentFlowControlText();
public slots:
void loadPortList();
void loadBaudRateList();
void togglePort();
void selectListedPort(QString portName);
void selectBaudRate(QString baudRate);
void _selectBaudRate(QString baudRate);
void selectParity(int parity); // parity must be one of QSerialPort::Parity
void selectDataBits(int dataBits); // bits must be one of QSerialPort::DataBits
void selectStopBits(int stopBits); // stopBits must be one of QSerialPort::StopBits
void selectFlowControl(int flowControl); // flowControl must be one of QSerialPort::FlowControl
void openActionTriggered(bool checked);
void onCbPortListActivated(int index);
void onTbPortListActivated(int index);
void onPortError(QSerialPort::SerialPortError error);
void updatePinLeds(void);
signals:
void portToggled(bool open);
#endif // PORTCONTROL_H
Status change: