Changeset - c374f20e3844
[Not reviewed]
Merge scrollbar-exp
0 47 11
Hasan Yavuz ÖZDERYA - 8 years ago 2017-04-24 14:20:22
hy@ozderya.net
Merge with default
58 files changed with 2897 insertions and 429 deletions:
0 comments (0 inline, 0 general)
.hgtags
Show inline comments
 
@@ -9,3 +9,4 @@ b4d0a38444d31872633e474d89ffc15cd0fe42f0
 
27b0354ca2c5ea7b3870156417ce7e04e799bbf7 v0.7.1
 
fd5f1eb480ec372b49df58b497458de05c30057c v0.8.0
 
9c9a11cd15fd094e2b2b65dc51805fd8fd1d2460 v0.8.1
 
4cf9a1ee1f107a38e03dbe17c4f2882c43d827c9 v0.9.0
CMakeLists.txt
Show inline comments
 
#
 
# Copyright © 2015-2016 Hasan Yavuz Özderya
 
# Copyright © 2017 Hasan Yavuz Özderya
 
#
 
# This file is part of serialplot.
 
#
 
@@ -38,24 +38,28 @@ 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.")
 
# Find QWT or use static manually provided by user
 
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 (BUILD_QWT)
 
  include(BuildQwt)
 
else (BUILD_QWT)
 
  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)
 
    find_package(Qwt 6.1 REQUIRED)
 
endif (BUILD_QWT)
 

	
 
# If set, cmake will download QtColorWidgets over git, build and use it as a static library.
 
set(BUILD_QTCOLORWIDGETS true CACHE BOOL "Download and build QtColorWidgets library automatically.")
 
if (BUILD_QTCOLORWIDGETS)
 
  include(BuildQColorWidgets)
 
else ()
 
  find_package(QtColorWidgets REQUIRED)
 
endif ()
 

	
 
# includes
 
include_directories("./src" ${QWT_INCLUDE_DIR})
 
include_directories("./src"
 
  ${QWT_INCLUDE_DIR}
 
  ${QTCOLORWIDGETS_INCLUDE_DIRS}
 
  )
 

	
 
# flags
 
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QTCOLORWIDGETS_FLAGS}")
 

	
 
# wrap UI and resource files
 
qt5_wrap_ui(UI_FILES
 
@@ -67,6 +71,7 @@ qt5_wrap_ui(UI_FILES
 
  src/commandwidget.ui
 
  src/dataformatpanel.ui
 
  src/plotcontrolpanel.ui
 
  src/recordpanel.ui
 
  src/numberformatbox.ui
 
  src/endiannessbox.ui
 
  src/binarystreamreadersettings.ui
 
@@ -102,9 +107,12 @@ add_executable(${PROGRAM_NAME} WIN32
 
  src/commandedit.cpp
 
  src/dataformatpanel.cpp
 
  src/plotcontrolpanel.cpp
 
  src/recordpanel.cpp
 
  src/datarecorder.cpp
 
  src/tooltipfilter.cpp
 
  src/sneakylineedit.cpp
 
  src/channelmanager.cpp
 
  src/channelinfomodel.cpp
 
  src/framebufferseries.cpp
 
  src/numberformatbox.cpp
 
  src/endiannessbox.cpp
 
@@ -124,12 +132,19 @@ add_executable(${PROGRAM_NAME} WIN32
 
  )
 

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

	
 
if (BUILD_QWT)
 
  add_dependencies(${PROGRAM_NAME} QWT)
 
endif (BUILD_QWT)
 
endif ()
 

	
 
if (BUILD_QTCOLORWIDGETS)
 
  add_dependencies(${PROGRAM_NAME} QCW)
 
endif ()
 

	
 
# set compiler flags
 
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
 
@@ -146,12 +161,6 @@ 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 8 CACHE INT "Program minor version number.")
 
set(PATCH_VERSION 1 CACHE INT "Program patch version number.")
 
set(VERSION_STRING "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
 

	
 
# get revision number from mercurial
 
find_program(MERCURIAL hg)
 

	
 
@@ -171,9 +180,7 @@ if (NOT VERSION_REVISION)
 
endif (NOT VERSION_REVISION)
 

	
 
message("SCM revision: ${VERSION_REVISION}")
 

	
 
# configure version file
 
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
 
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVERSION_REVISION=\\\"${VERSION_REVISION}\\\" ")
 

	
 
# add make run target
 
add_custom_target(run
cmake/modules/BuildQColorWidgets.cmake
Show inline comments
 
new file 100644
 
#
 
# 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(ExternalProject)
 

	
 
ExternalProject_Add(QCW
 
  PREFIX qcw
 
  GIT_REPOSITORY https://github.com/mbasaglia/Qt-Color-Widgets
 
  PATCH_COMMAND patch -t -p1 -i ${CMAKE_CURRENT_LIST_DIR}/qt_5_2_moc_creation_namespace_fix.diff
 
  CMAKE_CACHE_ARGS "-DCMAKE_CXX_FLAGS:string=-D QTCOLORWIDGETS_STATICALLY_LINKED"
 
  UPDATE_COMMAND ""
 
  INSTALL_COMMAND "")
 

	
 
ExternalProject_Get_Property(QCW binary_dir source_dir)
 
set(QTCOLORWIDGETS_FLAGS "-D QTCOLORWIDGETS_STATICALLY_LINKED")
 
set(QTCOLORWIDGETS_LIBRARY ${binary_dir}/libColorWidgets-qt5.a)
 
set(QTCOLORWIDGETS_INCLUDE_DIR ${source_dir}/include)
 

	
 
set(QTCOLORWIDGETS_LIBRARIES ${QTCOLORWIDGETS_LIBRARY})
 
set(QTCOLORWIDGETS_INCLUDE_DIRS ${QTCOLORWIDGETS_INCLUDE_DIR})
cmake/modules/BuildQwt.cmake
Show inline comments
 
@@ -25,8 +25,11 @@ ExternalProject_Add(QWT
 
  # disable QwtDesigner plugin and enable static build
 
  PATCH_COMMAND sed -i -r -e "s/QWT_CONFIG\\s*\\+=\\s*QwtDesigner/#&/"
 
                          -e "s/QWT_CONFIG\\s*\\+=\\s*QwtDll/#&/"
 
                          -e "s/QWT_CONFIG\\s*\\+=\\s*QwtSvg/#&/"
 
                          -e "s/QWT_CONFIG\\s*\\+=\\s*QwtOpenGL/#&/"
 
						  -e "s|QWT_INSTALL_PREFIX\\s*=.*|QWT_INSTALL_PREFIX = <INSTALL_DIR>|"
 
							 <SOURCE_DIR>/qwtconfig.pri
 
                             <SOURCE_DIR>/qwtconfig.pri
 
  UPDATE_COMMAND ""
 
  CONFIGURE_COMMAND qmake <SOURCE_DIR>/qwt.pro
 
  )
 

	
cmake/modules/FindQtColorWidgets.cmake
Show inline comments
 
new file 100644
 
#
 
# 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/>.
 
#
 

	
 
# Note: this script is intended for the debian package created for serialplot.
 

	
 
find_library(QTCOLORWIDGETS_LIBRARY "libColorWidgets-qt5.a")
 
find_path(QTCOLORWIDGETS_INCLUDE_DIR "color_preview.hpp" PATHS "/usr/include/qtcolorwidgets/" NO_DEFAULT_PATH)
 

	
 
mark_as_advanced(QTCOLORWIDGETS_LIBRARY QTCOLORWIDGETS_INCLUDE_DIR)
 

	
 
include(FindPackageHandleStandardArgs)
 
find_package_handle_standard_args(QtColorWidgets DEFAULT_MSG QTCOLORWIDGETS_LIBRARY QTCOLORWIDGETS_INCLUDE_DIR)
 

	
 
if (QTCOLORWIDGETS_FOUND)
 
  set(QTCOLORWIDGETS_FLAGS "-D QTCOLORWIDGETS_STATICALLY_LINKED")
 
  mark_as_advanced(QTCOLORWIDGETS_FLAGS)
 
  set(QTCOLORWIDGETS_LIBRARIES ${QTCOLORWIDGETS_LIBRARY})
 
  set(QTCOLORWIDGETS_INCLUDE_DIRS ${QTCOLORWIDGETS_INCLUDE_DIR})
 
endif (QTCOLORWIDGETS_FOUND)
cmake/modules/FindQwt.cmake
Show inline comments
 
#
 
# Copyright © 2015 Hasan Yavuz Özderya
 
# Copyright © 2017 Hasan Yavuz Özderya
 
#
 
# This file is part of serialplot.
 
#
 
@@ -66,7 +66,7 @@ endif(qwt_roots)
 

	
 
if(QWT_ROOT)
 
  set(QWT_INCLUDE_DIR "${QWT_ROOT}/include")
 
  find_library(QWT_LIBRARY "qwt"
 
  find_library(QWT_LIBRARY "qwt-qt5"
 
	PATHS "${QWT_ROOT}/lib")
 
else (QWT_ROOT)
 
  ## Look into system locations
 
@@ -90,7 +90,7 @@ else (QWT_ROOT)
 
	endif(qwt_version_string)
 
  endif (QWT_INCLUDE_DIR)
 
  # look into system locations for lib file
 
  find_library(QWT_LIBRARY "qwt" PATHS /usr/lib)
 
  find_library(QWT_LIBRARY "qwt-qt5" PATHS /usr/lib)
 
endif(QWT_ROOT)
 

	
 
# set version variables
cmake/modules/qt_5_2_moc_creation_namespace_fix.diff
Show inline comments
 
new file 100644
 
diff --git a/include/color_dialog.hpp b/include/color_dialog.hpp
 
index 5c7653d..895215c 100644
 
--- a/include/color_dialog.hpp
 
+++ b/include/color_dialog.hpp
 
@@ -30,6 +30,8 @@
 
 
 
 class QAbstractButton;
 
 
 
+using namespace color_widgets;
 
+
 
 namespace color_widgets {
 
 
 
 class QCP_EXPORT ColorDialog : public QDialog
 
diff --git a/include/color_list_widget.hpp b/include/color_list_widget.hpp
 
index 282bea5..7d8e0c5 100644
 
--- a/include/color_list_widget.hpp
 
+++ b/include/color_list_widget.hpp
 
@@ -25,6 +25,8 @@
 
 #include "abstract_widget_list.hpp"
 
 #include "color_wheel.hpp"
 
 
 
+using namespace color_widgets;
 
+
 
 namespace color_widgets {
 
 
 
 class QCP_EXPORT ColorListWidget : public AbstractWidgetList
 
diff --git a/include/color_selector.hpp b/include/color_selector.hpp
 
index db817d5..48b374d 100644
 
--- a/include/color_selector.hpp
 
+++ b/include/color_selector.hpp
 
@@ -25,6 +25,8 @@
 
 #include "color_preview.hpp"
 
 #include "color_wheel.hpp"
 
 
 
+using namespace color_widgets;
 
+
 
 namespace color_widgets {
 
 
 
 /**
serialplot.pro
Show inline comments
 
@@ -67,7 +67,8 @@ SOURCES += \
 
    src/demoreader.cpp \
 
    src/framedreader.cpp \
 
    src/plotmanager.cpp \
 
    src/numberformat.cpp
 
    src/numberformat.cpp \
 
    src/recordpanel.cpp
 

	
 
HEADERS += \
 
    src/mainwindow.h \
 
@@ -106,7 +107,8 @@ HEADERS += \
 
    src/framedreader.h \
 
    src/plotmanager.h \
 
    src/setting_defines.h \
 
    src/numberformat.h
 
    src/numberformat.h \
 
    src/recordpanel.h
 

	
 
FORMS += \
 
    src/mainwindow.ui \
 
@@ -121,7 +123,8 @@ FORMS += \
 
    src/endiannessbox.ui \
 
    src/framedreadersettings.ui \
 
    src/binarystreamreadersettings.ui \
 
    src/asciireadersettings.ui
 
    src/asciireadersettings.ui \
 
    src/recordpanel.ui
 

	
 
INCLUDEPATH += qmake/ src/
 

	
src/abstractreader.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -19,11 +19,14 @@
 

	
 
#include "abstractreader.h"
 

	
 
AbstractReader::AbstractReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
 
AbstractReader::AbstractReader(QIODevice* device, ChannelManager* channelMan,
 
                               DataRecorder* recorder, QObject* parent) :
 
    QObject(parent)
 
{
 
    _device = device;
 
    _channelMan = channelMan;
 
    _recorder = recorder;
 
    recording = false;
 

	
 
    // initialize sps counter
 
    sampleCount = 0;
 
@@ -44,3 +47,13 @@ void AbstractReader::spsTimerTimeout()
 
    }
 
    sampleCount = 0;
 
}
 

	
 
void AbstractReader::addData(double* samples, unsigned length)
 
{
 
    _channelMan->addData(samples, length);
 
    if (recording)
 
    {
 
        _recorder->addData(samples, length, numOfChannels());
 
    }
 
    sampleCount += length;
 
}
src/abstractreader.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -26,6 +26,7 @@
 
#include <QTimer>
 

	
 
#include "channelmanager.h"
 
#include "datarecorder.h"
 

	
 
/**
 
 * All reader classes must inherit this class.
 
@@ -34,7 +35,10 @@ class AbstractReader : public QObject
 
{
 
    Q_OBJECT
 
public:
 
    explicit AbstractReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0);
 
    explicit AbstractReader(QIODevice* device, ChannelManager* channelMan,
 
                            DataRecorder* recorder, QObject* parent = 0);
 

	
 
    bool recording;                 /// is recording started
 

	
 
    /**
 
     * Returns a widget to be shown in data format panel when reader
 
@@ -54,11 +58,18 @@ public:
 
    /// 'disabled'.
 
    virtual void enable(bool enabled = true) = 0;
 

	
 
    /**
 
     * @brief Starts sending data to recorder.
 
     *
 
     * @note recorder must have been started!
 
     */
 
    void startRecording();
 

	
 
    /// Stops recording.
 
    void stopRecording();
 

	
 
signals:
 
    void numOfChannelsChanged(unsigned);
 
    // TODO: this must be signaled by 'channel man' for better abstraction
 
    void dataAdded(); ///< emitted when data added to channel man.
 
    // TODO: this should be a part of 'channel man'
 
    void samplesPerSecondChanged(unsigned);
 

	
 
public slots:
 
@@ -72,14 +83,18 @@ public slots:
 

	
 
protected:
 
    QIODevice* _device;
 
    ChannelManager* _channelMan;
 

	
 
    /// Implementing class should simply increase this count as samples are read
 
    unsigned sampleCount;
 
    /// Should be called with read data
 
    void addData(double* samples, unsigned length);
 

	
 
private:
 
    const int SPS_UPDATE_TIMEOUT = 1;  // second
 

	
 
    unsigned sampleCount;
 
    unsigned samplesPerSecond;
 

	
 
    ChannelManager* _channelMan;
 
    DataRecorder* _recorder;
 
    QTimer spsTimer;
 

	
 
private slots:
src/asciireader.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -24,12 +24,12 @@
 
/// If set to this value number of channels is determined from input
 
#define NUMOFCHANNELS_AUTO   (0)
 

	
 
AsciiReader::AsciiReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
 
    AbstractReader(device, channelMan, parent)
 
AsciiReader::AsciiReader(QIODevice* device, ChannelManager* channelMan,
 
                         DataRecorder* recorder, QObject* parent) :
 
    AbstractReader(device, channelMan, recorder, parent)
 
{
 
    paused = false;
 
    discardFirstLine = true;
 
    sampleCount = 0;
 

	
 
    _numOfChannels = _settingsWidget.numOfChannels();
 
    autoNumOfChannels = (_numOfChannels == NUMOFCHANNELS_AUTO);
 
@@ -139,24 +139,27 @@ void AsciiReader::onDataReady()
 
        {
 
            numReadChannels = separatedValues.length();
 
            qWarning() << "Incoming data is missing data for some channels!";
 
            qWarning() << "Read line: " << line;
 
        }
 

	
 
        // parse read line
 
        double* channelSamples = new double[_numOfChannels]();
 
        for (unsigned ci = 0; ci < numReadChannels; ci++)
 
        {
 
            bool ok;
 
            double channelSample = separatedValues[ci].toDouble(&ok);
 
            if (ok)
 
            {
 
                _channelMan->addChannelData(ci, &channelSample, 1);
 
                sampleCount++;
 
            }
 
            else
 
            channelSamples[ci] = separatedValues[ci].toDouble(&ok);
 
            if (!ok)
 
            {
 
                qWarning() << "Data parsing error for channel: " << ci;
 
                qWarning() << "Read line: " << line;
 
                channelSamples[ci] = 0;
 
            }
 
        }
 
        emit dataAdded();
 

	
 
        // commit data
 
        addData(channelSamples, _numOfChannels);
 

	
 
        delete[] channelSamples;
 
    }
 
}
 

	
src/asciireader.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -30,7 +30,8 @@ class AsciiReader : public AbstractReade
 
    Q_OBJECT
 

	
 
public:
 
    explicit AsciiReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0);
 
    explicit AsciiReader(QIODevice* device, ChannelManager* channelMan,
 
                         DataRecorder* recorder, QObject *parent = 0);
 
    QWidget* settingsWidget();
 
    unsigned numOfChannels();
 
    void enable(bool enabled = true);
src/binarystreamreader.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -23,13 +23,13 @@
 
#include "binarystreamreader.h"
 
#include "floatswap.h"
 

	
 
BinaryStreamReader::BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
 
    AbstractReader(device, channelMan, parent)
 
BinaryStreamReader::BinaryStreamReader(QIODevice* device, ChannelManager* channelMan,
 
                                       DataRecorder* recorder, QObject* parent) :
 
    AbstractReader(device, channelMan, recorder, parent)
 
{
 
    paused = false;
 
    skipByteRequested = false;
 
    skipSampleRequested = false;
 
    sampleCount = 0;
 

	
 
    _numOfChannels = _settingsWidget.numOfChannels();
 
    connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged,
 
@@ -171,15 +171,9 @@ void BinaryStreamReader::onDataReady()
 
        }
 
    }
 

	
 
    for (unsigned int ci = 0; ci < _numOfChannels; ci++)
 
    {
 
        addChannelData(ci,
 
                       channelSamples + ci*numOfPackagesToRead,
 
                       numOfPackagesToRead);
 
    }
 
    emit dataAdded();
 
    addData(channelSamples, numOfPackagesToRead*_numOfChannels);
 

	
 
    delete channelSamples;
 
    delete[] channelSamples;
 
}
 

	
 
template<typename T> double BinaryStreamReader::readSampleAs()
 
@@ -200,13 +194,6 @@ template<typename T> double BinaryStream
 
    return double(data);
 
}
 

	
 
void BinaryStreamReader::addChannelData(unsigned int channel,
 
                                        double* data, unsigned size)
 
{
 
    _channelMan->addChannelData(channel, data, size);
 
    sampleCount += size;
 
}
 

	
 
void BinaryStreamReader::saveSettings(QSettings* settings)
 
{
 
    _settingsWidget.saveSettings(settings);
src/binarystreamreader.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -34,7 +34,8 @@ class BinaryStreamReader : public Abstra
 
{
 
    Q_OBJECT
 
public:
 
    explicit BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0);
 
    explicit BinaryStreamReader(QIODevice* device, ChannelManager* channelMan,
 
                                DataRecorder* recorder, QObject *parent = 0);
 
    QWidget* settingsWidget();
 
    unsigned numOfChannels();
 
    void enable(bool enabled = true);
 
@@ -65,9 +66,6 @@ private:
 
     */
 
    template<typename T> double readSampleAs();
 

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

	
 
private slots:
 
    void onNumberFormatChanged(NumberFormat numberFormat);
 
    void onNumOfChannelsChanged(unsigned value);
src/channelinfomodel.cpp
Show inline comments
 
new file 100644
 
/*
 
  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 "channelinfomodel.h"
 
#include "setting_defines.h"
 

	
 
#define NUMOF_COLORS  (32)
 

	
 
const QColor colors[NUMOF_COLORS] =
 
{
 
    QColor("#ff0056"),
 
    QColor("#7e2dd2"),
 
    QColor("#00ae7e"),
 
    QColor("#fe8900"),
 
    QColor("#ff937e"),
 
    QColor("#6a826c"),
 
    QColor("#ff029d"),
 
    QColor("#00b917"),
 
    QColor("#7a4782"),
 
    QColor("#85a900"),
 
    QColor("#a42400"),
 
    QColor("#683d3b"),
 
    QColor("#bdc6ff"),
 
    QColor("#263400"),
 
    QColor("#bdd393"),
 
    QColor("#d5ff00"),
 
    QColor("#9e008e"),
 
    QColor("#001544"),
 
    QColor("#c28c9f"),
 
    QColor("#ff74a3"),
 
    QColor("#01d0ff"),
 
    QColor("#004754"),
 
    QColor("#e56ffe"),
 
    QColor("#788231"),
 
    QColor("#0e4ca1"),
 
    QColor("#91d0cb"),
 
    QColor("#be9970"),
 
    QColor("#968ae8"),
 
    QColor("#bb8800"),
 
    QColor("#43002c"),
 
    QColor("#deff74"),
 
    QColor("#00ffc6")
 
};
 

	
 
ChannelInfoModel::ChannelInfoModel(unsigned numberOfChannels, QObject* parent) :
 
    QAbstractTableModel(parent)
 
{
 
    _numOfChannels = 0;
 
    setNumOfChannels(numberOfChannels);
 
}
 

	
 
ChannelInfoModel::ChannelInfoModel(const ChannelInfoModel& other) :
 
    ChannelInfoModel(other.rowCount(), other.parent())
 
{
 
    for (int i = 0; i < other.rowCount(); i++)
 
    {
 
        setData(index(i, COLUMN_NAME),
 
                other.data(other.index(i, COLUMN_NAME), Qt::EditRole),
 
                Qt::EditRole);
 
        setData(index(i, COLUMN_NAME),
 
                other.data(other.index(i, COLUMN_NAME), Qt::ForegroundRole),
 
                Qt::ForegroundRole);
 
        setData(index(i, COLUMN_VISIBILITY),
 
                other.data(other.index(i, COLUMN_VISIBILITY), Qt::CheckStateRole),
 
                Qt::CheckStateRole);
 
    }
 
}
 

	
 
ChannelInfoModel::ChannelInfoModel(const QStringList& channelNames) :
 
    ChannelInfoModel(channelNames.length(), NULL)
 
{
 
    for (int i = 0; i < channelNames.length(); i++)
 
    {
 
        setData(index(i, COLUMN_NAME), channelNames[i], Qt::EditRole);
 
    }
 
}
 

	
 
ChannelInfoModel::ChannelInfo::ChannelInfo(unsigned index)
 
{
 
    name = tr("Channel %1").arg(index + 1);
 
    visibility = true;
 
    color = colors[index % NUMOF_COLORS];
 
}
 

	
 
QString ChannelInfoModel::name(unsigned i) const
 
{
 
    return infos[i].name;
 
}
 

	
 
QColor ChannelInfoModel::color(unsigned i) const
 
{
 
    return infos[i].color;
 
}
 

	
 
bool ChannelInfoModel::isVisible(unsigned i) const
 
{
 
    return infos[i].visibility;
 
}
 

	
 
QStringList ChannelInfoModel::channelNames() const
 
{
 
    QStringList r;
 
    for (unsigned ci = 0; ci < _numOfChannels; ci++)
 
    {
 
        r << name(ci);
 
    }
 
    return r;
 
}
 

	
 
int ChannelInfoModel::rowCount(const QModelIndex &parent) const
 
{
 
    return _numOfChannels;
 
}
 

	
 
int ChannelInfoModel::columnCount(const QModelIndex & parent) const
 
{
 
    return COLUMN_COUNT;
 
}
 

	
 
Qt::ItemFlags ChannelInfoModel::flags(const QModelIndex &index) const
 
{
 
    if (index.column() == COLUMN_NAME)
 
    {
 
        return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable;
 
    }
 
    else if (index.column() == COLUMN_VISIBILITY)
 
    {
 
        return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable;
 
    }
 

	
 
    return Qt::NoItemFlags;
 
}
 

	
 
QVariant ChannelInfoModel::data(const QModelIndex &index, int role) const
 
{
 
    // check index
 
    if (index.row() >= (int) _numOfChannels || index.row() < 0)
 
    {
 
        return QVariant();
 
    }
 

	
 
    // get color
 
    if (role == Qt::ForegroundRole)
 
    {
 
        return infos[index.row()].color;
 
    }
 

	
 
    // get name
 
    if (index.column() == COLUMN_NAME)
 
    {
 
        if (role == Qt::DisplayRole || role == Qt::EditRole)
 
        {
 
            return QVariant(infos[index.row()].name);
 
        }
 
    } // get visibility
 
    else if (index.column() == COLUMN_VISIBILITY)
 
    {
 
        if (role == Qt::CheckStateRole)
 
        {
 
            bool visible = infos[index.row()].visibility;
 
            return visible ? Qt::Checked : Qt::Unchecked;
 
        }
 
    }
 

	
 
    return QVariant();
 
}
 

	
 
QVariant ChannelInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
 
{
 
    if (orientation == Qt::Horizontal)
 
    {
 
        if (role == Qt::DisplayRole)
 
        {
 
            if (section == COLUMN_NAME)
 
            {
 
                return tr("Channel");
 
            }
 
            else if (section == COLUMN_VISIBILITY)
 
            {
 
                return tr("Visible");
 
            }
 
        }
 
    }
 
    else                        // vertical
 
    {
 
        if (section < (int) _numOfChannels && role == Qt::DisplayRole)
 
        {
 
            return QString::number(section + 1);
 
        }
 
    }
 

	
 
    return QVariant();
 
}
 

	
 
bool ChannelInfoModel::setData(const QModelIndex &index, const QVariant &value, int role)
 
{
 
    // check index
 
    if (index.row() >= (int) _numOfChannels || index.row() < 0)
 
    {
 
        return false;
 
    }
 

	
 
    // set color
 
    if (role == Qt::ForegroundRole)
 
    {
 
        infos[index.row()].color = value.value<QColor>();
 
        emit dataChanged(index, index, QVector<int>({Qt::ForegroundRole}));
 
        return true;
 
    }
 

	
 
    // set name
 
    if (index.column() == COLUMN_NAME)
 
    {
 
        if (role == Qt::DisplayRole || role == Qt::EditRole)
 
        {
 
            infos[index.row()].name = value.toString();
 
            emit dataChanged(index, index, QVector<int>({role}));
 
            return true;
 
        }
 
    } // set visibility
 
    else if (index.column() == COLUMN_VISIBILITY)
 
    {
 
        if (role == Qt::CheckStateRole)
 
        {
 
            bool checked = value.toInt() == Qt::Checked;
 
            infos[index.row()].visibility = checked;
 
            emit dataChanged(index, index, QVector<int>({role}));
 
            return true;
 
        }
 
    }
 

	
 
    // invalid index/role
 
    return false;
 
}
 

	
 
void ChannelInfoModel::setNumOfChannels(unsigned number)
 
{
 
    if (number == _numOfChannels) return;
 

	
 
    bool isInserting = number > _numOfChannels;
 
    if (isInserting)
 
    {
 
        beginInsertRows(QModelIndex(), _numOfChannels, number-1);
 
    }
 
    else
 
    {
 
        beginRemoveRows(QModelIndex(), number, _numOfChannels-1);
 
    }
 

	
 
    // we create channel info but never remove channel info to
 
    // remember user entered info
 
    if ((int) number > infos.length())
 
    {
 
        for (unsigned ci = _numOfChannels; ci < number; ci++)
 
        {
 
            infos.append(ChannelInfo(ci));
 
        }
 
    }
 

	
 
    // make sure newly available channels are visible, we don't
 
    // remember visibility option intentionally so that user doesn't
 
    // get confused
 
    if (number > _numOfChannels)
 
    {
 
        for (unsigned ci = _numOfChannels; ci < number; ci++)
 
        {
 
            infos[ci].visibility = true;
 
        }
 
    }
 

	
 
    _numOfChannels = number;
 

	
 
    if (isInserting)
 
    {
 
        endInsertRows();
 
    }
 
    else
 
    {
 
        endRemoveRows();
 
    }
 
}
 

	
 
void ChannelInfoModel::resetInfos()
 
{
 
    beginResetModel();
 
    for (unsigned ci = 0; (int) ci < infos.length(); ci++)
 
    {
 
        infos[ci] = ChannelInfo(ci);
 
    }
 
    endResetModel();
 
}
 

	
 
void ChannelInfoModel::resetNames()
 
{
 
    beginResetModel();
 
    for (unsigned ci = 0; (int) ci < infos.length(); ci++)
 
    {
 
        infos[ci].name = ChannelInfo(ci).name;
 
    }
 
    endResetModel();
 
}
 

	
 
void ChannelInfoModel::resetColors()
 
{
 
    beginResetModel();
 
    for (unsigned ci = 0; (int) ci < infos.length(); ci++)
 
    {
 
        infos[ci].color = ChannelInfo(ci).color;
 
    }
 
    endResetModel();
 
}
 

	
 
void ChannelInfoModel::resetVisibility()
 
{
 
    beginResetModel();
 
    for (unsigned ci = 0; (int) ci < infos.length(); ci++)
 
    {
 
        infos[ci].visibility = true;
 
    }
 
    endResetModel();
 
}
 

	
 
void ChannelInfoModel::saveSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Channels);
 
    settings->beginWriteArray(SG_Channels_Channel);
 

	
 
    // save all channel information regardless of current number of channels
 
    for (unsigned ci = 0; (int) ci < infos.length(); ci++)
 
    {
 
        settings->setArrayIndex(ci);
 
        settings->setValue(SG_Channels_Name, infos[ci].name);
 
        settings->setValue(SG_Channels_Color, infos[ci].color);
 
        settings->setValue(SG_Channels_Visible, infos[ci].visibility);
 
    }
 

	
 
    settings->endArray();
 
    settings->endGroup();
 
}
 

	
 
void ChannelInfoModel::loadSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Channels);
 
    unsigned size = settings->beginReadArray(SG_Channels_Channel);
 

	
 
    for (unsigned ci = 0; ci < size; ci++)
 
    {
 
        settings->setArrayIndex(ci);
 

	
 
        ChannelInfo chanInfo(ci);
 
        chanInfo.name       = settings->value(SG_Channels_Name, chanInfo.name).toString();
 
        chanInfo.color      = settings->value(SG_Channels_Color, chanInfo.color).value<QColor>();
 
        chanInfo.visibility = settings->value(SG_Channels_Visible, true).toBool();
 

	
 
        if ((int) ci < infos.size())
 
        {
 
            infos[ci] = chanInfo;
 

	
 
            if (ci < _numOfChannels)
 
            {
 
                auto roles = QVector<int>({
 
                    Qt::DisplayRole, Qt::EditRole, Qt::ForegroundRole, Qt::CheckStateRole});
 
                emit dataChanged(index(ci, 0), index(ci, COLUMN_COUNT-1), roles);
 
            }
 
        }
 
        else
 
        {
 
            infos.append(chanInfo);
 
        }
 
    }
 

	
 
    settings->endArray();
 
    settings->endGroup();
 
}
src/channelinfomodel.h
Show inline comments
 
new file 100644
 
/*
 
  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/>.
 
*/
 

	
 
#ifndef CHANNELINFOMODEL_H
 
#define CHANNELINFOMODEL_H
 

	
 
#include <QAbstractTableModel>
 
#include <QColor>
 
#include <QSettings>
 
#include <QStringList>
 

	
 
class ChannelInfoModel : public QAbstractTableModel
 
{
 
    Q_OBJECT
 

	
 
public:
 
    enum ChannelInfoColumn
 
    {
 
        COLUMN_NAME = 0,
 
        COLUMN_VISIBILITY,
 
        COLUMN_COUNT
 
    };
 

	
 
    explicit ChannelInfoModel(unsigned numberOfChannels, QObject *parent = 0);
 
    ChannelInfoModel(const ChannelInfoModel& other);
 
    explicit ChannelInfoModel(const QStringList& channelNames);
 

	
 
    QString name     (unsigned i) const;
 
    QColor  color    (unsigned i) const;
 
    bool    isVisible(unsigned i) const;
 
    /// Returns a list of channel names
 
    QStringList channelNames() const;
 

	
 
    // implemented from QAbstractItemModel
 
    int           rowCount(const QModelIndex &parent = QModelIndex()) const;
 
    int           columnCount(const QModelIndex &parent = QModelIndex()) const;
 
    QVariant      data(const QModelIndex &index, int role = Qt::DisplayRole) const;
 
    bool          setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
 
    Qt::ItemFlags flags(const QModelIndex &index) const;
 
    QVariant      headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
 

	
 
    void setNumOfChannels(unsigned number);
 
    /// Stores all channel info into a `QSettings`
 
    void saveSettings(QSettings* settings);
 
    /// Loads all channel info from a `QSettings`.
 
    void loadSettings(QSettings* settings);
 

	
 
public slots:
 
    /// reset all channel info (names, color etc.)
 
    void resetInfos();
 
    /// reset all channel names
 
    void resetNames();
 
    /// reset all channel colors
 
    void resetColors();
 
    /// reset visibility
 
    void resetVisibility();
 

	
 
private:
 
    struct ChannelInfo
 
    {
 
        explicit ChannelInfo(unsigned index);
 

	
 
        QString name;
 
        bool visibility;
 
        QColor color;
 
    };
 

	
 
    unsigned _numOfChannels;     ///< @note this is not necessarily the length of `infos`
 

	
 
    /**
 
     * Channel info is added here but never removed so that we can
 
     * remember user entered info (names, colors etc.).
 
     */
 
    QList<ChannelInfo> infos;
 
};
 

	
 
#endif // CHANNELINFOMODEL_H
src/channelmanager.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -17,30 +17,28 @@
 
  along with serialplot.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include <QStringList>
 
#include <QModelIndex>
 

	
 
#include <QtDebug>
 

	
 
#include "channelmanager.h"
 
#include "setting_defines.h"
 

	
 
ChannelManager::ChannelManager(unsigned numberOfChannels, unsigned numberOfSamples, QObject *parent) :
 
    QObject(parent)
 
    QObject(parent),
 
    _infoModel(numberOfChannels)
 
{
 
    _numOfChannels = numberOfChannels;
 
    _numOfSamples = numberOfSamples;
 

	
 
    QStringList channelNamesList;
 
    _paused = false;
 

	
 
    for (unsigned int i = 0; i < numberOfChannels; i++)
 
    {
 
        channelBuffers.append(new FrameBuffer(numberOfSamples));
 
        channelNamesList << QString("Channel %1").arg(i+1);
 
    }
 

	
 
    _channelNames.setStringList(channelNamesList);
 

	
 
    connect(&_channelNames, &QStringListModel::dataChanged,
 
            this, &ChannelManager::onChannelNameDataChange);
 
    connect(&_infoModel, &ChannelInfoModel::dataChanged,
 
            this, &ChannelManager::onChannelInfoChanged);
 
}
 

	
 
ChannelManager::~ChannelManager()
 
@@ -71,7 +69,6 @@ void ChannelManager::setNumOfChannels(un
 
        for (unsigned int i = 0; i < number - oldNum; i++)
 
        {
 
            channelBuffers.append(new FrameBuffer(_numOfSamples));
 
            addChannelName(QString("Channel %1").arg(oldNum+i+1));
 
        }
 
    }
 
    else if(number < oldNum)
 
@@ -80,10 +77,12 @@ void ChannelManager::setNumOfChannels(un
 
        for (unsigned int i = oldNum-1; i > number-1; i--)
 
        {
 
            delete channelBuffers.takeLast();
 
            _channelNames.removeRow(i);
 
        }
 
    }
 

	
 
    _numOfChannels = number;
 
    _infoModel.setNumOfChannels(number);
 

	
 
    emit numOfChannelsChanged(number);
 
}
 

	
 
@@ -99,74 +98,96 @@ void ChannelManager::setNumOfSamples(uns
 
    emit numOfSamplesChanged(number);
 
}
 

	
 
void ChannelManager::pause(bool paused)
 
{
 
    _paused = paused;
 
}
 

	
 
FrameBuffer* ChannelManager::channelBuffer(unsigned channel)
 
{
 
    return channelBuffers[channel];
 
}
 

	
 
QStringListModel* ChannelManager::channelNames()
 
ChannelInfoModel* ChannelManager::infoModel()
 
{
 
    return &_channelNames;
 
    return &_infoModel;
 
}
 

	
 
QString ChannelManager::channelName(unsigned channel)
 
{
 
    return _channelNames.data(_channelNames.index(channel, 0), Qt::DisplayRole).toString();
 
    return _infoModel.data(_infoModel.index(channel, ChannelInfoModel::COLUMN_NAME),
 
                           Qt::DisplayRole).toString();
 
}
 

	
 
void ChannelManager::setChannelName(unsigned channel, QString name)
 
QStringList ChannelManager::channelNames()
 
{
 
    _channelNames.setData(_channelNames.index(channel, 0), QVariant(name), Qt::DisplayRole);
 
    QStringList list;
 
    for (unsigned ci = 0; ci < _numOfChannels; ci++)
 
    {
 
        list << channelName(ci);
 
    }
 
    return list;
 
}
 

	
 
void ChannelManager::addChannelName(QString name)
 
void ChannelManager::onChannelInfoChanged(const QModelIndex & topLeft,
 
                                          const QModelIndex & bottomRight,
 
                                          const QVector<int> & roles)
 
{
 
    _channelNames.insertRow(_channelNames.rowCount());
 
    setChannelName(_channelNames.rowCount()-1, name);
 
}
 

	
 
void ChannelManager::onChannelNameDataChange(const QModelIndex & topLeft,
 
                                             const QModelIndex & bottomRight,
 
                                             const QVector<int> & roles)
 
{
 
    Q_UNUSED(roles);
 
    int start = topLeft.row();
 
    int end = bottomRight.row();
 
    int col = topLeft.column();
 

	
 
    // TODO: maybe check `roles` parameter, can't think of a reason for current use case
 
    for (int i = start; i <= end; i++)
 
    for (int ci = start; ci <= end; ci++)
 
    {
 
        emit channelNameChanged(i, channelName(i));
 
        for (auto role : roles)
 
        {
 
            switch (role)
 
            {
 
                case Qt::EditRole:
 
                    if (col == ChannelInfoModel::COLUMN_NAME)
 
                    {
 
                        emit channelNameChanged(ci, channelName(ci));
 
                    }
 
                    break;
 
                case Qt::ForegroundRole:
 
                    if (col == ChannelInfoModel::COLUMN_NAME)
 
                    {
 
                        // TODO: emit channel color changed
 
                    }
 
                    break;
 
                case Qt::CheckStateRole:
 
                    if (col == ChannelInfoModel::COLUMN_VISIBILITY)
 
                    {
 
                        // TODO: emit visibility
 
                    }
 
                    break;
 
            }
 
        }
 
        // emit channelNameChanged(i, channelName(i));
 
    }
 
}
 

	
 
void ChannelManager::addChannelData(unsigned channel, double* data, unsigned size)
 
void ChannelManager::addData(double* data, unsigned size)
 
{
 
    channelBuffer(channel)->addSamples(data, size);
 
    Q_ASSERT(size % _numOfChannels == 0);
 

	
 
    if (_paused) return;
 

	
 
    int n = size / _numOfChannels;
 
    for (unsigned ci = 0; ci < _numOfChannels; ci++)
 
    {
 
        channelBuffers[ci]->addSamples(&data[ci*n], n);
 
    }
 

	
 
    emit dataAdded();
 
}
 

	
 
void ChannelManager::saveSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Channels);
 
    settings->beginWriteArray(SG_Channels_Channel);
 
    for (unsigned i = 0; i < numOfChannels(); i++)
 
    {
 
        settings->setArrayIndex(i);
 
        settings->setValue(SG_Channels_Name, channelName(i));
 
    }
 
    settings->endArray();
 
    settings->endGroup();
 
    _infoModel.saveSettings(settings);
 
}
 

	
 
void ChannelManager::loadSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Channels);
 
    settings->beginReadArray(SG_Channels_Channel);
 
    for (unsigned i = 0; i < numOfChannels(); i++)
 
    {
 
        settings->setArrayIndex(i);
 
        setChannelName(i, settings->value(SG_Channels_Name, channelName(i)).toString());
 
    }
 
    settings->endArray();
 
    settings->endGroup();
 
    _infoModel.loadSettings(settings);
 
}
src/channelmanager.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -21,12 +21,13 @@
 
#define CHANNELMANAGER_H
 

	
 
#include <QObject>
 
#include <QStringListModel>
 
#include <QStringList>
 
#include <QModelIndex>
 
#include <QVector>
 
#include <QSettings>
 

	
 
#include "framebuffer.h"
 
#include "channelinfomodel.h"
 

	
 
class ChannelManager : public QObject
 
{
 
@@ -38,36 +39,55 @@ public:
 
    unsigned numOfChannels();
 
    unsigned numOfSamples();
 
    FrameBuffer* channelBuffer(unsigned channel);
 
    QStringListModel* channelNames();
 
    QString channelName(unsigned channel);
 
    /// Stores channel names into a `QSettings`
 
    void saveSettings(QSettings* settings);
 
    /// Loads channel names from a `QSettings`.
 
    void loadSettings(QSettings* settings);
 
    /// Returns a model that manages channel information (name, color etc)
 
    ChannelInfoModel* infoModel();
 
    /// Returns a list of channel names
 
    QStringList channelNames();
 

	
 
signals:
 
    void numOfChannelsChanged(unsigned value);
 
    void numOfSamplesChanged(unsigned value);
 
    void channelNameChanged(unsigned channel, QString name);
 
    void dataAdded(); ///< emitted when data added to channel man.
 

	
 
public slots:
 
    void setNumOfChannels(unsigned number);
 
    void setNumOfSamples(unsigned number);
 
    void setChannelName(unsigned channel, QString name);
 
    void addChannelData(unsigned channel, double* data, unsigned size);
 
    /**
 
     * Add data for all channels.
 
     *
 
     * All channels data is provided in a single array which contains equal
 
     * number of samples for all channels. Structure is as shown below:
 
     *
 
     * [CH0_SMP0, CH0_SMP1 ... CH0_SMPN, CH1_SMP0, CH1_SMP1, ... , CHN_SMPN]
 
     *
 
     * @param data samples for all channels
 
     * @param size size of `data`, must be multiple of `numOfChannels`
 
     */
 
    void addData(double* data, unsigned size);
 

	
 
    /// When paused `addData` does nothing.
 
    void pause(bool paused);
 

	
 
private:
 
    unsigned _numOfChannels;
 
    unsigned _numOfSamples;
 
    bool _paused;
 
    QList<FrameBuffer*> channelBuffers;
 
    QStringListModel _channelNames;
 
    // QStringListModel _channelNames;
 
    ChannelInfoModel _infoModel;
 

	
 
    void addChannelName(QString name); ///< appends a new channel name at the end of list
 

	
 
private slots:
 
    void onChannelNameDataChange(const QModelIndex & topLeft,
 
                                 const QModelIndex & bottomRight,
 
                                 const QVector<int> & roles = QVector<int> ());
 
    void onChannelInfoChanged(const QModelIndex & topLeft,
 
                              const QModelIndex & bottomRight,
 
                              const QVector<int> & roles = QVector<int> ());
 
};
 

	
 
#endif // CHANNELMANAGER_H
src/dataformatpanel.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -31,26 +31,27 @@
 

	
 
DataFormatPanel::DataFormatPanel(QSerialPort* port,
 
                                 ChannelManager* channelMan,
 
                                 DataRecorder* recorder,
 
                                 QWidget *parent) :
 
    QWidget(parent),
 
    ui(new Ui::DataFormatPanel),
 
    bsReader(port, channelMan, this),
 
    asciiReader(port, channelMan, this),
 
    framedReader(port, channelMan, this),
 
    demoReader(port, channelMan, this)
 
    bsReader(port, channelMan, recorder, this),
 
    asciiReader(port, channelMan, recorder, this),
 
    framedReader(port, channelMan, recorder, this),
 
    demoReader(port, channelMan, recorder, this)
 
{
 
    ui->setupUi(this);
 

	
 
    serialPort = port;
 
    _channelMan = channelMan;
 
    paused = false;
 
    demoEnabled = false;
 

	
 
    // initalize default reader
 
    currentReader = &bsReader;
 
    bsReader.enable();
 
    ui->rbBinary->setChecked(true);
 
    ui->horizontalLayout->addWidget(bsReader.settingsWidget(), 1);
 
    connect(&bsReader, SIGNAL(dataAdded()), this, SIGNAL(dataAdded()));
 
    connect(&bsReader, SIGNAL(numOfChannelsChanged(unsigned)),
 
            this, SIGNAL(numOfChannelsChanged(unsigned)));
 
    connect(&bsReader, SIGNAL(samplesPerSecondChanged(unsigned)),
 
@@ -99,8 +100,7 @@ void DataFormatPanel::enableDemo(bool en
 
    if (enabled)
 
    {
 
        demoReader.enable();
 
        connect(&demoReader, &DemoReader::dataAdded,
 
                this, &DataFormatPanel::dataAdded);
 
        demoReader.recording = currentReader->recording;
 
        connect(&demoReader, &DemoReader::samplesPerSecondChanged,
 
                this, &DataFormatPanel::samplesPerSecondChanged);
 
    }
 
@@ -109,12 +109,19 @@ void DataFormatPanel::enableDemo(bool en
 
        demoReader.enable(false);
 
        disconnect(&demoReader, 0, this, 0);
 
    }
 
    demoEnabled = enabled;
 
}
 

	
 
void DataFormatPanel::addChannelData(unsigned int channel,
 
                                     double* data, unsigned size)
 
void DataFormatPanel::startRecording()
 
{
 
    _channelMan->addChannelData(channel, data, size);
 
    currentReader->recording = true;
 
    if (demoEnabled) demoReader.recording = true;
 
}
 

	
 
void DataFormatPanel::stopRecording()
 
{
 
    currentReader->recording = false;
 
    if (demoEnabled) demoReader.recording = false;
 
}
 

	
 
void DataFormatPanel::selectReader(AbstractReader* reader)
 
@@ -124,7 +131,6 @@ void DataFormatPanel::selectReader(Abstr
 

	
 
    // re-connect signals
 
    disconnect(currentReader, 0, this, 0);
 
    connect(reader, SIGNAL(dataAdded()), this, SIGNAL(dataAdded()));
 
    connect(reader, SIGNAL(numOfChannelsChanged(unsigned)),
 
            this, SIGNAL(numOfChannelsChanged(unsigned)));
 
    connect(reader, SIGNAL(samplesPerSecondChanged(unsigned)),
 
@@ -142,8 +148,8 @@ void DataFormatPanel::selectReader(Abstr
 
        emit numOfChannelsChanged(reader->numOfChannels());
 
    }
 

	
 
    // pause
 
    reader->pause(paused);
 
    reader->recording = currentReader->recording;
 

	
 
    currentReader = reader;
 
}
src/dataformatpanel.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -34,6 +34,7 @@
 
#include "asciireader.h"
 
#include "demoreader.h"
 
#include "framedreader.h"
 
#include "datarecorder.h"
 

	
 
namespace Ui {
 
class DataFormatPanel;
 
@@ -46,7 +47,8 @@ class DataFormatPanel : public QWidget
 
public:
 
    explicit DataFormatPanel(QSerialPort* port,
 
                             ChannelManager* channelMan,
 
                             QWidget *parent = 0);
 
                             DataRecorder* recorder,
 
                             QWidget* parent = 0);
 
    ~DataFormatPanel();
 

	
 
    /// Returns currently selected number of channels
 
@@ -60,10 +62,19 @@ public slots:
 
    void pause(bool);
 
    void enableDemo(bool); // demo shouldn't be enabled when port is open
 

	
 
    /**
 
     * @brief Starts sending data to recorder.
 
     *
 
     * @note recorder must have been started!
 
     */
 
    void startRecording();
 

	
 
    /// Stops recording.
 
    void stopRecording();
 

	
 
signals:
 
    void numOfChannelsChanged(unsigned);
 
    void samplesPerSecondChanged(unsigned);
 
    void dataAdded();
 

	
 
private:
 
    Ui::DataFormatPanel *ui;
 
@@ -81,10 +92,8 @@ private:
 

	
 
    bool paused;
 

	
 
    bool demoEnabled;
 
    DemoReader demoReader;
 

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

	
 
#endif // DATAFORMATPANEL_H
src/datarecorder.cpp
Show inline comments
 
new file 100644
 
/*
 
  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 "datarecorder.h"
 

	
 
#include <QtDebug>
 

	
 
DataRecorder::DataRecorder(QObject *parent) :
 
    QObject(parent),
 
    fileStream(&file)
 
{
 
    lastNumChannels = 0;
 
    disableBuffering = false;
 
    windowsLE = false;
 
}
 

	
 
bool DataRecorder::startRecording(QString fileName, QString separator, QStringList channelNames)
 
{
 
    Q_ASSERT(!file.isOpen());
 
    _sep =  separator;
 

	
 
    // open file
 
    file.setFileName(fileName);
 
    if (!file.open(QIODevice::WriteOnly))
 
    {
 
        qCritical() << "Opening file " << fileName
 
                    << " for recording failed with error: " << file.error();
 
        return false;
 
    }
 

	
 
    // write header line
 
    if (!channelNames.isEmpty())
 
    {
 
        fileStream << channelNames.join(_sep);
 
        fileStream << le();
 
        lastNumChannels = channelNames.length();
 
    }
 
    return true;
 
}
 

	
 
void DataRecorder::addData(double* data, unsigned length, unsigned numOfChannels)
 
{
 
    Q_ASSERT(length > 0);
 
    Q_ASSERT(length % numOfChannels == 0);
 

	
 
    if (lastNumChannels != 0 && numOfChannels != lastNumChannels)
 
    {
 
        qWarning() << "Number of channels changed from " << lastNumChannels
 
                   << " to " << numOfChannels <<
 
            " during recording, CSV file is corrupted but no data will be lost.";
 
    }
 
    lastNumChannels = numOfChannels;
 

	
 
    unsigned numOfSamples = length / numOfChannels; // per channel
 
    for (unsigned int i = 0; i < numOfSamples; i++)
 
    {
 
        for (unsigned ci = 0; ci < numOfChannels; ci++)
 
        {
 
            fileStream << data[ci * numOfSamples + i];
 
            if (ci != numOfChannels-1) fileStream << _sep;
 
        }
 
        fileStream << le();
 
    }
 

	
 
    if (disableBuffering) fileStream.flush();
 
}
 

	
 
void DataRecorder::stopRecording()
 
{
 
    Q_ASSERT(file.isOpen());
 

	
 
    file.close();
 
    lastNumChannels = 0;
 
}
 

	
 
const char* DataRecorder::le() const
 
{
 
    return windowsLE ? "\r\n" : "\n";
 
}
src/datarecorder.h
Show inline comments
 
new file 100644
 
/*
 
  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/>.
 
*/
 

	
 
#ifndef DATARECORDER_H
 
#define DATARECORDER_H
 

	
 
#include <QObject>
 
#include <QFile>
 
#include <QTextStream>
 

	
 
class DataRecorder : public QObject
 
{
 
    Q_OBJECT
 
public:
 
    explicit DataRecorder(QObject *parent = 0);
 

	
 
    /// Disables file buffering
 
    bool disableBuffering;
 

	
 
    /**
 
     * Use CR+LF as line ending. `false` by default.
 
     *
 
     * @note Toggling this variable during a recording will result in
 
     * a corrupted file. Care must be taken at higher (UI) levels.
 
     */
 
    bool windowsLE;
 

	
 
    /**
 
     * @brief Starts recording data to a file in CSV format.
 
     *
 
     * File is opened and header line (names of channels) is written.
 
     *
 
     * @param fileName name of the recording file
 
     * @param separator column separator
 
     * @param channelNames names of the channels for header line, if empty no header line is written
 
     * @return false if file operation fails (read only etc.)
 
     */
 
    bool startRecording(QString fileName, QString separator, QStringList channelNames);
 

	
 
    /**
 
     * @brief Adds data to a channel.
 
     *
 
     * Multiple rows of data can be added at a time. Each channels
 
     * data should be ordered consecutively in the `data` array:
 
     *
 
     * [CH0_SMP0, CH0_SMP1 ... CH0_SMPN, CH1_SMP0, CH1_SMP1, ... , CHN_SMPN]
 
     *
 
     * If `numOfChannels` changes during recording, no data will be
 
     * lost (ie. it will be written to the file) but this will produce
 
     * an invalid CSV file. An error message will be written to the
 
     * console.
 
     *
 
     * @param data samples array
 
     * @param length number of samples in `data`, must be multiple of `numOfChannels`
 
     * @param numOfChannels how many channels samples this data carries
 
     */
 
    void addData(double* data, unsigned length, unsigned numOfChannels);
 

	
 
    /// Stops recording, closes file.
 
    void stopRecording();
 

	
 
private:
 
    unsigned lastNumChannels;   ///< used for error message only
 
    QFile file;
 
    QTextStream fileStream;
 
    QString _sep;
 

	
 
    /// Returns the selected line ending.
 
    const char* le() const;
 
};
 

	
 
#endif // DATARECORDER_H
src/demoreader.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -25,8 +25,9 @@
 
#define M_PI 3.14159265358979323846
 
#endif
 

	
 
DemoReader::DemoReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
 
    AbstractReader(device, channelMan, parent)
 
DemoReader::DemoReader(QIODevice* device, ChannelManager* channelMan,
 
                       DataRecorder* recorder, QObject* parent) :
 
    AbstractReader(device, channelMan, recorder, parent)
 
{
 
    paused = false;
 
    _numOfChannels = 1;
 
@@ -76,13 +77,13 @@ void DemoReader::demoTimerTimeout()
 

	
 
    if (!paused)
 
    {
 
        double* samples = new double[_numOfChannels];
 
        for (unsigned ci = 0; ci < _numOfChannels; ci++)
 
        {
 
            // we are calculating the fourier components of square wave
 
            double value = 4*sin(2*M_PI*double((ci+1)*count)/period)/((2*(ci+1))*M_PI);
 
            _channelMan->addChannelData(ci, &value, 1);
 
            sampleCount++;
 
            samples[ci] = 4*sin(2*M_PI*double((ci+1)*count)/period)/((2*(ci+1))*M_PI);
 
        }
 
        emit dataAdded();
 
        addData(samples, _numOfChannels);
 
        delete[] samples;
 
    }
 
}
src/demoreader.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -38,7 +38,8 @@ class DemoReader : public AbstractReader
 
    Q_OBJECT
 

	
 
public:
 
    explicit DemoReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0);
 
    explicit DemoReader(QIODevice* device, ChannelManager* channelMan,
 
                        DataRecorder* recorder, QObject* parent = 0);
 

	
 
    /// Demo reader is an exception so this function returns NULL
 
    QWidget* settingsWidget();
src/framebuffer.cpp
Show inline comments
 
@@ -30,7 +30,7 @@ FrameBuffer::FrameBuffer(size_t size)
 

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

	
 
void FrameBuffer::resize(size_t size)
src/framebufferseries.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -21,9 +21,19 @@
 

	
 
FrameBufferSeries::FrameBufferSeries(FrameBuffer* buffer)
 
{
 
    xAsIndex = true;
 
    _xmin = 0;
 
    _xmax = 1;
 
    _buffer = buffer;
 
}
 

	
 
void FrameBufferSeries::setXAxis(bool asIndex, double xmin, double xmax)
 
{
 
    xAsIndex = asIndex;
 
    _xmin = xmin;
 
    _xmax = xmax;
 
}
 

	
 
size_t FrameBufferSeries::size() const
 
{
 
    return _buffer->size();
 
@@ -31,10 +41,27 @@ size_t FrameBufferSeries::size() const
 

	
 
QPointF FrameBufferSeries::sample(size_t i) const
 
{
 
    return QPointF(i, _buffer->sample(i));
 
    if (xAsIndex)
 
    {
 
        return QPointF(i, _buffer->sample(i));
 
    }
 
    else
 
    {
 
        return QPointF(i * (_xmax - _xmin) / size() + _xmin, _buffer->sample(i));
 
    }
 
}
 

	
 
QRectF FrameBufferSeries::boundingRect() const
 
{
 
    return _buffer->boundingRect();
 
    if (xAsIndex)
 
    {
 
        return _buffer->boundingRect();
 
    }
 
    else
 
    {
 
        auto rect = _buffer->boundingRect();
 
        rect.setLeft(_xmin);
 
        rect.setRight(_xmax);
 
        return rect;
 
    }
 
}
src/framebufferseries.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -37,6 +37,9 @@ class FrameBufferSeries : public QwtSeri
 
public:
 
    FrameBufferSeries(FrameBuffer* buffer);
 

	
 
    /// Behavior of X axis
 
    void setXAxis(bool asIndex, double xmin, double xmax);
 

	
 
    // QwtSeriesData implementations
 
    size_t size() const;
 
    QPointF sample(size_t i) const;
 
@@ -44,6 +47,9 @@ public:
 

	
 
private:
 
    FrameBuffer* _buffer;
 
    bool xAsIndex;
 
    double _xmin;
 
    double _xmax;
 
};
 

	
 
#endif // FRAMEBUFFERSERIES_H
src/framedreader.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -23,8 +23,9 @@
 

	
 
#include "framedreader.h"
 

	
 
FramedReader::FramedReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
 
    AbstractReader(device, channelMan, parent)
 
FramedReader::FramedReader(QIODevice* device, ChannelManager* channelMan,
 
                           DataRecorder* recorder, QObject* parent) :
 
    AbstractReader(device, channelMan, recorder, parent)
 
{
 
    paused = false;
 

	
 
@@ -310,22 +311,14 @@ void FramedReader::readFrameDataAndCheck
 
    if (!checksumEnabled || checksumPassed)
 
    {
 
        // commit data
 
        for (unsigned int ci = 0; ci < _numOfChannels; ci++)
 
        {
 
            _channelMan->addChannelData(
 
                ci,
 
                channelSamples + ci*numOfPackagesToRead,
 
                numOfPackagesToRead);
 
            sampleCount += numOfPackagesToRead;
 
        }
 
        emit dataAdded();
 
        addData(channelSamples, numOfPackagesToRead*_numOfChannels);
 
    }
 
    else
 
    {
 
        qCritical() << "Checksum failed! Received:" << rChecksum << "Calculated:" << calcChecksum;
 
    }
 

	
 
    delete channelSamples;
 
    delete[] channelSamples;
 
}
 

	
 
template<typename T> double FramedReader::readSampleAs()
src/framedreader.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -33,7 +33,8 @@ class FramedReader : public AbstractRead
 
    Q_OBJECT
 

	
 
public:
 
    explicit FramedReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0);
 
    explicit FramedReader(QIODevice* device, ChannelManager* channelMan,
 
                          DataRecorder* recorder, QObject *parent = 0);
 
    QWidget* settingsWidget();
 
    unsigned numOfChannels();
 
    void enable(bool enabled = true);
 
@@ -83,8 +84,6 @@ private:
 
    /// reads payload portion of the frame, calculates checksum and commits data
 
    /// @note should be called only if there are enough bytes on device
 
    void readFrameDataAndCheck();
 
    // `data` contains i th channels data
 
    void addChannelData(unsigned int channel, double* data, unsigned size);
 

	
 
private slots:
 
    void onDataReady();
src/hidabletabwidget.cpp
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -77,3 +77,8 @@ void HidableTabWidget::connectSignals()
 
        connect(this, SIGNAL(tabBarDoubleClicked(int)), this, SLOT(onTabBarDoubleClicked()));
 
    }
 
}
 

	
 
void HidableTabWidget::showTabs()
 
{
 
    hideAction.setChecked(false);
 
}
src/hidabletabwidget.h
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -30,6 +30,9 @@ public:
 
    explicit HidableTabWidget(QWidget *parent = 0);
 
    QAction hideAction;
 

	
 
public slots:
 
    void showTabs();
 

	
 
private slots:
 
    void onHideAction(bool checked);
 
    void onTabBarClicked();
src/main.cpp
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -24,7 +24,6 @@
 
#include "tooltipfilter.h"
 
#include "version.h"
 

	
 

	
 
MainWindow* pMainWindow;
 

	
 
void messageHandler(QtMsgType type, const QMessageLogContext &context,
 
@@ -50,5 +49,6 @@ int main(int argc, char *argv[])
 
    qDebug() << "Revision" << VERSION_REVISION;
 

	
 
    w.show();
 

	
 
    return a.exec();
 
}
src/mainwindow.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -62,19 +62,22 @@ MainWindow::MainWindow(QWidget *parent) 
 
    channelMan(1, 1, this),
 
    snapshotMan(this, &channelMan),
 
    commandPanel(&serialPort),
 
    dataFormatPanel(&serialPort, &channelMan)
 
    dataFormatPanel(&serialPort, &channelMan, &recorder),
 
    recordPanel(&recorder, &channelMan)
 
{
 
    ui->setupUi(this);
 

	
 
    plotMan = new PlotManager(ui->plotArea);
 
    plotMan = new PlotManager(ui->plotArea, channelMan.infoModel());
 

	
 
    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->setCurrentIndex(0);
 
    auto tbPortControl = portControl.toolBar();
 
    addToolBar(tbPortControl);
 
    addToolBar(recordPanel.toolbar());
 

	
 
    ui->plotToolBar->addAction(snapshotMan.takeSnapshotAction());
 
    ui->menuBar->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu());
 
@@ -83,6 +86,7 @@ MainWindow::MainWindow(QWidget *parent) 
 
    connect(&commandPanel, &CommandPanel::focusRequested, [this]()
 
            {
 
                this->ui->tabWidget->setCurrentWidget(&commandPanel);
 
                this->ui->tabWidget->showTabs();
 
            });
 

	
 
    tbPortControl->setObjectName("tbPortControl");
 
@@ -130,14 +134,18 @@ MainWindow::MainWindow(QWidget *parent) 
 
    QObject::connect(&portControl, &PortControl::portToggled,
 
                     this, &MainWindow::onPortToggled);
 

	
 
    // plot control signals
 
    connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
 
            this, &MainWindow::onNumOfSamplesChanged);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
 
            plotMan, &PlotManager::onNumOfSamplesChanged);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::scaleChanged,
 
            plotMan, &PlotManager::setAxis);
 
    connect(&plotControlPanel, &PlotControlPanel::yScaleChanged,
 
            plotMan, &PlotManager::setYAxis);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::xScaleChanged,
 
            plotMan, &PlotManager::setXAxis);
 

	
 
    QObject::connect(ui->actionClear, SIGNAL(triggered(bool)),
 
                     this, SLOT(clearPlot()));
 
@@ -150,11 +158,42 @@ MainWindow::MainWindow(QWidget *parent) 
 
                     this, SLOT(onPortError(QSerialPort::SerialPortError)));
 

	
 
    // init data format and reader
 
    QObject::connect(&dataFormatPanel, &DataFormatPanel::dataAdded,
 
    QObject::connect(&channelMan, &ChannelManager::dataAdded,
 
                     plotMan, &PlotManager::replot);
 

	
 
    QObject::connect(ui->actionPause, &QAction::triggered,
 
                     &dataFormatPanel, &DataFormatPanel::pause);
 
                     &channelMan, &ChannelManager::pause);
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordStarted,
 
                     &dataFormatPanel, &DataFormatPanel::startRecording);
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordStopped,
 
                     &dataFormatPanel, &DataFormatPanel::stopRecording);
 

	
 
    QObject::connect(ui->actionPause, &QAction::triggered,
 
                     [this](bool enabled)
 
                     {
 
                         if (enabled && !recordPanel.recordPaused())
 
                         {
 
                             dataFormatPanel.pause(true);
 
                         }
 
                         else
 
                         {
 
                             dataFormatPanel.pause(false);
 
                         }
 
                     });
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordPausedChanged,
 
                     [this](bool enabled)
 
                     {
 
                         if (ui->actionPause->isChecked() && enabled)
 
                         {
 
                             dataFormatPanel.pause(false);
 
                         }
 
                     });
 

	
 
    connect(&serialPort, &QIODevice::aboutToClose,
 
            &recordPanel, &RecordPanel::onPortClose);
 

	
 
    // init data arrays and plot
 
    numOfSamples = plotControlPanel.numOfSamples();
 
@@ -169,10 +208,7 @@ MainWindow::MainWindow(QWidget *parent) 
 
    connect(&channelMan, &ChannelManager::numOfChannelsChanged,
 
            this, &MainWindow::onNumOfChannelsChanged);
 

	
 
    connect(&channelMan, &ChannelManager::channelNameChanged,
 
            this, &MainWindow::onChannelNameChanged);
 

	
 
    plotControlPanel.setChannelNamesModel(channelMan.channelNames());
 
    plotControlPanel.setChannelInfoModel(channelMan.infoModel());
 

	
 
    // init curve list
 
    for (unsigned int i = 0; i < numOfChannels; i++)
 
@@ -180,9 +216,12 @@ MainWindow::MainWindow(QWidget *parent) 
 
        plotMan->addCurve(channelMan.channelName(i), channelMan.channelBuffer(i));
 
    }
 

	
 
    // init auto scale
 
    plotMan->setAxis(plotControlPanel.autoScale(),
 
                     plotControlPanel.yMin(), plotControlPanel.yMax());
 
    // init scales
 
    plotMan->setYAxis(plotControlPanel.autoScale(),
 
                      plotControlPanel.yMin(), plotControlPanel.yMax());
 
    plotMan->setXAxis(plotControlPanel.xAxisAsIndex(),
 
                      plotControlPanel.xMin(), plotControlPanel.xMax());
 
    plotMan->onNumOfSamplesChanged(numOfSamples);
 

	
 
    // Init sps (sample per second) counter
 
    spsLabel.setText("0sps");
 
@@ -215,15 +254,12 @@ MainWindow::MainWindow(QWidget *parent) 
 
    connect(commandPanel.newCommandAction(), &QAction::triggered, [this]()
 
            {
 
                this->ui->tabWidget->setCurrentWidget(&commandPanel);
 
                this->ui->tabWidget->showTabs();
 
            });
 
}
 

	
 
MainWindow::~MainWindow()
 
{
 
    // save settings
 
    QSettings settings("serialplot", "serialplot");
 
    saveAllSettings(&settings);
 

	
 
    if (serialPort.isOpen())
 
    {
 
        serialPort.close();
 
@@ -237,6 +273,7 @@ MainWindow::~MainWindow()
 

	
 
void MainWindow::closeEvent(QCloseEvent * event)
 
{
 
    // save snapshots
 
    if (!snapshotMan.isAllSaved())
 
    {
 
        auto clickedButton = QMessageBox::warning(
 
@@ -250,6 +287,41 @@ void MainWindow::closeEvent(QCloseEvent 
 
            return;
 
        }
 
    }
 

	
 
    // save settings
 
    QSettings settings("serialplot", "serialplot");
 
    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);
 
        }
 
        else
 
        {
 
            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)
 
        {
 
            event->ignore();
 
            return;
 
        }
 
    }
 

	
 
    QMainWindow::closeEvent(event);
 
}
 

	
 
@@ -369,17 +441,6 @@ void MainWindow::onNumOfChannelsChanged(
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onChannelNameChanged(unsigned channel, QString name)
 
{
 
    // This slot is triggered also when a new channel is added, in
 
    // this case curve list doesn't contain said channel. No worries,
 
    // since `onNumOfChannelsChanged` slot will update curve list.
 
    if (channel < plotMan->numOfCurves()) // check if channel exists in curve list
 
    {
 
        plotMan->setTitle(channel, name);
 
    }
 
}
 

	
 
void MainWindow::onSpsChanged(unsigned sps)
 
{
 
    spsLabel.setText(QString::number(sps) + "sps");
 
@@ -463,6 +524,11 @@ void MainWindow::messageHandler(QtMsgTyp
 
    {
 
        ui->statusBar->showMessage(msg, 5000);
 
    }
 

	
 
    if (type == QtFatalMsg)
 
    {
 
        __builtin_trap();
 
    }
 
}
 

	
 
void MainWindow::saveAllSettings(QSettings* settings)
 
@@ -474,6 +540,7 @@ void MainWindow::saveAllSettings(QSettin
 
    plotControlPanel.saveSettings(settings);
 
    plotMan->saveSettings(settings);
 
    commandPanel.saveSettings(settings);
 
    recordPanel.saveSettings(settings);
 
}
 

	
 
void MainWindow::loadAllSettings(QSettings* settings)
 
@@ -485,6 +552,7 @@ void MainWindow::loadAllSettings(QSettin
 
    plotControlPanel.loadSettings(settings);
 
    plotMan->loadSettings(settings);
 
    commandPanel.loadSettings(settings);
 
    recordPanel.loadSettings(settings);
 
}
 

	
 
void MainWindow::saveMWSettings(QSettings* settings)
src/mainwindow.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -38,11 +38,13 @@
 
#include "commandpanel.h"
 
#include "dataformatpanel.h"
 
#include "plotcontrolpanel.h"
 
#include "recordpanel.h"
 
#include "ui_about_dialog.h"
 
#include "framebuffer.h"
 
#include "channelmanager.h"
 
#include "snapshotmanager.h"
 
#include "plotmanager.h"
 
#include "datarecorder.h"
 

	
 
namespace Ui {
 
class MainWindow;
 
@@ -74,10 +76,12 @@ private:
 
    ChannelManager channelMan;
 
    PlotManager* plotMan;
 
    SnapshotManager snapshotMan;
 
    DataRecorder recorder;       // operated by `recordPanel`
 

	
 
    QLabel spsLabel;
 
    CommandPanel commandPanel;
 
    DataFormatPanel dataFormatPanel;
 
    RecordPanel recordPanel;
 
    PlotControlPanel plotControlPanel;
 

	
 
    bool isDemoRunning();
 
@@ -99,7 +103,6 @@ private slots:
 

	
 
    void onNumOfSamplesChanged(int value);
 
    void onNumOfChannelsChanged(unsigned value);
 
    void onChannelNameChanged(unsigned channel, QString name);
 

	
 
    void clearPlot();
 
    void onSpsChanged(unsigned sps);
src/mainwindow.ui
Show inline comments
 
@@ -141,6 +141,9 @@
 
   <property name="checked">
 
    <bool>false</bool>
 
   </property>
 
   <property name="icon">
 
    <iconset theme="player_pause"/>
 
   </property>
 
   <property name="text">
 
    <string>Pause</string>
 
   </property>
 
@@ -152,6 +155,9 @@
 
   </property>
 
  </action>
 
  <action name="actionClear">
 
   <property name="icon">
 
    <iconset theme="editclear"/>
 
   </property>
 
   <property name="text">
 
    <string>Clear</string>
 
   </property>
src/plot.cpp
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -38,6 +38,7 @@ Plot::Plot(QWidget* parent) :
 
{
 
    isAutoScaled = true;
 
    symbolSize = 0;
 
    numOfSamples = 1;
 

	
 
    QObject::connect(&zoomer, &Zoomer::unzoomed, this, &Plot::unzoomed);
 

	
 
@@ -78,7 +79,7 @@ Plot::~Plot()
 
    if (snapshotOverlay != NULL) delete snapshotOverlay;
 
}
 

	
 
void Plot::setAxis(bool autoScaled, double yAxisMin, double yAxisMax)
 
void Plot::setYAxis(bool autoScaled, double yAxisMin, double yAxisMax)
 
{
 
    this->isAutoScaled = autoScaled;
 

	
 
@@ -92,8 +93,29 @@ void Plot::setAxis(bool autoScaled, doub
 
    resetAxes();
 
}
 

	
 
void Plot::setXAxis(double xMin, double xMax)
 
{
 
    _xMin = xMin;
 
    _xMax = xMax;
 

	
 
    zoomer.zoom(0); // unzoom
 

	
 
    // set axis
 
    setAxisScale(QwtPlot::xBottom, xMin, xMax);
 
    replot(); // Note: if we don't replot here scale at startup isn't set correctly
 

	
 
    // reset zoom base
 
    auto base = zoomer.zoomBase();
 
    base.setLeft(xMin);
 
    base.setRight(xMax);
 
    zoomer.setZoomBase(base);
 

	
 
    onXScaleChanged();
 
}
 

	
 
void Plot::resetAxes()
 
{
 
    // reset y axis
 
    if (isAutoScaled)
 
    {
 
        setAxisAutoScale(QwtPlot::yLeft);
 
@@ -103,12 +125,13 @@ void Plot::resetAxes()
 
        setAxisScale(QwtPlot::yLeft, yMin, yMax);
 
    }
 

	
 
    zoomer.setZoomBase();
 

	
 
    replot();
 
}
 

	
 
void Plot::unzoomed()
 
{
 
    setAxisAutoScale(QwtPlot::xBottom);
 
    resetAxes();
 
    onXScaleChanged();
 
}
 
@@ -229,7 +252,10 @@ void Plot::onXScaleChanged()
 
    auto sw = axisWidget(QwtPlot::xBottom);
 
    auto paintDist = sw->scaleDraw()->scaleMap().pDist();
 
    auto scaleDist = sw->scaleDraw()->scaleMap().sDist();
 
    int symDisPx = round(paintDist / scaleDist);
 
    auto fullScaleDist = zoomer.zoomBase().width();
 
    auto zoomRate = fullScaleDist / scaleDist;
 
    float samplesInView = numOfSamples / zoomRate;
 
    int symDisPx = round(paintDist / samplesInView);
 

	
 
    if (symDisPx < SYMBOL_SHOW_AT_WIDTH)
 
    {
 
@@ -273,8 +299,6 @@ void Plot::resizeEvent(QResizeEvent * ev
 

	
 
void Plot::onNumOfSamplesChanged(unsigned value)
 
{
 
    auto currentBase = zoomer.zoomBase();
 
    currentBase.setWidth(value);
 
    zoomer.setZoomBase(currentBase);
 
    numOfSamples = value;
 
    onXScaleChanged();
 
}
src/plot.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -37,28 +37,14 @@ class Plot : public QwtPlot
 
{
 
    Q_OBJECT
 

	
 
    friend class PlotManager;
 

	
 
public:
 
    Plot(QWidget* parent = 0);
 
    ~Plot();
 

	
 
    static QColor makeColor(unsigned int channelIndex);
 

	
 
private:
 
    bool isAutoScaled;
 
    double yMin, yMax;
 
    int symbolSize;
 
    Zoomer zoomer;
 
    ScaleZoomer sZoomer;
 
    QwtPlotGrid grid;
 
    PlotSnapshotOverlay* snapshotOverlay;
 
    QwtPlotLegendItem legend;
 
    QwtPlotTextLabel demoIndicator;
 

	
 
    /// update the display of symbols depending on `symbolSize`
 
    void updateSymbols();
 
    void resetAxes();
 
    void resizeEvent(QResizeEvent * event);
 

	
 
public slots:
 
    void showGrid(bool show = true);
 
    void showMinorGrid(bool show = true);
 
@@ -66,7 +52,8 @@ public slots:
 
    void showDemoIndicator(bool show = true);
 
    void unzoom();
 
    void darkBackground(bool enabled = true);
 
    void setAxis(bool autoScaled, double yMin = 0, double yMax = 1);
 
    void setYAxis(bool autoScaled, double yMin = 0, double yMax = 1);
 
    void setXAxis(double xMin, double xMax);
 

	
 
    /**
 
     * Displays an animation for snapshot.
 
@@ -77,6 +64,26 @@ public slots:
 

	
 
    void onNumOfSamplesChanged(unsigned value);
 

	
 
protected:
 
    /// update the display of symbols depending on `symbolSize`
 
    void updateSymbols();
 

	
 
private:
 
    bool isAutoScaled;
 
    double yMin, yMax;
 
    double _xMin, _xMax;
 
    unsigned numOfSamples;
 
    int symbolSize;
 
    Zoomer zoomer;
 
    ScaleZoomer sZoomer;
 
    QwtPlotGrid grid;
 
    PlotSnapshotOverlay* snapshotOverlay;
 
    QwtPlotLegendItem legend;
 
    QwtPlotTextLabel demoIndicator;
 

	
 
    void resetAxes();
 
    void resizeEvent(QResizeEvent * event);
 

	
 
private slots:
 
    void unzoomed();
 
    void onXScaleChanged();
src/plotcontrolpanel.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -23,6 +23,7 @@
 

	
 
#include <math.h>
 

	
 
#include "color_selector.hpp"
 
#include "plotcontrolpanel.h"
 
#include "ui_plotcontrolpanel.h"
 
#include "setting_defines.h"
 
@@ -41,7 +42,12 @@ Q_DECLARE_METATYPE(Range);
 

	
 
PlotControlPanel::PlotControlPanel(QWidget *parent) :
 
    QWidget(parent),
 
    ui(new Ui::PlotControlPanel)
 
    ui(new Ui::PlotControlPanel),
 
    resetAct(tr("Reset"), this),
 
    resetNamesAct(tr("Reset Names"), this),
 
    resetColorsAct(tr("Reset Colors"), this),
 
    showAllAct(tr("Show All"), this),
 
    resetMenu(tr("Reset Menu"), this)
 
{
 
    ui->setupUi(this);
 

	
 
@@ -55,6 +61,12 @@ PlotControlPanel::PlotControlPanel(QWidg
 
    ui->spYmax->setRange((-1) * std::numeric_limits<double>::max(),
 
                         std::numeric_limits<double>::max());
 

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

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

	
 
    // connect signals
 
    connect(ui->spNumOfSamples, SIGNAL(valueChanged(int)),
 
            this, SLOT(onNumOfSamples(int)));
 
@@ -68,6 +80,15 @@ PlotControlPanel::PlotControlPanel(QWidg
 
    connect(ui->spYmin, SIGNAL(valueChanged(double)),
 
            this, SLOT(onYScaleChanged()));
 

	
 
    connect(ui->cbIndex, &QCheckBox::toggled,
 
            this, &PlotControlPanel::onIndexChecked);
 

	
 
    connect(ui->spXmax, SIGNAL(valueChanged(double)),
 
            this, SLOT(onXScaleChanged()));
 

	
 
    connect(ui->spXmin, SIGNAL(valueChanged(double)),
 
            this, SLOT(onXScaleChanged()));
 

	
 
    // init scale range preset list
 
    for (int nbits = 8; nbits <= 24; nbits++) // signed binary formats
 
    {
 
@@ -92,6 +113,18 @@ PlotControlPanel::PlotControlPanel(QWidg
 

	
 
    QObject::connect(ui->cbRangePresets, SIGNAL(activated(int)),
 
                     this, SLOT(onRangeSelected()));
 

	
 
    // color selector starts disabled until a channel is selected
 
    ui->colorSelector->setColor(QColor(0,0,0,0));
 
    ui->colorSelector->setDisplayMode(color_widgets::ColorPreview::AllAlpha);
 
    ui->colorSelector->setDisabled(true);
 

	
 
    // reset button
 
    resetMenu.addAction(&resetNamesAct);
 
    resetMenu.addAction(&resetColorsAct);
 
    resetMenu.addAction(&showAllAct);
 
    resetAct.setMenu(&resetMenu);
 
    ui->tbReset->setDefaultAction(&resetAct);
 
}
 

	
 
PlotControlPanel::~PlotControlPanel()
 
@@ -163,7 +196,7 @@ void PlotControlPanel::onAutoScaleChecke
 
        ui->spYmin->setEnabled(false);
 
        ui->spYmax->setEnabled(false);
 

	
 
        emit scaleChanged(true); // autoscale
 
        emit yScaleChanged(true); // autoscale
 
    }
 
    else
 
    {
 
@@ -172,30 +205,48 @@ void PlotControlPanel::onAutoScaleChecke
 
        ui->spYmin->setEnabled(true);
 
        ui->spYmax->setEnabled(true);
 

	
 
        emit scaleChanged(false, ui->spYmin->value(), ui->spYmax->value());
 
        emit yScaleChanged(false, ui->spYmin->value(), ui->spYmax->value());
 
    }
 
}
 

	
 
void PlotControlPanel::onYScaleChanged()
 
{
 
    emit scaleChanged(false, ui->spYmin->value(), ui->spYmax->value());
 
    if (!autoScale())
 
    {
 
        emit yScaleChanged(false, ui->spYmin->value(), ui->spYmax->value());
 
    }
 
}
 

	
 
bool PlotControlPanel::autoScale()
 
bool PlotControlPanel::autoScale() const
 
{
 
    return ui->cbAutoScale->isChecked();
 
}
 

	
 
double PlotControlPanel::yMax()
 
double PlotControlPanel::yMax() const
 
{
 
    return ui->spYmax->value();
 
}
 

	
 
double PlotControlPanel::yMin()
 
double PlotControlPanel::yMin() const
 
{
 
    return ui->spYmin->value();
 
}
 

	
 
bool PlotControlPanel::xAxisAsIndex() const
 
{
 
    return ui->cbIndex->isChecked();
 
}
 

	
 
double PlotControlPanel::xMax() const
 
{
 
    return ui->spXmax->value();
 
}
 

	
 
double PlotControlPanel::xMin() const
 
{
 
    return ui->spXmin->value();
 
}
 

	
 
void PlotControlPanel::onRangeSelected()
 
{
 
    Range r = ui->cbRangePresets->currentData().value<Range>();
 
@@ -204,15 +255,116 @@ void PlotControlPanel::onRangeSelected()
 
    ui->cbAutoScale->setChecked(false);
 
}
 

	
 
void PlotControlPanel::setChannelNamesModel(QAbstractItemModel * model)
 
void PlotControlPanel::onIndexChecked(bool checked)
 
{
 
    if (checked)
 
    {
 
        ui->lXmin->setEnabled(false);
 
        ui->lXmax->setEnabled(false);
 
        ui->spXmin->setEnabled(false);
 
        ui->spXmax->setEnabled(false);
 

	
 
        emit xScaleChanged(true); // use index
 
    }
 
    else
 
    {
 
        ui->lXmin->setEnabled(true);
 
        ui->lXmax->setEnabled(true);
 
        ui->spXmin->setEnabled(true);
 
        ui->spXmax->setEnabled(true);
 

	
 
        emit xScaleChanged(false, ui->spXmin->value(), ui->spXmax->value());
 
    }
 
}
 

	
 
void PlotControlPanel::onXScaleChanged()
 
{
 
    if (!xAxisAsIndex())
 
    {
 
        emit xScaleChanged(false, ui->spXmin->value(), ui->spXmax->value());
 
    }
 
}
 

	
 
void PlotControlPanel::setChannelInfoModel(ChannelInfoModel* model)
 
{
 
    ui->lvChannelNames->setModel(model);
 
    ui->tvChannelInfo->setModel(model);
 

	
 
    // channel color selector
 
    connect(ui->tvChannelInfo->selectionModel(), &QItemSelectionModel::currentRowChanged,
 
            [this](const QModelIndex &current, const QModelIndex &previous)
 
            {
 
                // TODO: duplicate with below lambda
 
                QColor color(0,0,0,0); // transparent
 

	
 
                if (current.isValid())
 
                {
 
                    ui->colorSelector->setEnabled(true);
 
                    auto model = ui->tvChannelInfo->model();
 
                    color = model->data(current, Qt::ForegroundRole).value<QColor>();
 
                }
 
                else
 
                {
 
                    ui->colorSelector->setDisabled(true);
 
                }
 

	
 
                // temporarily block signals because `setColor` emits `colorChanged`
 
                bool wasBlocked = ui->colorSelector->blockSignals(true);
 
                ui->colorSelector->setColor(color);
 
                ui->colorSelector->blockSignals(wasBlocked);
 
            });
 

	
 
    connect(ui->tvChannelInfo->selectionModel(), &QItemSelectionModel::selectionChanged,
 
            [this](const QItemSelection & selected, const QItemSelection & deselected)
 
            {
 
                if (!selected.length())
 
                {
 
                    ui->colorSelector->setDisabled(true);
 

	
 
                    // temporarily block signals because `setColor` emits `colorChanged`
 
                    bool wasBlocked = ui->colorSelector->blockSignals(true);
 
                    ui->colorSelector->setColor(QColor(0,0,0,0));
 
                    ui->colorSelector->blockSignals(wasBlocked);
 
                }
 
            });
 

	
 
    connect(ui->colorSelector, &color_widgets::ColorSelector::colorChanged,
 
            [this](QColor color)
 
            {
 
                auto index = ui->tvChannelInfo->selectionModel()->currentIndex();
 
                ui->tvChannelInfo->model()->setData(index, color, Qt::ForegroundRole);
 
            });
 

	
 
    connect(model, &QAbstractItemModel::dataChanged,
 
            [this](const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles = QVector<int> ())
 
            {
 
                auto current = ui->tvChannelInfo->selectionModel()->currentIndex();
 

	
 
                // no current selection
 
                if (!current.isValid()) return;
 

	
 
                auto mod = ui->tvChannelInfo->model();
 
                QColor color = mod->data(current, Qt::ForegroundRole).value<QColor>();
 

	
 
                // temporarily block signals because `setColor` emits `colorChanged`
 
                bool wasBlocked = ui->colorSelector->blockSignals(true);
 
                ui->colorSelector->setColor(color);
 
                ui->colorSelector->blockSignals(wasBlocked);
 
            });
 

	
 
    // reset actions
 
    connect(&resetAct, &QAction::triggered, model, &ChannelInfoModel::resetInfos);
 
    connect(&resetNamesAct, &QAction::triggered, model, &ChannelInfoModel::resetNames);
 
    connect(&resetColorsAct, &QAction::triggered, model, &ChannelInfoModel::resetColors);
 
    connect(&showAllAct, &QAction::triggered, model, &ChannelInfoModel::resetVisibility);
 
}
 

	
 
void PlotControlPanel::saveSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Plot);
 
    settings->setValue(SG_Plot_NumOfSamples, numOfSamples());
 
    settings->setValue(SG_Plot_IndexAsX, xAxisAsIndex());
 
    settings->setValue(SG_Plot_XMax, xMax());
 
    settings->setValue(SG_Plot_XMin, xMin());
 
    settings->setValue(SG_Plot_AutoScale, autoScale());
 
    settings->setValue(SG_Plot_YMax, yMax());
 
    settings->setValue(SG_Plot_YMin, yMin());
 
@@ -224,6 +376,10 @@ void PlotControlPanel::loadSettings(QSet
 
    settings->beginGroup(SettingGroup_Plot);
 
    ui->spNumOfSamples->setValue(
 
        settings->value(SG_Plot_NumOfSamples, numOfSamples()).toInt());
 
    ui->cbIndex->setChecked(
 
        settings->value(SG_Plot_IndexAsX, xAxisAsIndex()).toBool());
 
    ui->spXmax->setValue(settings->value(SG_Plot_XMax, xMax()).toDouble());
 
    ui->spXmin->setValue(settings->value(SG_Plot_XMin, xMin()).toDouble());
 
    ui->cbAutoScale->setChecked(
 
        settings->value(SG_Plot_AutoScale, autoScale()).toBool());
 
    ui->spYmax->setValue(settings->value(SG_Plot_YMax, yMax()).toDouble());
src/plotcontrolpanel.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -21,8 +21,11 @@
 
#define PLOTCONTROLPANEL_H
 

	
 
#include <QWidget>
 
#include <QAbstractItemModel>
 
#include <QSettings>
 
#include <QAction>
 
#include <QMenu>
 

	
 
#include "channelinfomodel.h"
 

	
 
namespace Ui {
 
class PlotControlPanel;
 
@@ -37,11 +40,14 @@ public:
 
    ~PlotControlPanel();
 

	
 
    unsigned numOfSamples();
 
    bool autoScale();
 
    double yMax();
 
    double yMin();
 
    bool   autoScale() const;
 
    double yMax() const;
 
    double yMin() const;
 
    bool   xAxisAsIndex() const;
 
    double xMax() const;
 
    double xMin() const;
 

	
 
    void setChannelNamesModel(QAbstractItemModel * model);
 
    void setChannelInfoModel(ChannelInfoModel* model);
 

	
 
    /// Stores plot settings into a `QSettings`
 
    void saveSettings(QSettings* settings);
 
@@ -50,7 +56,8 @@ public:
 

	
 
signals:
 
    void numOfSamplesChanged(int value);
 
    void scaleChanged(bool autoScaled, double yMin = 0, double yMax = 1);
 
    void yScaleChanged(bool autoScaled, double yMin = 0, double yMax = 1);
 
    void xScaleChanged(bool asIndex, double xMin = 0, double xMax = 1);
 

	
 
private:
 
    Ui::PlotControlPanel *ui;
 
@@ -60,6 +67,9 @@ private:
 
    /// User can disable this setting in the checkbox
 
    bool warnNumOfSamples;
 

	
 
    QAction resetAct, resetNamesAct, resetColorsAct, showAllAct;
 
    QMenu resetMenu;
 

	
 
    /// Show a confirmation dialog before setting #samples to a big value
 
    bool askNSConfirmation(int value);
 

	
 
@@ -68,6 +78,8 @@ private slots:
 
    void onAutoScaleChecked(bool checked);
 
    void onYScaleChanged();
 
    void onRangeSelected();
 
    void onIndexChecked(bool checked);
 
    void onXScaleChanged();
 
};
 

	
 
#endif // PLOTCONTROLPANEL_H
src/plotcontrolpanel.ui
Show inline comments
 
@@ -6,7 +6,7 @@
 
   <rect>
 
    <x>0</x>
 
    <y>0</y>
 
    <width>590</width>
 
    <width>706</width>
 
    <height>187</height>
 
   </rect>
 
  </property>
 
@@ -15,28 +15,108 @@
 
  </property>
 
  <layout class="QHBoxLayout" name="horizontalLayout">
 
   <item>
 
    <layout class="QVBoxLayout" name="verticalLayout">
 
     <item>
 
      <widget class="QLabel" name="label_2">
 
       <property name="styleSheet">
 
        <string notr="true">font-weight: bold;</string>
 
       </property>
 
       <property name="text">
 
        <string>Channel Names:</string>
 
       </property>
 
      </widget>
 
     </item>
 
     <item>
 
      <widget class="QListView" name="lvChannelNames">
 
       <property name="maximumSize">
 
        <size>
 
         <width>16777215</width>
 
         <height>170</height>
 
        </size>
 
       </property>
 
      </widget>
 
     </item>
 
    </layout>
 
    <widget class="QWidget" name="widget" native="true">
 
     <property name="sizePolicy">
 
      <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
 
       <horstretch>0</horstretch>
 
       <verstretch>0</verstretch>
 
      </sizepolicy>
 
     </property>
 
     <layout class="QVBoxLayout" name="verticalLayout_2">
 
      <property name="spacing">
 
       <number>3</number>
 
      </property>
 
      <property name="leftMargin">
 
       <number>0</number>
 
      </property>
 
      <property name="topMargin">
 
       <number>0</number>
 
      </property>
 
      <property name="rightMargin">
 
       <number>0</number>
 
      </property>
 
      <property name="bottomMargin">
 
       <number>0</number>
 
      </property>
 
      <item>
 
       <widget class="QTableView" name="tvChannelInfo">
 
        <property name="sizePolicy">
 
         <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
 
          <horstretch>0</horstretch>
 
          <verstretch>0</verstretch>
 
         </sizepolicy>
 
        </property>
 
        <property name="maximumSize">
 
         <size>
 
          <width>300</width>
 
          <height>170</height>
 
         </size>
 
        </property>
 
        <property name="selectionMode">
 
         <enum>QAbstractItemView::SingleSelection</enum>
 
        </property>
 
        <property name="selectionBehavior">
 
         <enum>QAbstractItemView::SelectRows</enum>
 
        </property>
 
       </widget>
 
      </item>
 
      <item>
 
       <layout class="QHBoxLayout" name="horizontalLayout_2">
 
        <property name="sizeConstraint">
 
         <enum>QLayout::SetMaximumSize</enum>
 
        </property>
 
        <item>
 
         <widget class="color_widgets::ColorSelector" name="colorSelector" native="true">
 
          <property name="sizePolicy">
 
           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
 
            <horstretch>0</horstretch>
 
            <verstretch>0</verstretch>
 
           </sizepolicy>
 
          </property>
 
          <property name="minimumSize">
 
           <size>
 
            <width>20</width>
 
            <height>20</height>
 
           </size>
 
          </property>
 
          <property name="maximumSize">
 
           <size>
 
            <width>20</width>
 
            <height>20</height>
 
           </size>
 
          </property>
 
         </widget>
 
        </item>
 
        <item>
 
         <spacer name="horizontalSpacer">
 
          <property name="orientation">
 
           <enum>Qt::Horizontal</enum>
 
          </property>
 
          <property name="sizeHint" stdset="0">
 
           <size>
 
            <width>1</width>
 
            <height>20</height>
 
           </size>
 
          </property>
 
         </spacer>
 
        </item>
 
        <item>
 
         <widget class="QToolButton" name="tbReset">
 
          <property name="text">
 
           <string>Reset</string>
 
          </property>
 
          <property name="popupMode">
 
           <enum>QToolButton::MenuButtonPopup</enum>
 
          </property>
 
          <property name="arrowType">
 
           <enum>Qt::NoArrow</enum>
 
          </property>
 
         </widget>
 
        </item>
 
       </layout>
 
      </item>
 
     </layout>
 
    </widget>
 
   </item>
 
   <item>
 
    <widget class="Line" name="line">
 
@@ -48,7 +128,7 @@
 
   <item>
 
    <layout class="QFormLayout" name="formLayout_2">
 
     <property name="fieldGrowthPolicy">
 
      <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
 
      <enum>QFormLayout::FieldsStayAtSizeHint</enum>
 
     </property>
 
     <item row="0" column="0">
 
      <widget class="QLabel" name="label_3">
 
@@ -77,6 +157,87 @@
 
      </widget>
 
     </item>
 
     <item row="1" column="0">
 
      <widget class="QCheckBox" name="cbIndex">
 
       <property name="text">
 
        <string>Index as X AXis</string>
 
       </property>
 
       <property name="checked">
 
        <bool>true</bool>
 
       </property>
 
      </widget>
 
     </item>
 
     <item row="1" column="1">
 
      <layout class="QHBoxLayout" name="horizontalLayout_4">
 
       <item>
 
        <widget class="QLabel" name="lXmin">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="text">
 
          <string>Xmin</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QDoubleSpinBox" name="spXmin">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="minimumSize">
 
          <size>
 
           <width>75</width>
 
           <height>0</height>
 
          </size>
 
         </property>
 
         <property name="maximumSize">
 
          <size>
 
           <width>75</width>
 
           <height>16777215</height>
 
          </size>
 
         </property>
 
         <property name="toolTip">
 
          <string>lower limit of Y axis</string>
 
         </property>
 
         <property name="value">
 
          <double>0.000000000000000</double>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QLabel" name="lXmax">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="text">
 
          <string>Xmax</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QDoubleSpinBox" name="spXmax">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="maximumSize">
 
          <size>
 
           <width>75</width>
 
           <height>16777215</height>
 
          </size>
 
         </property>
 
         <property name="toolTip">
 
          <string>upper limit of Y axis</string>
 
         </property>
 
         <property name="maximum">
 
          <double>1000.000000000000000</double>
 
         </property>
 
         <property name="value">
 
          <double>1000.000000000000000</double>
 
         </property>
 
        </widget>
 
       </item>
 
      </layout>
 
     </item>
 
     <item row="2" column="0">
 
      <widget class="QCheckBox" name="cbAutoScale">
 
       <property name="text">
 
        <string>Auto Scale Y Axis</string>
 
@@ -86,81 +247,115 @@
 
       </property>
 
      </widget>
 
     </item>
 
     <item row="2" column="0">
 
      <widget class="QLabel" name="lYmax">
 
       <property name="enabled">
 
        <bool>false</bool>
 
       </property>
 
       <property name="text">
 
        <string>Ymax</string>
 
       </property>
 
      </widget>
 
     <item row="2" column="1">
 
      <layout class="QHBoxLayout" name="horizontalLayout_3">
 
       <item>
 
        <widget class="QLabel" name="lYmin">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="text">
 
          <string>Ymin</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QDoubleSpinBox" name="spYmin">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="minimumSize">
 
          <size>
 
           <width>75</width>
 
           <height>0</height>
 
          </size>
 
         </property>
 
         <property name="maximumSize">
 
          <size>
 
           <width>75</width>
 
           <height>16777215</height>
 
          </size>
 
         </property>
 
         <property name="toolTip">
 
          <string>lower limit of Y axis</string>
 
         </property>
 
         <property name="value">
 
          <double>0.000000000000000</double>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QLabel" name="lYmax">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="text">
 
          <string>Ymax</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QDoubleSpinBox" name="spYmax">
 
         <property name="enabled">
 
          <bool>false</bool>
 
         </property>
 
         <property name="maximumSize">
 
          <size>
 
           <width>75</width>
 
           <height>16777215</height>
 
          </size>
 
         </property>
 
         <property name="toolTip">
 
          <string>upper limit of Y axis</string>
 
         </property>
 
         <property name="maximum">
 
          <double>1000.000000000000000</double>
 
         </property>
 
         <property name="value">
 
          <double>1000.000000000000000</double>
 
         </property>
 
        </widget>
 
       </item>
 
      </layout>
 
     </item>
 
     <item row="2" column="1">
 
      <widget class="QDoubleSpinBox" name="spYmax">
 
       <property name="enabled">
 
        <bool>false</bool>
 
       </property>
 
       <property name="maximumSize">
 
        <size>
 
         <width>75</width>
 
         <height>16777215</height>
 
        </size>
 
       </property>
 
       <property name="toolTip">
 
        <string>upper limit of Y axis</string>
 
       </property>
 
       <property name="maximum">
 
        <double>1000.000000000000000</double>
 
       </property>
 
       <property name="value">
 
        <double>1000.000000000000000</double>
 
       </property>
 
      </widget>
 
     </item>
 
     <item row="3" column="0">
 
      <widget class="QLabel" name="lYmin">
 
       <property name="enabled">
 
        <bool>false</bool>
 
       </property>
 
       <property name="text">
 
        <string>Ymin</string>
 
       </property>
 
      </widget>
 
     </item>
 
     <item row="3" column="1">
 
      <widget class="QDoubleSpinBox" name="spYmin">
 
       <property name="enabled">
 
        <bool>false</bool>
 
       </property>
 
       <property name="maximumSize">
 
        <size>
 
         <width>75</width>
 
         <height>16777215</height>
 
        </size>
 
       </property>
 
       <property name="toolTip">
 
        <string>lower limit of Y axis</string>
 
       </property>
 
       <property name="value">
 
        <double>0.000000000000000</double>
 
       </property>
 
      </widget>
 
     </item>
 
     <item row="4" column="0">
 
     <item row="5" column="0">
 
      <widget class="QLabel" name="label">
 
       <property name="text">
 
        <string>Select Range Preset:</string>
 
       </property>
 
      </widget>
 
     </item>
 
     <item row="4" column="1">
 
     <item row="5" column="1">
 
      <widget class="QComboBox" name="cbRangePresets"/>
 
     </item>
 
    </layout>
 
   </item>
 
   <item>
 
    <spacer name="horizontalSpacer_2">
 
     <property name="orientation">
 
      <enum>Qt::Horizontal</enum>
 
     </property>
 
     <property name="sizeType">
 
      <enum>QSizePolicy::MinimumExpanding</enum>
 
     </property>
 
     <property name="sizeHint" stdset="0">
 
      <size>
 
       <width>1</width>
 
       <height>20</height>
 
      </size>
 
     </property>
 
    </spacer>
 
   </item>
 
  </layout>
 
 </widget>
 
 <customwidgets>
 
  <customwidget>
 
   <class>color_widgets::ColorSelector</class>
 
   <extends>QWidget</extends>
 
   <header>color_selector.hpp</header>
 
   <container>1</container>
 
  </customwidget>
 
 </customwidgets>
 
 <resources/>
 
 <connections/>
 
</ui>
src/plotmanager.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -17,14 +17,14 @@
 
  along with serialplot.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include <QtDebug>
 
#include "qwt_symbol.h"
 

	
 
#include "plot.h"
 
#include "plotmanager.h"
 
#include "utils.h"
 
#include "setting_defines.h"
 

	
 
PlotManager::PlotManager(QWidget* plotArea, QObject *parent) :
 
PlotManager::PlotManager(QWidget* plotArea, ChannelInfoModel* infoModel, QObject *parent) :
 
    QObject(parent),
 
    _plotArea(plotArea),
 
    showGridAction("&Grid", this),
 
@@ -38,6 +38,8 @@ PlotManager::PlotManager(QWidget* plotAr
 
    _yMin = 0;
 
    _yMax = 1;
 
    isDemoShown = false;
 
    _infoModel = infoModel;
 
    _numOfSamples = 1;
 

	
 
    // initalize layout and single widget
 
    isMulti = false;
 
@@ -85,6 +87,21 @@ PlotManager::PlotManager(QWidget* plotAr
 
            this, &PlotManager::showLegend);
 
    connect(&showMultiAction, SELECT<bool>::OVERLOAD_OF(&QAction::triggered),
 
            this, &PlotManager::setMulti);
 

	
 
    // connect to channel info model
 
    if (_infoModel != NULL)     // TODO: remove when snapshots have infomodel
 
    {
 
        connect(_infoModel, &QAbstractItemModel::dataChanged,
 
                this, &PlotManager::onChannelInfoChanged);
 

	
 
        connect(_infoModel, &QAbstractItemModel::modelReset,
 
                [this]()
 
                {
 
                    onChannelInfoChanged(_infoModel->index(0, 0), // start
 
                                         _infoModel->index(_infoModel->rowCount()-1, 0), // end
 
                                         {}); // roles ignored
 
                });
 
    }
 
}
 

	
 
PlotManager::~PlotManager()
 
@@ -103,6 +120,46 @@ PlotManager::~PlotManager()
 
    if (scrollArea != NULL) delete scrollArea;
 
}
 

	
 
void PlotManager::onChannelInfoChanged(const QModelIndex &topLeft,
 
                                       const QModelIndex &bottomRight,
 
                                       const QVector<int> &roles)
 
{
 
    int start = topLeft.row();
 
    int end = bottomRight.row();
 

	
 
    for (int ci = start; ci <= end; ci++)
 
    {
 
        QString name = topLeft.sibling(ci, ChannelInfoModel::COLUMN_NAME).data(Qt::EditRole).toString();
 
        QColor color = topLeft.sibling(ci, ChannelInfoModel::COLUMN_NAME).data(Qt::ForegroundRole).value<QColor>();
 
        bool visible = topLeft.sibling(ci, ChannelInfoModel::COLUMN_VISIBILITY).data(Qt::CheckStateRole).toBool();
 

	
 
        curves[ci]->setTitle(name);
 
        curves[ci]->setPen(color);
 
        curves[ci]->setVisible(visible);
 
        curves[ci]->setItemAttribute(QwtPlotItem::Legend, visible);
 

	
 
        // replot only updated widgets
 
        if (isMulti)
 
        {
 
            plotWidgets[ci]->updateSymbols(); // required for color change
 
            plotWidgets[ci]->updateLegend(curves[ci]);
 
            plotWidgets[ci]->setVisible(visible);
 
            if (visible)
 
            {
 
                plotWidgets[ci]->replot();
 
            }
 
        }
 
    }
 

	
 
    // replot single widget
 
    if (!isMulti)
 
    {
 
        plotWidgets[0]->updateSymbols();
 
        plotWidgets[0]->updateLegend();
 
        replot();
 
    }
 
}
 

	
 
void PlotManager::setMulti(bool enabled)
 
{
 
    if (enabled == isMulti) return;
 
@@ -194,7 +251,16 @@ Plot* PlotManager::addPlotWidget()
 
    plot->showMinorGrid(showMinorGridAction.isChecked());
 
    plot->showLegend(showLegendAction.isChecked());
 
    plot->showDemoIndicator(isDemoShown);
 
    plot->setAxis(_autoScaled, _yMin, _yMax);
 
    plot->setYAxis(_autoScaled, _yMin, _yMax);
 

	
 
    if (_xAxisAsIndex)
 
    {
 
        plot->setXAxis(0, _numOfSamples);
 
    }
 
    else
 
    {
 
        plot->setXAxis(_xMin, _xMax);
 
    }
 

	
 
    return plot;
 
}
 
@@ -202,7 +268,9 @@ Plot* PlotManager::addPlotWidget()
 
void PlotManager::addCurve(QString title, FrameBuffer* buffer)
 
{
 
    auto curve = new QwtPlotCurve(title);
 
    curve->setSamples(new FrameBufferSeries(buffer));
 
    auto series = new FrameBufferSeries(buffer);
 
    series->setXAxis(_xAxisAsIndex, _xMin, _xMax);
 
    curve->setSamples(series);
 
    _addCurve(curve);
 
}
 

	
 
@@ -219,7 +287,8 @@ void PlotManager::_addCurve(QwtPlotCurve
 
    curves.append(curve);
 

	
 
    unsigned index = curves.size()-1;
 
    curve->setPen(Plot::makeColor(index));
 
    auto color = _infoModel->color(index);
 
    curve->setPen(color);
 

	
 
    // create the plot for the curve if we are on multi display
 
    Plot* plot;
 
@@ -346,17 +415,42 @@ void PlotManager::darkBackground(bool en
 
    }
 
}
 

	
 
void PlotManager::setAxis(bool autoScaled, double yAxisMin, double yAxisMax)
 
void PlotManager::setYAxis(bool autoScaled, double yAxisMin, double yAxisMax)
 
{
 
    _autoScaled = autoScaled;
 
    _yMin = yAxisMin;
 
    _yMax = yAxisMax;
 
    for (auto plot : plotWidgets)
 
    {
 
        plot->setAxis(autoScaled, yAxisMin, yAxisMax);
 
        plot->setYAxis(autoScaled, yAxisMin, yAxisMax);
 
    }
 
}
 

	
 
void PlotManager::setXAxis(bool asIndex, double xMin, double xMax)
 
{
 
    _xAxisAsIndex = asIndex;
 
    _xMin = xMin;
 
    _xMax = xMax;
 
    for (auto curve : curves)
 
    {
 
        // TODO: what happens when addCurve(QVector) is used?
 
        FrameBufferSeries* series = static_cast<FrameBufferSeries*>(curve->data());
 
        series->setXAxis(asIndex, xMin, xMax);
 
    }
 
    for (auto plot : plotWidgets)
 
    {
 
        if (asIndex)
 
        {
 
            plot->setXAxis(0, _numOfSamples);
 
        }
 
        else
 
        {
 
            plot->setXAxis(xMin, xMax);
 
        }
 
    }
 
    replot();
 
}
 

	
 
void PlotManager::flashSnapshotOverlay()
 
{
 
    for (auto plot : plotWidgets)
 
@@ -367,9 +461,11 @@ void PlotManager::flashSnapshotOverlay()
 

	
 
void PlotManager::onNumOfSamplesChanged(unsigned value)
 
{
 
    _numOfSamples = value;
 
    for (auto plot : plotWidgets)
 
    {
 
        plot->onNumOfSamplesChanged(value);
 
        if (_xAxisAsIndex) plot->setXAxis(0, value);
 
    }
 
}
 

	
src/plotmanager.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -30,13 +30,14 @@
 
#include <qwt_plot_curve.h>
 
#include "plot.h"
 
#include "framebufferseries.h"
 
#include "channelinfomodel.h"
 

	
 
class PlotManager : public QObject
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit PlotManager(QWidget* plotArea, QObject *parent = 0);
 
    explicit PlotManager(QWidget* plotArea, ChannelInfoModel* infoModel = NULL, QObject *parent = 0);
 
    ~PlotManager();
 
    /// Add a new curve with title and buffer. A color is
 
    /// automatically chosen for curve.
 
@@ -64,7 +65,9 @@ public slots:
 
    /// Enable display of a "DEMO" label on each plot
 
    void showDemoIndicator(bool show = true);
 
    /// Set the Y axis
 
    void setAxis(bool autoScaled, double yMin = 0, double yMax = 1);
 
    void setYAxis(bool autoScaled, double yMin = 0, double yMax = 1);
 
    /// Set the X axis
 
    void setXAxis(bool asIndex, double xMin = 0 , double xMax = 1);
 
    /// Display an animation for snapshot
 
    void flashSnapshotOverlay();
 
    /// Should be called to update zoom base
 
@@ -77,10 +80,15 @@ private:
 
    QScrollArea* scrollArea;
 
    QList<QwtPlotCurve*> curves;
 
    QList<Plot*> plotWidgets;
 
    ChannelInfoModel* _infoModel;
 
    bool isDemoShown;
 
    bool _autoScaled;
 
    double _yMin;
 
    double _yMax;
 
    bool _xAxisAsIndex;
 
    double _xMin;
 
    double _xMax;
 
    unsigned _numOfSamples;
 

	
 
    // menu actions
 
    QAction showGridAction;
 
@@ -104,6 +112,10 @@ private slots:
 
    void showLegend(bool show = true);
 
    void unzoom();
 
    void darkBackground(bool enabled = true);
 

	
 
    void onChannelInfoChanged(const QModelIndex & topLeft,
 
                              const QModelIndex & bottomRight,
 
                              const QVector<int> & roles = QVector<int> ());
 
};
 

	
 
#endif // PLOTMANAGER_H
src/portcontrol.ui
Show inline comments
 
@@ -317,6 +317,24 @@
 
   </item>
 
  </layout>
 
 </widget>
 
 <tabstops>
 
  <tabstop>cbPortList</tabstop>
 
  <tabstop>pbReloadPorts</tabstop>
 
  <tabstop>pbOpenPort</tabstop>
 
  <tabstop>cbBaudRate</tabstop>
 
  <tabstop>rbNoParity</tabstop>
 
  <tabstop>rbOddParity</tabstop>
 
  <tabstop>rbEvenParity</tabstop>
 
  <tabstop>rb8Bits</tabstop>
 
  <tabstop>rb7Bits</tabstop>
 
  <tabstop>rb6Bits</tabstop>
 
  <tabstop>rb5Bits</tabstop>
 
  <tabstop>rb1StopBit</tabstop>
 
  <tabstop>rb2StopBit</tabstop>
 
  <tabstop>rbNoFlowControl</tabstop>
 
  <tabstop>rbHardwareControl</tabstop>
 
  <tabstop>rbSoftwareControl</tabstop>
 
 </tabstops>
 
 <resources/>
 
 <connections/>
 
</ui>
src/recordpanel.cpp
Show inline comments
 
new file 100644
 
/*
 
  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 <QIcon>
 
#include <QFile>
 
#include <QFileInfo>
 
#include <QMessageBox>
 
#include <QFileDialog>
 
#include <QRegularExpression>
 
#include <QtDebug>
 

	
 
#include "recordpanel.h"
 
#include "ui_recordpanel.h"
 
#include "setting_defines.h"
 

	
 
RecordPanel::RecordPanel(DataRecorder* recorder, ChannelManager* channelMan, QWidget *parent) :
 
    QWidget(parent),
 
    ui(new Ui::RecordPanel),
 
    recordToolBar(tr("Record Toolbar")),
 
    recordAction(QIcon::fromTheme("media-record"), tr("Record"), this)
 
{
 
    overwriteSelected = false;
 
    _recorder = recorder;
 
    _channelMan = channelMan;
 

	
 
    ui->setupUi(this);
 

	
 
    recordToolBar.setObjectName("tbRecord");
 

	
 
    recordAction.setCheckable(true);
 
    recordToolBar.addAction(&recordAction);
 
    ui->pbRecord->setDefaultAction(&recordAction);
 

	
 
    connect(ui->pbBrowse, &QPushButton::clicked,
 
            this, &RecordPanel::selectFile);
 
    connect(&recordAction, &QAction::triggered,
 
            this, &RecordPanel::onRecord);
 

	
 
    connect(ui->cbRecordPaused, SIGNAL(toggled(bool)),
 
            this, SIGNAL(recordPausedChanged(bool)));
 

	
 
    connect(ui->cbDisableBuffering, &QCheckBox::toggled,
 
            [this](bool enabled)
 
            {
 
                _recorder->disableBuffering = enabled;
 
            });
 

	
 
    connect(ui->cbWindowsLE, &QCheckBox::toggled,
 
            [this](bool enabled)
 
            {
 
                _recorder->windowsLE = enabled;
 
            });
 

	
 
    connect(&recordAction, &QAction::toggled, ui->cbWindowsLE, &QWidget::setDisabled);
 
}
 

	
 
RecordPanel::~RecordPanel()
 
{
 
    delete ui;
 
}
 

	
 
QToolBar* RecordPanel::toolbar()
 
{
 
    return &recordToolBar;
 
}
 

	
 
bool RecordPanel::isRecording()
 
{
 
    return recordAction.isChecked();
 
}
 

	
 
bool RecordPanel::recordPaused()
 
{
 
    return ui->cbRecordPaused->isChecked();
 
}
 

	
 
bool RecordPanel::selectFile()
 
{
 
    QString fileName = QFileDialog::getSaveFileName(
 
        parentWidget(), tr("Select recording file"));
 

	
 
    if (fileName.isEmpty())
 
    {
 
        return false;
 
    }
 
    else
 
    {
 
        selectedFile = fileName;
 
        ui->lbFileName->setText(selectedFile);
 
        overwriteSelected = QFile::exists(fileName);
 
        return true;
 
    }
 
}
 

	
 
void RecordPanel::onRecord(bool start)
 
{
 
    if (!start)
 
    {
 
        stopRecording();
 
        return;
 
    }
 

	
 
    bool canceled = false;
 
    if (ui->leSeparator->text().isEmpty())
 
    {
 
        QMessageBox::critical(this, "Error",
 
                              "Column separator cannot be empty! Please select a separator.");
 
        ui->leSeparator->setFocus(Qt::OtherFocusReason);
 
        canceled = true;
 
    }
 

	
 
    // check file name
 
    if (!canceled && selectedFile.isEmpty() && !selectFile())
 
    {
 
        canceled = true;
 
    }
 

	
 
    if (!canceled && !overwriteSelected && QFile::exists(selectedFile))
 
    {
 
        if (ui->cbAutoIncrement->isChecked())
 
        {
 
            // TODO: should we increment even if user selected to replace?
 
            canceled = !incrementFileName();
 
        }
 
        else
 
        {
 
            canceled = !confirmOverwrite(selectedFile);
 
        }
 
    }
 

	
 
    if (canceled)
 
    {
 
        recordAction.setChecked(false);
 
    }
 
    else
 
    {
 
        overwriteSelected = false;
 
        startRecording();
 
    }
 
}
 

	
 
bool RecordPanel::incrementFileName(void)
 
{
 
    QFileInfo fileInfo(selectedFile);
 

	
 
    QString base = fileInfo.completeBaseName();
 
    QRegularExpression regex("(.*?)(\\d+)(?!.*\\d)(.*)");
 
    auto match = regex.match(base);
 

	
 
    if (match.hasMatch())
 
    {
 
        bool ok;
 
        int fileNum = match.captured(2).toInt(&ok);
 
        base = match.captured(1) + QString::number(fileNum + 1) + match.captured(3);
 
    }
 
    else
 
    {
 
        base += "_1";
 
    }
 

	
 
    QString suffix = fileInfo.suffix();;
 
    if (!suffix.isEmpty())
 
    {
 
        suffix = "." + suffix;
 
    }
 

	
 
    QString autoFileName = fileInfo.path() + "/" + base + suffix;
 

	
 
    // check if auto generated file name exists, ask user another name
 
    if (QFile::exists(autoFileName))
 
    {
 
        if (!confirmOverwrite(autoFileName))
 
        {
 
            return false;
 
        }
 
    }
 
    else
 
    {
 
        selectedFile = autoFileName;
 
    }
 

	
 
    ui->lbFileName->setText(selectedFile);
 
    return true;
 
}
 

	
 
bool RecordPanel::confirmOverwrite(QString fileName)
 
{
 
    // prepare message box
 
    QMessageBox mb(parentWidget());
 
    mb.setWindowTitle(tr("File Already Exists"));
 
    mb.setIcon(QMessageBox::Warning);
 
    mb.setText(tr("File (%1) already exists. How to continue?").arg(fileName));
 

	
 
    auto bCancel    = mb.addButton(QMessageBox::Cancel);
 
    auto bOverwrite = mb.addButton(tr("Overwrite"), QMessageBox::DestructiveRole);
 
    mb.addButton(tr("Select Another File"), QMessageBox::YesRole);
 

	
 
    mb.setEscapeButton(bCancel);
 

	
 
    // show message box
 
    mb.exec();
 

	
 
    if (mb.clickedButton() == bCancel)
 
    {
 
        return false;
 
    }
 
    else if (mb.clickedButton() == bOverwrite)
 
    {
 
        selectedFile = fileName;
 
        return true;
 
    }
 
    else                    // select button
 
    {
 
        return selectFile();
 
    }
 
}
 

	
 
void RecordPanel::startRecording(void)
 
{
 
    QStringList channelNames;
 
    if (ui->cbHeader->isChecked())
 
    {
 
        channelNames = _channelMan->infoModel()->channelNames();
 
    }
 
    _recorder->startRecording(selectedFile, getSeparator(), channelNames);
 
    emit recordStarted();
 
}
 

	
 
void RecordPanel::stopRecording(void)
 
{
 
    emit recordStopped();
 
    _recorder->stopRecording();
 
}
 

	
 
void RecordPanel::onPortClose()
 
{
 
    if (recordAction.isChecked() && ui->cbStopOnClose->isChecked())
 
    {
 
        stopRecording();
 
        recordAction.setChecked(false);
 
    }
 
}
 

	
 
QString RecordPanel::getSeparator() const
 
{
 
    QString sep = ui->leSeparator->text();
 
    sep.replace("\\t", "\t");
 
    return sep;
 
}
 

	
 
void RecordPanel::saveSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Record);
 
    settings->setValue(SG_Record_AutoIncrement, ui->cbAutoIncrement->isChecked());
 
    settings->setValue(SG_Record_RecordPaused, ui->cbRecordPaused->isChecked());
 
    settings->setValue(SG_Record_StopOnClose, ui->cbStopOnClose->isChecked());
 
    settings->setValue(SG_Record_Header, ui->cbHeader->isChecked());
 
    settings->setValue(SG_Record_DisableBuffering, ui->cbDisableBuffering->isChecked());
 
    settings->setValue(SG_Record_Separator, ui->leSeparator->text());
 
    settings->endGroup();
 
}
 

	
 
void RecordPanel::loadSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Record);
 
    ui->cbAutoIncrement->setChecked(
 
        settings->value(SG_Record_AutoIncrement, ui->cbAutoIncrement->isChecked()).toBool());
 
    ui->cbRecordPaused->setChecked(
 
        settings->value(SG_Record_RecordPaused, ui->cbRecordPaused->isChecked()).toBool());
 
    ui->cbStopOnClose->setChecked(
 
        settings->value(SG_Record_StopOnClose, ui->cbStopOnClose->isChecked()).toBool());
 
    ui->cbHeader->setChecked(
 
        settings->value(SG_Record_Header, ui->cbHeader->isChecked()).toBool());
 
    ui->cbDisableBuffering->setChecked(
 
        settings->value(SG_Record_DisableBuffering, ui->cbDisableBuffering->isChecked()).toBool());
 
    ui->leSeparator->setText(settings->value(SG_Record_Separator, ui->leSeparator->text()).toString());
 
    settings->endGroup();
 
}
src/recordpanel.h
Show inline comments
 
new file 100644
 
/*
 
  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/>.
 
*/
 

	
 
#ifndef RECORDPANEL_H
 
#define RECORDPANEL_H
 

	
 
#include <QWidget>
 
#include <QString>
 
#include <QToolBar>
 
#include <QAction>
 

	
 
#include "datarecorder.h"
 
#include "channelmanager.h"
 

	
 
namespace Ui {
 
class RecordPanel;
 
}
 

	
 
class RecordPanel : public QWidget
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit RecordPanel(DataRecorder* recorder, ChannelManager* channelMan,
 
                         QWidget* parent = 0);
 
    ~RecordPanel();
 

	
 
    QToolBar* toolbar();
 

	
 
    bool isRecording();
 
    bool recordPaused();
 

	
 
    /// Stores settings into a `QSettings`
 
    void saveSettings(QSettings* settings);
 
    /// Loads settings from a `QSettings`.
 
    void loadSettings(QSettings* settings);
 

	
 
signals:
 
    void recordStarted();
 
    void recordStopped();
 
    void recordPausedChanged(bool enabled);
 

	
 
public slots:
 
    /// Must be called when port is closed
 
    void onPortClose();
 

	
 
private:
 
    Ui::RecordPanel *ui;
 
    QToolBar recordToolBar;
 
    QAction recordAction;
 
    QString selectedFile;
 
    bool overwriteSelected;
 
    DataRecorder* _recorder;
 
    ChannelManager* _channelMan;
 

	
 
    /**
 
     * @brief Increments the file name.
 
     *
 
     * If file name doesn't have a number at the end of it, a number is appended
 
     * with underscore starting from 1.
 
     *
 
     * @return false if user cancels
 
     */
 
    bool incrementFileName(void);
 

	
 
    /**
 
     * @brief Used to ask user confirmation if auto generated file
 
     * name exists.
 
     *
 
     * If user confirms overwrite, `selectedFile` is set to
 
     * `fileName`. User is also given option to select file and is
 
     * shown a file select dialog in this case.
 
     *
 
     * @param fileName auto generated file name.
 
     * @return false if user cancels
 
     */
 
    bool confirmOverwrite(QString fileName);
 

	
 
    void startRecording(void);
 
    void stopRecording(void);
 

	
 
    /// Returns separator text from ui. "\t" is converted to TAB
 
    /// character.
 
    QString getSeparator() const;
 

	
 
private slots:
 
    /**
 
     * @brief Opens up the file select dialog
 
     *
 
     * If you cancel the selection operation, currently selected file is not
 
     * changed.
 
     *
 
     * @return true if file selected, false if user cancels
 
     */
 
    bool selectFile();
 

	
 
    void onRecord(bool start);
 

	
 
};
 

	
 
#endif // RECORDPANEL_H
src/recordpanel.ui
Show inline comments
 
new file 100644
 
<?xml version="1.0" encoding="UTF-8"?>
 
<ui version="4.0">
 
 <class>RecordPanel</class>
 
 <widget class="QWidget" name="RecordPanel">
 
  <property name="geometry">
 
   <rect>
 
    <x>0</x>
 
    <y>0</y>
 
    <width>627</width>
 
    <height>261</height>
 
   </rect>
 
  </property>
 
  <property name="windowTitle">
 
   <string>Form</string>
 
  </property>
 
  <layout class="QHBoxLayout" name="horizontalLayout_2">
 
   <item>
 
    <layout class="QVBoxLayout" name="verticalLayout">
 
     <item>
 
      <layout class="QHBoxLayout" name="horizontalLayout">
 
       <item>
 
        <widget class="QPushButton" name="pbBrowse">
 
         <property name="toolTip">
 
          <string>Select record file</string>
 
         </property>
 
         <property name="text">
 
          <string>Browse</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QLabel" name="lbFileName">
 
         <property name="sizePolicy">
 
          <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
 
           <horstretch>0</horstretch>
 
           <verstretch>0</verstretch>
 
          </sizepolicy>
 
         </property>
 
         <property name="text">
 
          <string>Select file...</string>
 
         </property>
 
        </widget>
 
       </item>
 
      </layout>
 
     </item>
 
     <item>
 
      <layout class="QGridLayout" name="gridLayout">
 
       <item row="1" column="1">
 
        <widget class="QCheckBox" name="cbHeader">
 
         <property name="toolTip">
 
          <string>Channel names are written to the first line of record file</string>
 
         </property>
 
         <property name="text">
 
          <string>Write header line</string>
 
         </property>
 
         <property name="checked">
 
          <bool>true</bool>
 
         </property>
 
        </widget>
 
       </item>
 
       <item row="2" column="0">
 
        <widget class="QCheckBox" name="cbRecordPaused">
 
         <property name="toolTip">
 
          <string>Continue recording to file even when plotting is paused</string>
 
         </property>
 
         <property name="text">
 
          <string>Record while paused</string>
 
         </property>
 
         <property name="checked">
 
          <bool>true</bool>
 
         </property>
 
        </widget>
 
       </item>
 
       <item row="3" column="0">
 
        <widget class="QCheckBox" name="cbDisableBuffering">
 
         <property name="toolTip">
 
          <string>Do not buffer when writing to file. Check this if you are using other software to open the file during recording.</string>
 
         </property>
 
         <property name="text">
 
          <string>Disable buffering</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item row="2" column="1">
 
        <widget class="QCheckBox" name="cbStopOnClose">
 
         <property name="text">
 
          <string>Stop recording when port closed</string>
 
         </property>
 
         <property name="checked">
 
          <bool>true</bool>
 
         </property>
 
        </widget>
 
       </item>
 
       <item row="1" column="0">
 
        <widget class="QCheckBox" name="cbAutoIncrement">
 
         <property name="toolTip">
 
          <string>Increments file name automatically everytime a new recording starts</string>
 
         </property>
 
         <property name="text">
 
          <string>Auto increment file name</string>
 
         </property>
 
         <property name="checked">
 
          <bool>true</bool>
 
         </property>
 
        </widget>
 
       </item>
 
       <item row="3" column="1">
 
        <widget class="QCheckBox" name="cbWindowsLE">
 
         <property name="toolTip">
 
          <string>Use CR+LF as line endings. Some windows software may not show lines correctly otherwise. Can't be changed during recording.</string>
 
         </property>
 
         <property name="text">
 
          <string>Windows Style Line Endings</string>
 
         </property>
 
         <property name="checked">
 
          <bool>false</bool>
 
         </property>
 
        </widget>
 
       </item>
 
       <item row="1" column="2">
 
        <spacer name="horizontalSpacer_2">
 
         <property name="orientation">
 
          <enum>Qt::Horizontal</enum>
 
         </property>
 
         <property name="sizeHint" stdset="0">
 
          <size>
 
           <width>1</width>
 
           <height>20</height>
 
          </size>
 
         </property>
 
        </spacer>
 
       </item>
 
      </layout>
 
     </item>
 
     <item>
 
      <layout class="QHBoxLayout" name="horizontalLayout_3">
 
       <item>
 
        <widget class="QLabel" name="label">
 
         <property name="text">
 
          <string>Column Separator:</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <widget class="QLineEdit" name="leSeparator">
 
         <property name="maximumSize">
 
          <size>
 
           <width>30</width>
 
           <height>16777215</height>
 
          </size>
 
         </property>
 
         <property name="toolTip">
 
          <string>For TAB character enter \t</string>
 
         </property>
 
         <property name="text">
 
          <string>,</string>
 
         </property>
 
        </widget>
 
       </item>
 
       <item>
 
        <spacer name="horizontalSpacer">
 
         <property name="orientation">
 
          <enum>Qt::Horizontal</enum>
 
         </property>
 
         <property name="sizeHint" stdset="0">
 
          <size>
 
           <width>40</width>
 
           <height>20</height>
 
          </size>
 
         </property>
 
        </spacer>
 
       </item>
 
      </layout>
 
     </item>
 
     <item>
 
      <spacer name="verticalSpacer">
 
       <property name="orientation">
 
        <enum>Qt::Vertical</enum>
 
       </property>
 
       <property name="sizeHint" stdset="0">
 
        <size>
 
         <width>20</width>
 
         <height>1</height>
 
        </size>
 
       </property>
 
      </spacer>
 
     </item>
 
    </layout>
 
   </item>
 
   <item>
 
    <layout class="QVBoxLayout" name="verticalLayout_2">
 
     <item>
 
      <widget class="QToolButton" name="pbRecord">
 
       <property name="minimumSize">
 
        <size>
 
         <width>85</width>
 
         <height>50</height>
 
        </size>
 
       </property>
 
       <property name="toolTip">
 
        <string>Start/Stop Recording</string>
 
       </property>
 
       <property name="text">
 
        <string>Record</string>
 
       </property>
 
      </widget>
 
     </item>
 
     <item>
 
      <spacer name="verticalSpacer_2">
 
       <property name="orientation">
 
        <enum>Qt::Vertical</enum>
 
       </property>
 
       <property name="sizeHint" stdset="0">
 
        <size>
 
         <width>20</width>
 
         <height>1</height>
 
        </size>
 
       </property>
 
      </spacer>
 
     </item>
 
    </layout>
 
   </item>
 
  </layout>
 
 </widget>
 
 <resources/>
 
 <connections/>
 
</ui>
src/scalepicker.cpp
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -23,6 +23,7 @@
 
#include <qwt_scale_widget.h>
 
#include <qwt_scale_map.h>
 
#include <qwt_scale_div.h>
 
#include <qwt_text.h>
 
#include <math.h>
 

	
 
#include "scalepicker.h"
 
@@ -107,7 +108,7 @@ bool ScalePicker::eventFilter(QObject* o
 
        {
 
            for (auto sp : snapPoints)
 
            {
 
                if (fabs(posPx-sp) <= SNAP_DISTANCE)
 
                if (std::abs(posPx-sp) <= SNAP_DISTANCE)
 
                {
 
                    posPx = sp;
 
                    break;
 
@@ -132,13 +133,8 @@ bool ScalePicker::eventFilter(QObject* o
 
            if (!started && pressed && (fabs(posPx-firstPosPx) > MIN_PICK_SIZE))
 
            {
 
                started = true;
 
                emit pickStarted(pos);
 
            }
 
            else if (started)
 
            {
 
                pickerOverlay->updateOverlay();
 
                emit picking(firstPos, pos);
 
            }
 
            pickerOverlay->updateOverlay();
 
            scaleOverlay->updateOverlay();
 
        }
 
        else // event->type() == QEvent::MouseButtonRelease
 
@@ -148,14 +144,20 @@ bool ScalePicker::eventFilter(QObject* o
 
            {
 
                // finalize
 
                started = false;
 
                emit picked(firstPos, pos);
 
                if (firstPos != pos) // ignore 0 width zoom
 
                {
 
                    emit picked(firstPos, pos);
 
                }
 
            }
 
            pickerOverlay->updateOverlay();
 
            scaleOverlay->updateOverlay();
 
        }
 
        return true;
 
    }
 
    else if (event->type() == QEvent::Leave)
 
    {
 
        scaleOverlay->updateOverlay();
 
        pickerOverlay->updateOverlay();
 
        return true;
 
    }
 
    else
 
@@ -164,54 +166,222 @@ bool ScalePicker::eventFilter(QObject* o
 
    }
 
}
 

	
 
const int TEXT_MARGIN = 4;
 

	
 
void ScalePicker::drawPlotOverlay(QPainter* painter)
 
{
 
    const double FILL_ALPHA = 0.2;
 

	
 
    painter->save();
 
    painter->setPen(_pen);
 

	
 
    if (started)
 
    {
 
        painter->save();
 
        painter->setPen(_pen);
 
        QColor color = _pen.color();
 
        color.setAlphaF(FILL_ALPHA);
 
        painter->setBrush(color);
 

	
 
        QRect rect;
 
        QwtText text = trackerText();
 
        auto tSize = text.textSize(painter->font());
 

	
 
        if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale ||
 
            _scaleWidget->alignment() == QwtScaleDraw::TopScale)
 
        {
 
            int height = painter->device()->height();
 
            rect = QRect(posCanvasPx(firstPosPx), 0, currentPosPx-firstPosPx, height);
 
            int canvasHeight = painter->device()->height();
 
            int pickWidth = currentPosPx-firstPosPx;
 
            rect = QRect(posCanvasPx(firstPosPx), 0, pickWidth, canvasHeight);
 
        }
 
        else // vertical
 
        {
 
            int width = painter->device()->width();
 
            rect = QRect(0, posCanvasPx(firstPosPx), width, currentPosPx-firstPosPx);
 
            int canvasWidth = painter->device()->width();
 
            int pickHeight = currentPosPx-firstPosPx;
 
            rect = QRect(0, posCanvasPx(firstPosPx), canvasWidth, pickHeight);
 
        }
 
        painter->drawRect(rect);
 
        painter->restore();
 
        text.draw(painter, pickTrackerTextRect(painter, rect, tSize));
 
    }
 
    else if (_scaleWidget->underMouse())
 
    {
 
        // draw tracker text centered on cursor
 
        QwtText text = trackerText();
 
        auto tsize = text.textSize(painter->font());
 
        text.draw(painter, trackerTextRect(painter, currentPosPx, tsize));
 
    }
 
    painter->restore();
 
}
 

	
 
QwtText ScalePicker::trackerText() const
 
{
 
    double pos;
 
    // use stored value if snapped to restore precision
 
    if (snapPointMap.contains(currentPosPx))
 
    {
 
        pos = snapPointMap[currentPosPx];
 
    }
 
    else
 
    {
 
        pos = position(currentPosPx);
 
    }
 

	
 
    return QwtText(QString("%1").arg(pos));
 
}
 

	
 
QRectF ScalePicker::trackerTextRect(QPainter* painter, int posPx, QSizeF textSize) const
 
{
 
    int canvasPosPx = posCanvasPx(posPx);
 
    QPointF topLeft;
 

	
 
    if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale ||
 
        _scaleWidget->alignment() == QwtScaleDraw::TopScale)
 
    {
 
        int left = canvasPosPx - textSize.width() / 2;
 
        int canvasWidth = painter->device()->width();
 
        left = std::max(TEXT_MARGIN, left);
 
        left = std::min(double(left), canvasWidth - textSize.width() - TEXT_MARGIN);
 
        int top = 0;
 
        if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale)
 
        {
 
            top = painter->device()->height() - textSize.height();
 
        }
 
        topLeft = QPointF(left, top);
 
    }
 
    else                        // left/right scales
 
    {
 
        int top = canvasPosPx-textSize.height() / 2;
 
        int canvasHeight = painter->device()->height();
 
        top = std::max(0, top);
 
        top = std::min(double(top), canvasHeight - textSize.height());
 
        int left = TEXT_MARGIN;
 
        if (_scaleWidget->alignment() == QwtScaleDraw::RightScale)
 
        {
 
            left = painter->device()->width() - textSize.width();
 
        }
 
        topLeft = QPointF(left, top);
 
    }
 
    return QRectF(topLeft, textSize);
 
}
 

	
 
QRectF ScalePicker::pickTrackerTextRect(QPainter* painter, QRect pickRect, QSizeF textSize) const
 
{
 
    qreal left = 0;
 
    int pickLength = currentPosPx - firstPosPx;
 
    QPointF topLeft;
 

	
 
    if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale ||
 
        _scaleWidget->alignment() == QwtScaleDraw::TopScale)
 
    {
 
        int canvasWidth = painter->device()->width();
 

	
 
        if (pickLength > 0)
 
        {
 
            left = pickRect.right() + TEXT_MARGIN;
 
        }
 
        else
 
        {
 
            left = pickRect.right() - (textSize.width() + TEXT_MARGIN);
 
        }
 

	
 
        // make sure text is not off the canvas
 
        if (left < TEXT_MARGIN)
 
        {
 
            left = std::max(0, pickRect.right()) + TEXT_MARGIN;
 
        }
 
        else if (left + textSize.width() + TEXT_MARGIN > canvasWidth)
 
        {
 
            left = std::min(pickRect.right(), canvasWidth) - (textSize.width() + TEXT_MARGIN);
 
        }
 

	
 
        if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale)
 
        {
 
            int canvasHeight = painter->device()->height();
 
            topLeft = QPointF(left, canvasHeight - textSize.height());
 
        }
 
        else                // top scale
 
        {
 
            topLeft = QPointF(left, 0);
 
        }
 
    }
 
    else                        // left/right scale
 
    {
 
        int canvasHeight = painter->device()->height();
 

	
 
        int top = 0;
 
        if (pickLength > 0)
 
        {
 
            top = pickRect.bottom();
 
        }
 
        else
 
        {
 
            top = pickRect.bottom() - textSize.height();
 
        }
 

	
 
        // make sure text is not off the canvas
 
        if (top < 0)
 
        {
 
            top = std::max(0, pickRect.bottom());
 
        }
 
        else if (top + textSize.height() > canvasHeight)
 
        {
 
            top = std::min(canvasHeight, pickRect.bottom()) - textSize.height();
 
        }
 

	
 
        if (_scaleWidget->alignment() == QwtScaleDraw::LeftScale)
 
        {
 
            topLeft = QPointF(TEXT_MARGIN, top);
 
        }
 
        else                    // right scale
 
        {
 
            int canvasWidth = painter->device()->width();
 
            topLeft = QPointF(canvasWidth - textSize.width() - TEXT_MARGIN, top);
 
        }
 
    }
 
    return QRectF(topLeft, textSize);
 
}
 

	
 
void ScalePicker::drawScaleOverlay(QPainter* painter)
 
{
 
    painter->save();
 
    painter->setPen(_pen);
 

	
 
    if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale ||
 
        _scaleWidget->alignment() == QwtScaleDraw::TopScale)
 
    {
 
        int height = painter->device()->height();
 
        if (started) painter->drawLine(firstPosPx, 0, firstPosPx, height);
 
        if (started || _scaleWidget->underMouse())
 
        {
 
            painter->drawLine(currentPosPx, 0, currentPosPx, height);
 
        }
 
    }
 
    else // vertical
 
    // rotate & adjust coordinate system for vertical drawing
 
    if (_scaleWidget->alignment() == QwtScaleDraw::LeftScale ||
 
        _scaleWidget->alignment() == QwtScaleDraw::RightScale) // vertical
 
    {
 
        int width = painter->device()->width();
 
        if (started) painter->drawLine(0, firstPosPx, width, firstPosPx);
 
        if (started || _scaleWidget->underMouse())
 
        painter->rotate(90);
 
        painter->translate(0, -width);
 
    }
 

	
 
    // draw the indicators
 
    if (started) drawTriangle(painter, firstPosPx);
 
    if (started || _scaleWidget->underMouse())
 
    {
 
        drawTriangle(painter, currentPosPx);
 
    }
 

	
 
    painter->restore();
 
}
 

	
 
void ScalePicker::drawTriangle(QPainter* painter, int position)
 
{
 
    const double tan60 = 1.732;
 
    const double trsize = 10;
 
    const int TRIANGLE_NUM_POINTS = 3;
 
    const int MARGIN = 2;
 
    const QPointF points[TRIANGLE_NUM_POINTS] =
 
        {
 
            painter->drawLine(0, currentPosPx, width, currentPosPx);
 
        }
 
    }
 
            {0, 0},
 
            {-trsize/tan60 , trsize},
 
            {trsize/tan60 , trsize}
 
        };
 

	
 
    painter->save();
 
    painter->setPen(Qt::NoPen);
 
    painter->setBrush(_scaleWidget->palette().windowText());
 
    painter->setRenderHint(QPainter::Antialiasing);
 

	
 
    painter->translate(position, MARGIN);
 
    painter->drawPolygon(points, TRIANGLE_NUM_POINTS);
 

	
 
    painter->restore();
 
}
 
@@ -222,7 +392,7 @@ void ScalePicker::setPen(QPen pen)
 
}
 

	
 
// convert the position of the click to the plot coordinates
 
double ScalePicker::position(double posPx)
 
double ScalePicker::position(double posPx) const
 
{
 
    return _scaleWidget->scaleDraw()->scaleMap().invTransform(posPx);
 
}
 
@@ -248,7 +418,7 @@ int ScalePicker::positionPx(QMouseEvent*
 
 * when drawing the tracker lines. This function maps scale widgets
 
 * pixel coordinate to canvas' coordinate.
 
 */
 
double ScalePicker::posCanvasPx(double pos)
 
double ScalePicker::posCanvasPx(double pos) const
 
{
 
    // assumption: scale.width < canvas.width && scale.x > canvas.x
 
    if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale ||
 
@@ -270,9 +440,12 @@ void ScalePicker::updateSnapPoints()
 
        _scaleWidget->scaleDraw()->scaleDiv().ticks(QwtScaleDiv::MinorTick);
 

	
 
    snapPoints.clear();
 
    snapPointMap.clear();
 
    for(auto t : allTicks)
 
    {
 
        // `round` is used because `allTicks` is double but `snapPoints` is int
 
        snapPoints << round(_scaleWidget->scaleDraw()->scaleMap().transform(t));
 
        int p = round(_scaleWidget->scaleDraw()->scaleMap().transform(t));
 
        snapPoints << p;
 
        snapPointMap[p] = t;
 
    }
 
}
src/scalepicker.h
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -25,6 +25,7 @@
 
#include <QPen>
 
#include <QWidget>
 
#include <QList>
 
#include <QMap>
 
#include <qwt_scale_widget.h>
 
#include <qwt_widget_overlay.h>
 

	
 
@@ -41,8 +42,6 @@ public:
 
    void setPen(QPen pen);
 

	
 
signals:
 
    void pickStarted(double pos);
 
    void picking(double firstPos, double lastPos);
 
    void picked(double firstPos, double lastPos);
 

	
 
private:
 
@@ -58,10 +57,18 @@ private:
 
    double firstPosPx; // pixel coordinates
 
    double currentPosPx; // current position in pixel coordinates
 
    QList<int> snapPoints;
 
    /// used to restore precision of snappoints that is lost due to rounding
 
    QMap<int, double> snapPointMap;
 

	
 
    double position(double); // returns the axis mouse position relative to plot coordinates
 
    double position(double) const; // returns the axis mouse position relative to plot coordinates
 
    int positionPx(QMouseEvent*); // returns the axis mouse position in pixels
 
    double posCanvasPx(double pos); // returns the given position in canvas coordinates
 
    double posCanvasPx(double pos) const; // returns the given position in canvas coordinates
 
    void drawTriangle(QPainter* painter, int position);
 
    QwtText trackerText() const;
 
     /// Returns tracker text position
 
    QRectF trackerTextRect(QPainter* painter, int posPx, QSizeF textSize) const;
 
    /// Returns the text position for tracker text shown during picking
 
    QRectF pickTrackerTextRect(QPainter* painter, QRect pickRect, QSizeF textSize) const;
 

	
 
private slots:
 
    void updateSnapPoints();
src/setting_defines.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -29,6 +29,7 @@ const char SettingGroup_CustomFrame[] = 
 
const char SettingGroup_Channels[] = "Channels";
 
const char SettingGroup_Plot[] = "Plot";
 
const char SettingGroup_Commands[] = "Commands";
 
const char SettingGroup_Record[] = "Record";
 

	
 
// mainwindow setting keys
 
const char SG_MainWindow_Size[] = "size";
 
@@ -70,9 +71,14 @@ const char SG_CustomFrame_DebugMode[] = 
 
// channel manager keys
 
const char SG_Channels_Channel[] = "channel";
 
const char SG_Channels_Name[] = "name";
 
const char SG_Channels_Color[] = "color";
 
const char SG_Channels_Visible[] = "visible";
 

	
 
// plot settings keys
 
const char SG_Plot_NumOfSamples[] = "numOfSamples";
 
const char SG_Plot_IndexAsX[] = "indexAsX";
 
const char SG_Plot_XMax[] = "xMax";
 
const char SG_Plot_XMin[] = "xMin";
 
const char SG_Plot_AutoScale[] = "autoScale";
 
const char SG_Plot_YMax[] = "yMax";
 
const char SG_Plot_YMin[] = "yMin";
 
@@ -88,4 +94,12 @@ const char SG_Commands_Name[] = "name";
 
const char SG_Commands_Type[] = "type";
 
const char SG_Commands_Data[] = "data";
 

	
 
// record panel settings keys
 
const char SG_Record_AutoIncrement[]    = "autoIncrement";
 
const char SG_Record_RecordPaused[]     = "recordPaused";
 
const char SG_Record_StopOnClose[]      = "stopOnClose";
 
const char SG_Record_Header[]           = "header";
 
const char SG_Record_Separator[]        = "separator";
 
const char SG_Record_DisableBuffering[] = "disableBuffering";
 

	
 
#endif // SETTING_DEFINES_H
src/snapshot.cpp
Show inline comments
 
@@ -24,8 +24,9 @@
 
#include "snapshot.h"
 
#include "snapshotview.h"
 

	
 
Snapshot::Snapshot(QMainWindow* parent, QString name) :
 
Snapshot::Snapshot(QMainWindow* parent, QString name, ChannelInfoModel infoModel) :
 
    QObject(parent),
 
    cInfoModel(infoModel),
 
    _showAction(this),
 
    _deleteAction("&Delete", this)
 
{
 
@@ -106,14 +107,14 @@ void Snapshot::setName(QString name)
 
    emit nameChanged(this);
 
}
 

	
 
void Snapshot::setChannelNames(QStringList names)
 
ChannelInfoModel* Snapshot::infoModel()
 
{
 
    _channelNames = names;
 
    return &cInfoModel;
 
}
 

	
 
QString Snapshot::channelName(unsigned channel)
 
{
 
    return _channelNames[channel];
 
    return cInfoModel.name(channel);
 
}
 

	
 
void Snapshot::save(QString fileName)
src/snapshot.h
Show inline comments
 
@@ -27,6 +27,8 @@
 
#include <QString>
 
#include <QStringList>
 

	
 
#include "channelinfomodel.h"
 

	
 
class SnapshotView;
 

	
 
class Snapshot : public QObject
 
@@ -34,7 +36,7 @@ class Snapshot : public QObject
 
    Q_OBJECT
 

	
 
public:
 
    Snapshot(QMainWindow* parent, QString name);
 
    Snapshot(QMainWindow* parent, QString name, ChannelInfoModel infoModel);
 
    ~Snapshot();
 

	
 
    QVector<QVector<QPointF>> data;
 
@@ -43,8 +45,8 @@ public:
 

	
 
    QString name();
 
    QString displayName(); ///< `name()` plus '*' if snapshot is not saved
 
    ChannelInfoModel* infoModel();
 
    void setName(QString name);
 
    void setChannelNames(QStringList names); // must be called when setting data!
 
    QString channelName(unsigned channel);
 

	
 
    void save(QString fileName); ///< save snapshot data as CSV
 
@@ -56,7 +58,7 @@ signals:
 

	
 
private:
 
    QString _name;
 
    QStringList _channelNames;
 
    ChannelInfoModel cInfoModel;
 
    QAction _showAction;
 
    QAction _deleteAction;
 
    QMainWindow* mainWindow;
src/snapshotmanager.cpp
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -24,6 +24,7 @@
 
#include <QFile>
 
#include <QVector>
 
#include <QPointF>
 
#include <QIcon>
 
#include <QtDebug>
 

	
 
#include "snapshotmanager.h"
 
@@ -40,6 +41,7 @@ SnapshotManager::SnapshotManager(QMainWi
 

	
 
    _takeSnapshotAction.setToolTip("Take a snapshot of current plot");
 
    _takeSnapshotAction.setShortcut(QKeySequence("F5"));
 
    _takeSnapshotAction.setIcon(QIcon::fromTheme("camera"));
 
    loadSnapshotAction.setToolTip("Load snapshots from CSV files");
 
    clearAction.setToolTip("Delete all snapshots");
 
    connect(&_takeSnapshotAction, SIGNAL(triggered(bool)),
 
@@ -63,7 +65,7 @@ SnapshotManager::~SnapshotManager()
 
Snapshot* SnapshotManager::makeSnapshot()
 
{
 
    QString name = QTime::currentTime().toString("'Snapshot ['HH:mm:ss']'");
 
    auto snapshot = new Snapshot(_mainWindow, name);
 
    auto snapshot = new Snapshot(_mainWindow, name, *(_channelMan->infoModel()));
 

	
 
    unsigned numOfChannels = _channelMan->numOfChannels();
 
    unsigned numOfSamples = _channelMan->numOfSamples();
 
@@ -76,7 +78,6 @@ Snapshot* SnapshotManager::makeSnapshot(
 
            snapshot->data[ci][i] = QPointF(i, _channelMan->channelBuffer(ci)->sample(i));
 
        }
 
    }
 
    snapshot->setChannelNames(_channelMan->channelNames()->stringList());
 

	
 
    return snapshot;
 
}
 
@@ -189,9 +190,11 @@ void SnapshotManager::loadSnapshotFromFi
 
        lineNum++;
 
    }
 

	
 
    auto snapshot = new Snapshot(_mainWindow, QFileInfo(fileName).baseName());
 
    ChannelInfoModel channelInfo(channelNames);
 

	
 
    auto snapshot = new Snapshot(
 
        _mainWindow, QFileInfo(fileName).baseName(), ChannelInfoModel(channelNames));
 
    snapshot->data = data;
 
    snapshot->setChannelNames(channelNames);
 

	
 
    addSnapshot(snapshot, false);
 
}
src/snapshotview.cpp
Show inline comments
 
@@ -29,7 +29,7 @@ SnapshotView::SnapshotView(QWidget *pare
 

	
 
    ui->setupUi(this);
 

	
 
    plotMan = new PlotManager(ui->plotArea);
 
    plotMan = new PlotManager(ui->plotArea, snapshot->infoModel());
 

	
 
    ui->menuSnapshot->insertAction(ui->actionClose, snapshot->deleteAction());
 
    this->setWindowTitle(snapshot->displayName());
 
@@ -46,7 +46,7 @@ SnapshotView::SnapshotView(QWidget *pare
 
    connect(ui->actionRename, &QAction::triggered,
 
            this, &SnapshotView::showRenameDialog);
 

	
 
    connect(ui->actionExport, &QAction::triggered,
 
    connect(ui->actionSave, &QAction::triggered,
 
            this, &SnapshotView::save);
 

	
 
    // add 'View' menu items
src/snapshotview.ui
Show inline comments
 
@@ -26,7 +26,7 @@
 
     <x>0</x>
 
     <y>0</y>
 
     <width>544</width>
 
     <height>20</height>
 
     <height>25</height>
 
    </rect>
 
   </property>
 
   <widget class="QMenu" name="menuSnapshot">
 
@@ -34,7 +34,7 @@
 
     <string>&amp;Snapshot</string>
 
    </property>
 
    <addaction name="actionRename"/>
 
    <addaction name="actionExport"/>
 
    <addaction name="actionSave"/>
 
    <addaction name="actionClose"/>
 
   </widget>
 
   <widget class="QMenu" name="menuView">
 
@@ -45,9 +45,9 @@
 
   <addaction name="menuSnapshot"/>
 
   <addaction name="menuView"/>
 
  </widget>
 
  <action name="actionExport">
 
  <action name="actionSave">
 
   <property name="text">
 
    <string>&amp;Export CSV</string>
 
    <string>&amp;Save as CSV</string>
 
   </property>
 
   <property name="toolTip">
 
    <string>Save snapshot as CSV file</string>
src/version.h
Show inline comments
 
new file 100644
 
/*
 
  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/>.
 
*/
 

	
 
#ifndef VERSION_H
 
#define VERSION_H
 

	
 
#ifndef VERSION_STRING
 
#define VERSION_STRING   "0.9.0"
 
#endif
 

	
 
#ifndef VERSION_REVISION
 
#define VERSION_REVISION ""
 
#endif
 

	
 
#endif // VERSION_H
src/version.h.in
Show inline comments
 
/*
 
  Copyright © 2015 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -20,9 +20,6 @@
 
#ifndef VERSION_H
 
#define VERSION_H
 

	
 
#define MAJOR_VERSION    @MAJOR_VERSION@
 
#define MINOR_VERSION    @MINOR_VERSION@
 
#define PATCH_VERSION    @PATCH_VERSION@
 
#define VERSION_STRING   "@VERSION_STRING@"
 
#define VERSION_REVISION "@VERSION_REVISION@"
 

	
src/zoomer.cpp
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -21,9 +21,13 @@
 
#include <qwt_plot.h>
 
#include <QtDebug>
 

	
 
#include <QMouseEvent>
 

	
 
Zoomer::Zoomer(QWidget* widget, bool doReplot) :
 
    ScrollZoomer(widget)
 
{
 
    is_panning = false;
 

	
 
    // set corner widget between the scrollbars with default background color
 
    auto cornerWidget = new QWidget();
 
    auto bgColor = cornerWidget->palette().color(QPalette::Window).name();
 
@@ -52,3 +56,88 @@ void Zoomer::zoom( const QRectF & rect)
 

	
 
    ScrollZoomer::zoom(rect);
 
}
 

	
 
QwtText Zoomer::trackerTextF(const QPointF& pos) const
 
{
 
    QwtText b = ScrollZoomer::trackerTextF(pos);
 

	
 
    const QPolygon pa = selection();
 
    if (pa.count() < 2)
 
    {
 
        return b;
 
    }
 

	
 
    const QRectF rect = invTransform(QRect(pa.first(), pa.last()).normalized());
 

	
 
    QString sizeText = QString(" [%1, %2]").\
 
        arg(rect.width(), 0, 'g', 4).\
 
        arg(rect.height(), 0, 'g', 4);
 

	
 
    b.setText(b.text() + sizeText);
 

	
 
    return b;
 
}
 

	
 
void Zoomer::drawRubberBand(QPainter* painter) const
 
{
 
    const double FILL_ALPHA = 0.2;
 

	
 
    QColor color = painter->pen().color();
 
    color.setAlphaF(FILL_ALPHA);
 
    painter->setBrush(color);
 

	
 
    ScrollZoomer::drawRubberBand(painter);
 
}
 

	
 
QRegion Zoomer::rubberBandMask() const
 
{
 
    const QPolygon pa = selection();
 
    if (pa.count() < 2)
 
    {
 
        return QRegion();
 
    }
 
    const QRect r = QRect(pa.first(), pa.last()).normalized().adjusted(0, 0, 1, 1);
 
    return QRegion(r);
 
}
 

	
 
void Zoomer::widgetMousePressEvent(QMouseEvent* mouseEvent)
 
{
 
    if (mouseEvent->modifiers() & Qt::ControlModifier)
 
    {
 
        is_panning = true;
 
        parentWidget()->setCursor(Qt::ClosedHandCursor);
 
        pan_point = invTransform(mouseEvent->pos());
 
    }
 
    else
 
    {
 
        ScrollZoomer::widgetMousePressEvent(mouseEvent);
 
    }
 
}
 

	
 
void Zoomer::widgetMouseMoveEvent(QMouseEvent* mouseEvent)
 
{
 
    if (is_panning)
 
    {
 
        auto cur_point = invTransform(mouseEvent->pos());
 
        auto delta = cur_point - pan_point;
 
        moveBy(-delta.x(), -delta.y());
 
        pan_point = invTransform(mouseEvent->pos());
 
    }
 
    else
 
    {
 
        ScrollZoomer::widgetMouseMoveEvent(mouseEvent);
 
    }
 
}
 

	
 
void Zoomer::widgetMouseReleaseEvent(QMouseEvent* mouseEvent)
 
{
 
    if (is_panning)
 
    {
 
        is_panning = false;
 
        parentWidget()->setCursor(Qt::CrossCursor);
 
    }
 
    else
 
    {
 
        ScrollZoomer::widgetMouseReleaseEvent(mouseEvent);
 
    }
 
}
src/zoomer.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  Copyright © 2017 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -33,6 +33,24 @@ public:
 

	
 
signals:
 
    void unzoomed();
 

	
 
protected:
 
    /// Re-implemented to display selection size in the tracker text.
 
    QwtText trackerTextF(const QPointF &pos) const;
 
    /// Re-implemented for alpha background
 
    void drawRubberBand(QPainter* painter) const;
 
    /// Re-implemented for alpha background (masking is basically disabled)
 
    QRegion rubberBandMask() const;
 
    /// Overloaded for panning
 
    void widgetMousePressEvent(QMouseEvent* mouseEvent);
 
    /// Overloaded for panning
 
    void widgetMouseReleaseEvent(QMouseEvent* mouseEvent);
 
    /// Overloaded for panning
 
    void widgetMouseMoveEvent(QMouseEvent* mouseEvent);
 

	
 
private:
 
    bool is_panning;
 
    QPointF pan_point;
 
};
 

	
 
#endif // ZOOMER_H
0 comments (0 inline, 0 general)