Changeset - b0405ffbfd80
[Not reviewed]
snapshots
0 7 3
Hasan Yavuz Ă–ZDERYA - 10 years ago 2015-09-19 15:24:02
hy@ozderya.net
take snapshot button creates a new snapshot and opens its window
10 files changed with 218 insertions and 10 deletions:
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
@@ -7,110 +7,116 @@
 
# 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 2.8.11)
 

	
 
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)
 

	
 
# Find QWT or use static
 
set(QWT_USE_STATIC false CACHE BOOL "Use a static version of Qwt provided by user.")
 
set(QWT_STATIC_LIBRARY "" CACHE FILEPATH "Path to the static Qwt library, libqwt.a.")
 
set(QWT_STATIC_INCLUDE "" CACHE PATH "Path to the Qwt include directory when building Qwt static.")
 

	
 
if (QWT_USE_STATIC)
 
  set(QWT_LIBRARY ${QWT_STATIC_LIBRARY})
 
  set(QWT_INCLUDE_DIR ${QWT_STATIC_INCLUDE})
 
else (QWT_USE_STATIC)
 
  find_package(Qwt 6.1 REQUIRED)
 
endif (QWT_USE_STATIC)
 

	
 
# includes
 
include_directories(${QWT_INCLUDE_DIR})
 

	
 
# wrap UI and resource files
 
qt5_wrap_ui(UI_FILES mainwindow.ui portcontrol.ui about_dialog.ui)
 
qt5_wrap_ui(UI_FILES
 
  mainwindow.ui
 
  portcontrol.ui
 
  about_dialog.ui
 
  snapshotview.ui
 
  )
 
qt5_add_resources(RES_FILES misc/icons.qrc)
 

	
 
add_executable(${PROGRAM_NAME} WIN32
 
  main.cpp
 
  mainwindow.cpp
 
  portcontrol.cpp
 
  plot.cpp
 
  zoomer.cpp
 
  hidabletabwidget.cpp
 
  framebuffer.cpp
 
  scalepicker.cpp
 
  scalezoomer.cpp
 
  portlist.cpp
 
  snapshotview.cpp
 
  ${UI_FILES}
 
  ${RES_FILES}
 
  misc/windows_icon.rc
 
  )
 

	
 
# Use the Widgets module from Qt 5.
 
target_link_libraries(${PROGRAM_NAME} ${QWT_LIBRARY})
 
qt5_use_modules(${PROGRAM_NAME} Widgets SerialPort Svg)
 

	
 
# 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()
 

	
 
# version number
 
set(MAJOR_VERSION 0 CACHE INT "Program major version number.")
 
set(MINOR_VERSION 5 CACHE INT "Program minor version number.")
 
set(PATCH_VERSION 0 CACHE INT "Program patch version number.")
 
set(VERSION_STRING "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
 

	
 
# get revision number from mercurial
 
find_program(MERCURIAL hg)
 

	
 
if (MERCURIAL)
 
  execute_process(COMMAND ${MERCURIAL} id -i
 
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 
    RESULT_VARIABLE MERCURIAL_RESULT
 
    OUTPUT_VARIABLE VERSION_REVISION
 
    OUTPUT_STRIP_TRAILING_WHITESPACE)
 
  if(NOT MERCURIAL_RESULT EQUAL 0)
 
    set(VERSION_SCM_REVISION false)
 
  endif(NOT MERCURIAL_RESULT EQUAL 0)
 
endif (MERCURIAL)
 

	
 
if (NOT VERSION_REVISION)
 
  set(VERSION_REVISION "0")
 
endif (NOT VERSION_REVISION)
 

	
framebuffer.cpp
Show inline comments
 
/*
 
  Copyright © 2015 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 "framebuffer.h"
 

	
 
FrameBuffer::FrameBuffer(size_t size)
 
{
 
    _size = size;
 
    data = new double[_size]();
 
    headIndex = 0;
 

	
 
    _boundingRect.setCoords(0, 0, size, 0);
 
}
 

	
 
FrameBuffer::~FrameBuffer()
 
{
 
    delete data;
 
}
 

	
 
void FrameBuffer::resize(size_t size)
 
{
 
    int offset = size - _size;
 
    if (offset == 0) return;
 

	
 
    double* newData = new double[size];
 

	
 
    // move data to new array
 
    int fill_start = offset > 0 ? offset : 0;
 

	
 
    for (int i = fill_start; i < int(size); i++)
 
    {
 
        newData[i] = _sample(i - offset);
 
        newData[i] = sampleY(i - offset);
 
    }
 

	
 
    // fill the beginning of the new data
 
    if (fill_start > 0)
 
    {
 
        for (int i = 0; i < fill_start; i++)
 
        {
 
            newData[i] = 0;
 
        }
 
    }
 

	
 
    // data is ready, clean and re-point
 
    delete data;
 
    data = newData;
 
    headIndex = 0;
 
    _size = size;
 

	
 
    // update the bounding rectangle
 
    _boundingRect.setRight(_size);
 
}
 

	
 
void FrameBuffer::addSamples(double* samples, size_t size)
 
{
 
    unsigned shift = size;
 
    if (shift < _size)
 
    {
 
        unsigned x = _size - headIndex; // distance of `head` to end
 

	
 
        if (shift <= x) // there is enough room at the end of array
 
        {
 
            for (size_t i = 0; i < shift; i++)
 
            {
 
                data[i+headIndex] = samples[i];
 
            }
 

	
 
            if (shift == x) // we used all the room at the end
 
            {
 
                headIndex = 0;
 
            }
 
            else
 
            {
 
                headIndex += shift;
 
            }
 
        }
 
        else // there isn't enough room
 
        {
 
            for (size_t i = 0; i < x; i++) // fill the end part
 
            {
 
@@ -98,62 +98,62 @@ void FrameBuffer::addSamples(double* sam
 
            }
 
            for (size_t i = 0; i < (shift-x); i++) // continue from the beginning
 
            {
 
                data[i] = samples[i+x];
 
            }
 
            headIndex = shift-x;
 
        }
 
    }
 
    else // number of new samples equal or bigger than current size
 
    {
 
        int x = shift - _size;
 
        for (size_t i = 0; i < _size; i++)
 
        {
 
            data[i] = samples[i+x];
 
        }
 
        headIndex = 0;
 
    }
 

	
 
    // update bounding rectangle
 
    double minValue = 0;
 
    double maxValue = 0;
 
    for (size_t i = 0; i < _size; i++)
 
    {
 
        if (data[i] > maxValue)
 
        {
 
            maxValue = data[i];
 
        }
 
        else if (data[i] < minValue)
 
        {
 
            minValue = data[i];
 
        }
 
    }
 
    _boundingRect.setTop(minValue);
 
    _boundingRect.setBottom(maxValue);
 
}
 

	
 
void FrameBuffer::clear()
 
{
 
    for (size_t i=0; i < _size; i++) data[i] = 0.;
 
}
 

	
 
size_t FrameBuffer::size() const
 
{
 
    return _size;
 
}
 

	
 
QPointF FrameBuffer::sample(size_t i) const
 
{
 
    return QPointF(i, _sample(i));
 
    return QPointF(i, sampleY(i));
 
}
 

	
 
QRectF FrameBuffer::boundingRect() const
 
{
 
    return _boundingRect;
 
}
 

	
 
double FrameBuffer::_sample(size_t i) const
 
double FrameBuffer::sampleY(size_t i) const
 
{
 
    size_t index = headIndex + i;
 
    if (index >= _size) index -= _size;
 
    return data[index];
 
}
framebuffer.h
Show inline comments
 
/*
 
  Copyright © 2015 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/>.
 
*/
 

	
 
#ifndef FRAMEBUFFER_H
 
#define FRAMEBUFFER_H
 

	
 
#include <qwt_series_data.h>
 
#include <QPointF>
 
#include <QRectF>
 

	
 
class FrameBuffer : public QwtSeriesData<QPointF>
 
{
 
public:
 
    FrameBuffer(size_t size);
 
    ~FrameBuffer();
 

	
 
    void resize(size_t size);
 
    void addSamples(double* samples, size_t size);
 
    void clear(); // fill 0
 

	
 
    // QwtSeriesData implementations
 
    size_t size() const;
 
    QPointF sample(size_t i) const;
 
    double sampleY(size_t i) const;
 
    QRectF boundingRect() const;
 

	
 
private:
 
    size_t _size; // size of `data`
 
    double* data;
 
    size_t headIndex; // indicates the actual `0` index of the ring buffer
 

	
 
    QRectF _boundingRect;
 

	
 
    double _sample(size_t i) const;
 
};
 

	
 
#endif // FRAMEBUFFER_H
mainwindow.cpp
Show inline comments
 
/*
 
  Copyright © 2015 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 "mainwindow.h"
 
#include "ui_mainwindow.h"
 
#include <QByteArray>
 
#include <QApplication>
 
#include <QFileDialog>
 
#include <QFile>
 
#include <QTextStream>
 
#include <QtDebug>
 
#include <QtEndian>
 
#include <qwt_plot.h>
 
#include <limits.h>
 
#include <cmath>
 
#include <iostream>
 

	
 
#include <snapshotview.h>
 

	
 
#include "utils.h"
 
#include "version.h"
 
#include "floatswap.h"
 

	
 
#if defined(Q_OS_WIN) && defined(QT_STATIC)
 
#include <QtPlugin>
 
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
 
#endif
 

	
 
struct Range
 
{
 
    double rmin;
 
    double rmax;
 
};
 

	
 
Q_DECLARE_METATYPE(Range);
 

	
 
MainWindow::MainWindow(QWidget *parent) :
 
    QMainWindow(parent),
 
    ui(new Ui::MainWindow),
 
    portControl(&serialPort)
 
{
 
    ui->setupUi(this);
 
    ui->tabWidget->insertTab(0, &portControl, "Port");
 
    ui->tabWidget->setCurrentIndex(0);
 
    addToolBar(portControl.toolBar());
 

	
 
    setupAboutDialog();
 

	
 
    // init UI signals
 

	
 
    // menu signals
 
    QObject::connect(ui->actionHelpAbout, &QAction::triggered,
 
              &aboutDialog, &QWidget::show);
 

	
 
    QObject::connect(ui->actionExportCsv, &QAction::triggered,
 
                     this, &MainWindow::onExportCsv);
 

	
 
    QObject::connect(ui->actionQuit, &QAction::triggered,
 
                     this, &MainWindow::close);
 

	
 
    QObject::connect(ui->actionGrid, &QAction::toggled, [this](bool show)
 
                     {
 
                         ui->plot->showGrid(show);
 
                         ui->actionMinorGrid->setEnabled(show);
 
                     });
 

	
 
    ui->actionMinorGrid->setEnabled(ui->actionGrid->isChecked());
 
    QObject::connect(ui->actionMinorGrid, &QAction::toggled,
 
                     ui->plot, &Plot::showMinorGrid);
 

	
 
    QObject::connect(ui->actionUnzoom, &QAction::triggered,
 
                     ui->plot, &Plot::unzoom);
 

	
 
    QObject::connect(ui->actionDarkBackground, &QAction::toggled,
 
                     ui->plot, &Plot::darkBackground);
 

	
 
    ui->plot->darkBackground(ui->actionDarkBackground->isChecked());
 

	
 
    // port control signals
 
    QObject::connect(&portControl, &PortControl::portToggled,
 
                     this, &MainWindow::onPortToggled);
 

	
 
    QObject::connect(&portControl, &PortControl::skipByteRequested,
 
                     this, &MainWindow::skipByte);
 

	
 
    QObject::connect(ui->spNumOfSamples, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged),
 
                     this, &MainWindow::onNumOfSamplesChanged);
 

	
 
    QObject::connect(ui->cbAutoScale, &QCheckBox::toggled,
 
                     this, &MainWindow::onAutoScaleChecked);
 

	
 
    QObject::connect(ui->spYmin, SIGNAL(valueChanged(double)),
 
                     this, SLOT(onYScaleChanged()));
 

	
 
    QObject::connect(ui->spYmax, SIGNAL(valueChanged(double)),
 
                     this, SLOT(onYScaleChanged()));
 

	
 
    QObject::connect(ui->actionClear, SIGNAL(triggered(bool)),
 
                     this, SLOT(clearPlot()));
 

	
 
    QObject::connect(ui->actionSnapShot, SIGNAL(triggered(bool)),
 
                     this, SLOT(takeSnapShot()));
 

	
 
    // setup number of channels spinbox
 
    QObject::connect(ui->spNumOfChannels,
 
                     SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged),
 
                     this, &MainWindow::onNumOfChannelsChanged);
 

	
 
    // setup number format buttons
 
    numberFormatButtons.addButton(ui->rbUint8,  NumberFormat_uint8);
 
    numberFormatButtons.addButton(ui->rbUint16, NumberFormat_uint16);
 
    numberFormatButtons.addButton(ui->rbUint32, NumberFormat_uint32);
 
    numberFormatButtons.addButton(ui->rbInt8,   NumberFormat_int8);
 
    numberFormatButtons.addButton(ui->rbInt16,  NumberFormat_int16);
 
    numberFormatButtons.addButton(ui->rbInt32,  NumberFormat_int32);
 
    numberFormatButtons.addButton(ui->rbFloat,  NumberFormat_float);
 
    numberFormatButtons.addButton(ui->rbASCII,  NumberFormat_ASCII);
 

	
 
    QObject::connect(&numberFormatButtons, SIGNAL(buttonToggled(int, bool)),
 
                     this, SLOT(onNumberFormatButtonToggled(int, bool)));
 

	
 
    // init port signals
 
    QObject::connect(&(this->serialPort), SIGNAL(error(QSerialPort::SerialPortError)),
 
                     this, SLOT(onPortError(QSerialPort::SerialPortError)));
 

	
 
    // set limits for axis limit boxes
 
    ui->spYmin->setRange((-1) * std::numeric_limits<double>::max(),
 
                         std::numeric_limits<double>::max());
 

	
 
    ui->spYmax->setRange((-1) * std::numeric_limits<double>::max(),
 
                         std::numeric_limits<double>::max());
 

	
 
    // init data arrays and plot
 

	
 
    numOfSamples = ui->spNumOfSamples->value();
 
    numOfChannels = ui->spNumOfChannels->value();
 

	
 
    // init channel data and curve list
 
    for (unsigned int i = 0; i < numOfChannels; i++)
 
    {
 
        channelBuffers.append(new FrameBuffer(numOfSamples));
 
        curves.append(new QwtPlotCurve());
 
        curves[i]->setSamples(channelBuffers[i]);
 
        curves[i]->setPen(makeColor(i));
 
        curves[i]->attach(ui->plot);
 
    }
 

	
 
    // init auto scale
 
    ui->plot->setAxis(ui->cbAutoScale->isChecked(),
 
                      ui->spYmin->value(), ui->spYmax->value());
 

	
 
@@ -196,96 +201,102 @@ MainWindow::MainWindow(QWidget *parent) 
 
    }
 
    else
 
    {
 
        selectNumberFormat(NumberFormat_uint8);
 
    }
 

	
 
    // Init sps (sample per second) counter
 
    sampleCount = 0;
 
    spsLabel.setText("0sps");
 
    spsLabel.setToolTip("samples per second (total of all channels)");
 
    ui->statusBar->addPermanentWidget(&spsLabel);
 
    spsTimer.start(SPS_UPDATE_TIMEOUT * 1000);
 
    QObject::connect(&spsTimer, &QTimer::timeout,
 
                     this, &MainWindow::spsTimerTimeout);
 

	
 
    // Init demo mode
 
    demoCount = 0;
 
    demoTimer.setInterval(100);
 
    QObject::connect(&demoTimer, &QTimer::timeout,
 
                     this, &MainWindow::demoTimerTimeout);
 
    QObject::connect(ui->actionDemoMode, &QAction::toggled,
 
                     this, &MainWindow::enableDemo);
 

	
 
    {   // init demo indicator
 
        QwtText demoText(" DEMO RUNNING ");  // looks better with spaces
 
        demoText.setColor(QColor("white"));
 
        demoText.setBackgroundBrush(Qt::darkRed);
 
        demoText.setBorderRadius(4);
 
        demoText.setRenderFlags(Qt::AlignLeft | Qt::AlignTop);
 
        demoIndicator.setText(demoText);
 
        demoIndicator.hide();
 
        demoIndicator.attach(ui->plot);
 
    }
 

	
 
}
 

	
 
MainWindow::~MainWindow()
 
{
 
    for (auto curve : curves)
 
    {
 
        // also deletes respective FrameBuffer
 
        delete curve;
 
    }
 

	
 
    if (serialPort.isOpen())
 
    {
 
        serialPort.close();
 
    }
 

	
 
    for (auto snapshot : snapshots)
 
    {
 
        delete snapshot;
 
    }
 

	
 
    delete ui;
 
    ui = NULL; // we check if ui is deleted in messageHandler
 
}
 

	
 
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::onDataReady()
 
{
 
    if (ui->actionPause->isChecked())
 
    {
 
        serialPort.clear(QSerialPort::Input);
 
        return;
 
    }
 

	
 
    // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...}
 
    int packageSize = sampleSize * numOfChannels;
 
    int bytesAvailable = serialPort.bytesAvailable();
 

	
 
    if (bytesAvailable > 0 && skipByteRequested)
 
    {
 
        serialPort.read(1);
 
        skipByteRequested = false;
 
        bytesAvailable--;
 
    }
 

	
 
    if (bytesAvailable < packageSize) return;
 

	
 
    int numOfPackagesToRead =
 
        (bytesAvailable - (bytesAvailable % packageSize)) / packageSize;
 
@@ -700,48 +711,68 @@ void MainWindow::onExportCsv()
 

	
 
            for (unsigned int i = 0; i < numOfSamples; i++)
 
            {
 
                for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
                {
 
                    fileStream << channelBuffers[ci]->sample(i).y();
 
                    if (ci != numOfChannels-1) fileStream << ",";
 
                }
 
                fileStream << '\n';
 
            }
 
        }
 
        else
 
        {
 
            qCritical() << "File open error during export: " << file.error();
 
        }
 
    }
 
}
 

	
 
void MainWindow::messageHandler(QtMsgType type,
 
                                const QMessageLogContext &context,
 
                                const QString &msg)
 
{
 
    QString logString;
 

	
 
    switch (type)
 
    {
 
        case QtDebugMsg:
 
            logString = "[Debug] " + msg;
 
            break;
 
        case QtWarningMsg:
 
            logString = "[Warning] " + msg;
 
            break;
 
        case QtCriticalMsg:
 
            logString = "[Error] " + msg;
 
            break;
 
        case QtFatalMsg:
 
            logString = "[Fatal] " + msg;
 
            break;
 
    }
 

	
 
    if (ui != NULL) ui->ptLog->appendPlainText(logString);
 
    std::cerr << logString.toStdString() << std::endl;
 

	
 
    if (type != QtDebugMsg && ui != NULL)
 
    {
 
        ui->statusBar->showMessage(msg, 5000);
 
    }
 
}
 

	
 
void MainWindow::takeSnapShot()
 
{
 
    qDebug() << "taking a snopshot yay!";
 
    auto snapShot = new SnapShot();
 
    snapShot->name = QString("SnapShot");
 

	
 
    for (unsigned ci = 0; ci < numOfChannels; ci++)
 
    {
 
        snapShot->data.append(QVector<QPointF>(numOfSamples));
 
        for (unsigned i = 0; i < numOfSamples; i++)
 
        {
 
            snapShot->data[ci][i] = channelBuffers[ci]->sample(i);
 
        }
 
    }
 
    snapshots.append(snapShot);
 

	
 
    auto sv = new SnapShotView(this, snapShot);
 
    sv->show();
 
}
mainwindow.h
Show inline comments
 
/*
 
  Copyright © 2015 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/>.
 
*/
 

	
 
#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 <QtGlobal>
 
#include <qwt_plot_curve.h>
 
#include <qwt_plot_textlabel.h>
 

	
 
#include "portcontrol.h"
 
#include "snapshotview.h"
 
#include "ui_about_dialog.h"
 
#include "framebuffer.h"
 

	
 
namespace Ui {
 
class MainWindow;
 
}
 

	
 
class MainWindow : public QMainWindow
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit MainWindow(QWidget *parent = 0);
 
    ~MainWindow();
 

	
 
    void messageHandler(QtMsgType type, const QMessageLogContext &context,
 
                        const QString &msg);
 

	
 
private:
 
    enum NumberFormat
 
    {
 
        NumberFormat_uint8,
 
        NumberFormat_uint16,
 
        NumberFormat_uint32,
 
        NumberFormat_int8,
 
        NumberFormat_int16,
 
        NumberFormat_int32,
 
        NumberFormat_float,
 
        NumberFormat_ASCII
 
    };
 

	
 
    Ui::MainWindow *ui;
 
    QButtonGroup numberFormatButtons;
 

	
 
    QDialog aboutDialog;
 
    void setupAboutDialog();
 

	
 
    QSerialPort serialPort;
 
    PortControl portControl;
 

	
 
    unsigned int numOfSamples;
 
    unsigned int numOfChannels;
 

	
 
    QList<QwtPlotCurve*> curves;
 
    // Note: FrameBuffer s are owned by their respective QwtPlotCurve s.
 
    QList<FrameBuffer*> channelBuffers;
 

	
 
    // `data` contains i th channels data
 
    void addChannelData(unsigned int channel, double* data, unsigned size);
 

	
 
    NumberFormat numberFormat;
 
    unsigned int sampleSize; // number of bytes in the selected number format
 
    double (MainWindow::*readSample)();
 

	
 
    // note that serialPort should already have enough bytes present
 
    template<typename T> double readSampleAs();
 

	
 
    bool skipByteRequested;
 

	
 
    const int SPS_UPDATE_TIMEOUT = 1;  // second
 
    QLabel spsLabel;
 
    unsigned int sampleCount;
 
    QTimer spsTimer;
 

	
 
    // snapshots
 
    QList<SnapShot*> snapshots;
 

	
 
    // demo
 
    QTimer demoTimer;
 
    int demoCount;
 
    bool isDemoRunning();
 
    QwtPlotTextLabel demoIndicator;
 

	
 
    QColor makeColor(unsigned int channelIndex);
 

	
 
private slots:
 
    void onPortToggled(bool open);
 
    void onDataReady();      // used with binary number formats
 
    void onDataReadyASCII(); // used with ASCII number format
 
    void onPortError(QSerialPort::SerialPortError error);
 

	
 
    void skipByte();
 

	
 
    void onNumOfSamplesChanged(int value);
 
    void onAutoScaleChecked(bool checked);
 
    void onYScaleChanged();
 
    void onRangeSelected();
 

	
 
    void onNumOfChannelsChanged(int value);
 
    void onNumberFormatButtonToggled(int numberFormatId, bool checked);
 
    void selectNumberFormat(NumberFormat numberFormatId);
 

	
 
    void clearPlot();
 

	
 
    void spsTimerTimeout();
 

	
 
    void takeSnapShot();
 

	
 
    void demoTimerTimeout();
 
    void enableDemo(bool enabled);
 

	
 
    void onExportCsv();
 
};
 

	
 
#endif // MAINWINDOW_H
mainwindow.ui
Show inline comments
 
<?xml version="1.0" encoding="UTF-8"?>
 
<ui version="4.0">
 
 <class>MainWindow</class>
 
 <widget class="QMainWindow" name="MainWindow">
 
  <property name="geometry">
 
   <rect>
 
    <x>0</x>
 
    <y>0</y>
 
    <width>653</width>
 
    <height>534</height>
 
   </rect>
 
  </property>
 
  <property name="windowTitle">
 
   <string>SerialPlot</string>
 
  </property>
 
  <widget class="QWidget" name="centralWidget">
 
   <layout class="QVBoxLayout" name="verticalLayout_2">
 
    <item>
 
     <widget class="Plot" name="plot">
 
     <widget class="Plot" name="plot" native="true">
 
      <property name="sizePolicy">
 
       <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
 
        <horstretch>0</horstretch>
 
        <verstretch>0</verstretch>
 
       </sizepolicy>
 
      </property>
 
     </widget>
 
    </item>
 
    <item>
 
     <widget class="HidableTabWidget" name="tabWidget">
 
      <property name="sizePolicy">
 
       <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
 
        <horstretch>0</horstretch>
 
        <verstretch>0</verstretch>
 
       </sizepolicy>
 
      </property>
 
      <property name="minimumSize">
 
       <size>
 
        <width>0</width>
 
        <height>0</height>
 
       </size>
 
      </property>
 
      <property name="maximumSize">
 
       <size>
 
        <width>16777215</width>
 
        <height>16777215</height>
 
       </size>
 
      </property>
 
      <property name="currentIndex">
 
       <number>0</number>
 
      </property>
 
      <property name="movable">
 
       <bool>false</bool>
 
      </property>
 
      <widget class="QWidget" name="tabDataFormat">
 
       <attribute name="title">
 
        <string>Data Format</string>
 
       </attribute>
 
       <layout class="QHBoxLayout" name="horizontalLayout_5">
 
        <item>
 
         <layout class="QFormLayout" name="formLayout_3">
 
          <item row="0" column="0">
 
           <widget class="QLabel" name="label_4">
 
            <property name="text">
 
             <string>Number Of Channels:</string>
 
            </property>
 
           </widget>
 
          </item>
 
@@ -456,96 +456,97 @@
 
   </layout>
 
  </widget>
 
  <widget class="QMenuBar" name="menuBar">
 
   <property name="geometry">
 
    <rect>
 
     <x>0</x>
 
     <y>0</y>
 
     <width>653</width>
 
     <height>27</height>
 
    </rect>
 
   </property>
 
   <widget class="QMenu" name="menuHelp">
 
    <property name="title">
 
     <string>Help</string>
 
    </property>
 
    <addaction name="actionDemoMode"/>
 
    <addaction name="actionHelpAbout"/>
 
   </widget>
 
   <widget class="QMenu" name="menuFile">
 
    <property name="title">
 
     <string>File</string>
 
    </property>
 
    <addaction name="actionExportCsv"/>
 
    <addaction name="separator"/>
 
    <addaction name="actionQuit"/>
 
   </widget>
 
   <widget class="QMenu" name="menuView">
 
    <property name="title">
 
     <string>View</string>
 
    </property>
 
    <addaction name="actionGrid"/>
 
    <addaction name="actionMinorGrid"/>
 
    <addaction name="actionUnzoom"/>
 
    <addaction name="actionDarkBackground"/>
 
   </widget>
 
   <addaction name="menuFile"/>
 
   <addaction name="menuView"/>
 
   <addaction name="menuHelp"/>
 
  </widget>
 
  <widget class="QToolBar" name="mainToolBar">
 
   <attribute name="toolBarArea">
 
    <enum>TopToolBarArea</enum>
 
   </attribute>
 
   <attribute name="toolBarBreak">
 
    <bool>false</bool>
 
   </attribute>
 
   <addaction name="actionPause"/>
 
   <addaction name="actionClear"/>
 
   <addaction name="actionSnapShot"/>
 
  </widget>
 
  <widget class="QStatusBar" name="statusBar"/>
 
  <action name="actionPause">
 
   <property name="checkable">
 
    <bool>true</bool>
 
   </property>
 
   <property name="checked">
 
    <bool>false</bool>
 
   </property>
 
   <property name="text">
 
    <string>Pause</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Pause Plotting</string>
 
   </property>
 
   <property name="shortcut">
 
    <string>P</string>
 
   </property>
 
  </action>
 
  <action name="actionClear">
 
   <property name="text">
 
    <string>Clear</string>
 
   </property>
 
   <property name="shortcut">
 
    <string>Ctrl+K</string>
 
   </property>
 
  </action>
 
  <action name="actionHelpAbout">
 
   <property name="text">
 
    <string>About</string>
 
   </property>
 
  </action>
 
  <action name="actionDemoMode">
 
   <property name="checkable">
 
    <bool>true</bool>
 
   </property>
 
   <property name="text">
 
    <string>Demo Mode</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Toggle Demo Mode</string>
 
   </property>
 
  </action>
 
  <action name="actionExportCsv">
 
   <property name="text">
 
    <string>Export CSV</string>
 
   </property>
 
   <property name="toolTip">
 
@@ -559,67 +560,78 @@
 
   <property name="shortcut">
 
    <string>Ctrl+Q</string>
 
   </property>
 
  </action>
 
  <action name="actionGrid">
 
   <property name="checkable">
 
    <bool>true</bool>
 
   </property>
 
   <property name="text">
 
    <string>Grid</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Show/hide grid</string>
 
   </property>
 
   <property name="shortcut">
 
    <string>G</string>
 
   </property>
 
  </action>
 
  <action name="actionUnzoom">
 
   <property name="text">
 
    <string>UnZoom</string>
 
   </property>
 
  </action>
 
  <action name="actionMinorGrid">
 
   <property name="checkable">
 
    <bool>true</bool>
 
   </property>
 
   <property name="text">
 
    <string>Minor Grid</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Show/hide minor grid</string>
 
   </property>
 
   <property name="shortcut">
 
    <string>M</string>
 
   </property>
 
  </action>
 
  <action name="actionDarkBackground">
 
   <property name="checkable">
 
    <bool>true</bool>
 
   </property>
 
   <property name="text">
 
    <string>Dark Background</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Toggle Dark Background</string>
 
   </property>
 
  </action>
 
  <action name="actionSnapShot">
 
   <property name="text">
 
    <string>SnapShot</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Take a snapshot of the current plot (F5)</string>
 
   </property>
 
   <property name="shortcut">
 
    <string>F5</string>
 
   </property>
 
  </action>
 
 </widget>
 
 <layoutdefault spacing="6" margin="11"/>
 
 <customwidgets>
 
  <customwidget>
 
   <class>Plot</class>
 
   <extends>QFrame</extends>
 
   <extends>QWidget</extends>
 
   <header>plot.h</header>
 
   <container>1</container>
 
  </customwidget>
 
  <customwidget>
 
   <class>HidableTabWidget</class>
 
   <extends>QTabWidget</extends>
 
   <header>hidabletabwidget.h</header>
 
   <container>1</container>
 
  </customwidget>
 
 </customwidgets>
 
 <resources/>
 
 <connections/>
 
</ui>
serialplot.pro
Show inline comments
 
#
 
# Copyright © 2015 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/>.
 
#
 

	
 
#-------------------------------------------------
 
#
 
# Project created by QtCreator 2015-03-04T08:20:06
 
#
 
#-------------------------------------------------
 

	
 
QT       += core gui serialport
 

	
 
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 

	
 
TARGET = serialplot
 
TEMPLATE = app
 

	
 
CONFIG += qwt
 

	
 

	
 
SOURCES += main.cpp\
 
        mainwindow.cpp \
 
    portcontrol.cpp \
 
    plot.cpp \
 
    zoomer.cpp \
 
    hidabletabwidget.cpp \
 
    framebuffer.cpp \
 
    scalepicker.cpp \
 
    scalezoomer.cpp \
 
    portlist.cpp
 
    portlist.cpp \
 
    snapshotview.cpp
 

	
 
HEADERS  += mainwindow.h \
 
    utils.h \
 
    portcontrol.h \
 
    floatswap.h \
 
    plot.h \
 
    zoomer.h \
 
    hidabletabwidget.h \
 
    framebuffer.h \
 
    scalepicker.h \
 
    scalezoomer.h \
 
    portlist.h
 
    portlist.h \
 
    snapshotview.h
 

	
 
FORMS    += mainwindow.ui \
 
    about_dialog.ui \
 
    portcontrol.ui
 
    portcontrol.ui \
 
    snapshotview.ui
 

	
 
INCLUDEPATH += qmake/
 

	
 
CONFIG += c++11
 

	
 
RESOURCES += \
 
    misc/icons.qrc
snapshotview.cpp
Show inline comments
 
new file 100644
 
#include "snapshotview.h"
 
#include "ui_snapshotview.h"
 

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

	
 
    unsigned numOfChannels = snapShot->data.size();
 

	
 
    for (unsigned ci = 0; ci < numOfChannels; ci++)
 
    {
 
        QwtPlotCurve* curve = new QwtPlotCurve();
 
        curves.append(curve);
 
        curve->setSamples(snapShot->data[ci]);
 
        curve->attach(ui->plot);
 
    }
 

	
 
    _snapShot = snapShot;
 
}
 

	
 
SnapShotView::~SnapShotView()
 
{
 
    for (auto curve : curves)
 
    {
 
        delete curve;
 
    }
 
    delete ui;
 
}
snapshotview.h
Show inline comments
 
new file 100644
 
#ifndef SNAPSHOTVIEW_H
 
#define SNAPSHOTVIEW_H
 

	
 
#include <QMainWindow>
 
#include <QVector>
 
#include <QPointF>
 
#include <qwt_plot_curve.h>
 
#include "plot.h"
 

	
 
class SnapShotView;
 
struct SnapShot
 
{
 
    QString name;
 
    QVector<QVector<QPointF>> data;
 
    SnapShotView* view;
 
};
 

	
 
namespace Ui {
 
class SnapShotView;
 
}
 

	
 
class SnapShotView : public QMainWindow
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit SnapShotView(QWidget *parent, SnapShot* snapShot);
 
    ~SnapShotView();
 

	
 
private:
 
    Ui::SnapShotView *ui;
 
    QList<QwtPlotCurve*> curves;
 

	
 
    SnapShot* _snapShot;
 
};
 

	
 
#endif // SNAPSHOTVIEW_H
snapshotview.ui
Show inline comments
 
new file 100644
 
<?xml version="1.0" encoding="UTF-8"?>
 
<ui version="4.0">
 
 <class>SnapShotView</class>
 
 <widget class="QMainWindow" name="SnapShotView">
 
  <property name="geometry">
 
   <rect>
 
    <x>0</x>
 
    <y>0</y>
 
    <width>544</width>
 
    <height>449</height>
 
   </rect>
 
  </property>
 
  <property name="windowTitle">
 
   <string>MainWindow</string>
 
  </property>
 
  <widget class="QWidget" name="centralwidget">
 
   <layout class="QVBoxLayout" name="verticalLayout">
 
    <item>
 
     <widget class="Plot" name="plot" native="true"/>
 
    </item>
 
   </layout>
 
  </widget>
 
  <widget class="QMenuBar" name="menubar">
 
   <property name="geometry">
 
    <rect>
 
     <x>0</x>
 
     <y>0</y>
 
     <width>544</width>
 
     <height>27</height>
 
    </rect>
 
   </property>
 
   <widget class="QMenu" name="menuFile">
 
    <property name="title">
 
     <string>File</string>
 
    </property>
 
    <addaction name="actionExportCSV"/>
 
   </widget>
 
   <widget class="QMenu" name="menuView">
 
    <property name="title">
 
     <string>View</string>
 
    </property>
 
   </widget>
 
   <addaction name="menuFile"/>
 
   <addaction name="menuView"/>
 
  </widget>
 
  <widget class="QToolBar" name="toolBar">
 
   <property name="windowTitle">
 
    <string>toolBar</string>
 
   </property>
 
   <attribute name="toolBarArea">
 
    <enum>TopToolBarArea</enum>
 
   </attribute>
 
   <attribute name="toolBarBreak">
 
    <bool>false</bool>
 
   </attribute>
 
   <addaction name="actionExportCSV"/>
 
   <addaction name="actionDelete"/>
 
  </widget>
 
  <action name="actionExportCSV">
 
   <property name="text">
 
    <string>Export CSV</string>
 
   </property>
 
  </action>
 
  <action name="actionDelete">
 
   <property name="text">
 
    <string>Delete</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Delete this snapshot!</string>
 
   </property>
 
  </action>
 
 </widget>
 
 <customwidgets>
 
  <customwidget>
 
   <class>Plot</class>
 
   <extends>QWidget</extends>
 
   <header>plot.h</header>
 
   <container>1</container>
 
  </customwidget>
 
 </customwidgets>
 
 <resources/>
 
 <connections/>
 
</ui>
0 comments (0 inline, 0 general)