# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2017-01-30 04:06:49 # Node ID cd06dee2a8f4f00d55fb7a36b3a8aeb5c19a6e60 # Parent 1c96dc8ca6536a243284eb5fb90918b5c418fd8f # Parent 840ec925ba83ba4ee382946cdf13dca7363b7fb7 Merge with channelmanager diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,8 +54,10 @@ else (BUILD_QWT) endif (QWT_USE_STATIC) endif (BUILD_QWT) +include(BuildQColorWidgets) + # includes -include_directories("./src" ${QWT_INCLUDE_DIR}) +include_directories("./src" ${QWT_INCLUDE_DIR} ${QCW_INCLUDE_DIR}) # wrap UI and resource files qt5_wrap_ui(UI_FILES @@ -103,6 +105,7 @@ add_executable(${PROGRAM_NAME} WIN32 src/tooltipfilter.cpp src/sneakylineedit.cpp src/channelmanager.cpp + src/channelinfomodel.cpp src/framebufferseries.cpp src/numberformatbox.cpp src/endiannessbox.cpp @@ -122,7 +125,7 @@ add_executable(${PROGRAM_NAME} WIN32 ) # Use the Widgets module from Qt 5. -target_link_libraries(${PROGRAM_NAME} ${QWT_LIBRARY}) +target_link_libraries(${PROGRAM_NAME} ${QWT_LIBRARY} ${QCW_LIBRARY}) qt5_use_modules(${PROGRAM_NAME} Widgets SerialPort) if (BUILD_QWT) diff --git a/cmake/modules/BuildQColorWidgets.cmake b/cmake/modules/BuildQColorWidgets.cmake new file mode 100644 --- /dev/null +++ b/cmake/modules/BuildQColorWidgets.cmake @@ -0,0 +1,31 @@ +# +# 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 . +# + +include(ExternalProject) + +ExternalProject_Add(QCW + PREFIX qcw + GIT_REPOSITORY https://github.com/mbasaglia/Qt-Color-Widgets + PATCH_COMMAND patch -t -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/qt_5_2_moc_creation_namespace_fix.diff + UPDATE_COMMAND "" + INSTALL_COMMAND "") + +ExternalProject_Get_Property(QCW binary_dir source_dir) +set(QCW_LIBRARY ${binary_dir}/libColorWidgets-qt5.a) +set(QCW_INCLUDE_DIR ${source_dir}/include) diff --git a/cmake/modules/BuildQwt.cmake b/cmake/modules/BuildQwt.cmake --- a/cmake/modules/BuildQwt.cmake +++ b/cmake/modules/BuildQwt.cmake @@ -28,7 +28,8 @@ ExternalProject_Add(QWT -e "s/QWT_CONFIG\\s*\\+=\\s*QwtSvg/#&/" -e "s/QWT_CONFIG\\s*\\+=\\s*QwtOpenGL/#&/" -e "s|QWT_INSTALL_PREFIX\\s*=.*|QWT_INSTALL_PREFIX = |" - /qwtconfig.pri + /qwtconfig.pri + UPDATE_COMMAND "" CONFIGURE_COMMAND qmake /qwt.pro ) diff --git a/cmake/modules/qt_5_2_moc_creation_namespace_fix.diff b/cmake/modules/qt_5_2_moc_creation_namespace_fix.diff new file mode 100644 --- /dev/null +++ b/cmake/modules/qt_5_2_moc_creation_namespace_fix.diff @@ -0,0 +1,39 @@ +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 { + + /** diff --git a/src/channelinfomodel.cpp b/src/channelinfomodel.cpp new file mode 100644 --- /dev/null +++ b/src/channelinfomodel.cpp @@ -0,0 +1,380 @@ +/* + 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 . +*/ + +#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) +{ + return infos[i].name; +} + +QColor ChannelInfoModel::color(unsigned i) +{ + return infos[i].color; +} + +bool ChannelInfoModel::isVisible(unsigned i) +{ + return infos[i].visibility; +} + +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(); + emit dataChanged(index, index, QVector({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({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({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(); + chanInfo.visibility = settings->value(SG_Channels_Visible, true).toBool(); + + if ((int) ci < infos.size()) + { + infos[ci] = chanInfo; + + if (ci < _numOfChannels) + { + auto roles = QVector({ + 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(); +} diff --git a/src/channelinfomodel.h b/src/channelinfomodel.h new file mode 100644 --- /dev/null +++ b/src/channelinfomodel.h @@ -0,0 +1,91 @@ +/* + 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 . +*/ + +#ifndef CHANNELINFOMODEL_H +#define CHANNELINFOMODEL_H + +#include +#include +#include +#include + +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); + QColor color (unsigned i); + bool isVisible(unsigned i); + + // 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 infos; +}; + +#endif // CHANNELINFOMODEL_H diff --git a/src/channelmanager.cpp b/src/channelmanager.cpp --- a/src/channelmanager.cpp +++ b/src/channelmanager.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -17,30 +17,27 @@ along with serialplot. If not, see . */ -#include #include +#include + #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; - 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 +68,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 +76,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); } @@ -104,39 +102,62 @@ FrameBuffer* ChannelManager::channelBuff 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 & roles) { - _channelNames.insertRow(_channelNames.rowCount()); - setChannelName(_channelNames.rowCount()-1, name); -} - -void ChannelManager::onChannelNameDataChange(const QModelIndex & topLeft, - const QModelIndex & bottomRight, - const QVector & 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)); } } @@ -147,26 +168,10 @@ void ChannelManager::addChannelData(unsi 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); } diff --git a/src/channelmanager.h b/src/channelmanager.h --- a/src/channelmanager.h +++ b/src/channelmanager.h @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -21,12 +21,13 @@ #define CHANNELMANAGER_H #include -#include +#include #include #include #include #include "framebuffer.h" +#include "channelinfomodel.h" class ChannelManager : public QObject { @@ -38,12 +39,16 @@ public: unsigned numOfChannels(); unsigned numOfSamples(); FrameBuffer* channelBuffer(unsigned channel); - QStringListModel* channelNames(); + // 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); @@ -53,21 +58,21 @@ signals: public slots: void setNumOfChannels(unsigned number); void setNumOfSamples(unsigned number); - void setChannelName(unsigned channel, QString name); void addChannelData(unsigned channel, double* data, unsigned size); private: unsigned _numOfChannels; unsigned _numOfSamples; QList 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 & roles = QVector ()); + void onChannelInfoChanged(const QModelIndex & topLeft, + const QModelIndex & bottomRight, + const QVector & roles = QVector ()); }; #endif // CHANNELMANAGER_H diff --git a/src/main.cpp b/src/main.cpp --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* - 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(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -66,7 +66,7 @@ MainWindow::MainWindow(QWidget *parent) { 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"); @@ -172,7 +172,7 @@ MainWindow::MainWindow(QWidget *parent) 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++) @@ -376,7 +376,7 @@ void MainWindow::onChannelNameChanged(un // since `onNumOfChannelsChanged` slot will update curve list. if (channel < plotMan->numOfCurves()) // check if channel exists in curve list { - plotMan->setTitle(channel, name); + // plotMan->setTitle(channel, name); } } @@ -463,6 +463,11 @@ void MainWindow::messageHandler(QtMsgTyp { ui->statusBar->showMessage(msg, 5000); } + + if (type == QtFatalMsg) + { + __builtin_trap(); + } } void MainWindow::saveAllSettings(QSettings* settings) diff --git a/src/plot.h b/src/plot.h --- a/src/plot.h +++ b/src/plot.h @@ -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); @@ -77,6 +63,24 @@ public slots: void onNumOfSamplesChanged(unsigned value); +protected: + /// update the display of symbols depending on `symbolSize` + void updateSymbols(); + +private: + bool isAutoScaled; + double yMin, yMax; + 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(); diff --git a/src/plotcontrolpanel.cpp b/src/plotcontrolpanel.cpp --- a/src/plotcontrolpanel.cpp +++ b/src/plotcontrolpanel.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -23,6 +23,7 @@ #include +#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); @@ -92,6 +98,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() @@ -204,9 +222,77 @@ void PlotControlPanel::onRangeSelected() ui->cbAutoScale->setChecked(false); } -void PlotControlPanel::setChannelNamesModel(QAbstractItemModel * model) +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 ¤t, 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(); + } + 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 & roles = QVector ()) + { + 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(); + + // 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) diff --git a/src/plotcontrolpanel.h b/src/plotcontrolpanel.h --- a/src/plotcontrolpanel.h +++ b/src/plotcontrolpanel.h @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -21,8 +21,11 @@ #define PLOTCONTROLPANEL_H #include -#include #include +#include +#include + +#include "channelinfomodel.h" namespace Ui { class PlotControlPanel; @@ -41,7 +44,7 @@ public: double yMax(); double yMin(); - void setChannelNamesModel(QAbstractItemModel * model); + void setChannelInfoModel(ChannelInfoModel* model); /// Stores plot settings into a `QSettings` void saveSettings(QSettings* settings); @@ -60,6 +63,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); diff --git a/src/plotcontrolpanel.ui b/src/plotcontrolpanel.ui --- a/src/plotcontrolpanel.ui +++ b/src/plotcontrolpanel.ui @@ -15,28 +15,112 @@ - - - - - font-weight: bold; - - - Channel Names: - - - - - - - - 16777215 - 170 - - - - - + + + + 0 + 0 + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 300 + 170 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 20 + + + tbReset + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + Reset + + + QToolButton::MenuButtonPopup + + + Qt::NoArrow + + + + + + + tbReset + horizontalLayoutWidget + tvChannelInfo + @@ -48,7 +132,7 @@ - QFormLayout::AllNonFixedFieldsGrow + QFormLayout::FieldsStayAtSizeHint @@ -159,8 +243,32 @@ + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 1 + 20 + + + + + + + color_widgets::ColorSelector + QWidget +
color_selector.hpp
+ 1 +
+
diff --git a/src/plotmanager.cpp b/src/plotmanager.cpp --- a/src/plotmanager.cpp +++ b/src/plotmanager.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -18,13 +18,14 @@ */ #include +#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 +39,7 @@ PlotManager::PlotManager(QWidget* plotAr _yMin = 0; _yMax = 1; isDemoShown = false; + _infoModel = infoModel; // initalize layout and single widget isMulti = false; @@ -85,6 +87,21 @@ PlotManager::PlotManager(QWidget* plotAr this, &PlotManager::showLegend); connect(&showMultiAction, SELECT::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 &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(); + 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; @@ -219,7 +276,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; diff --git a/src/plotmanager.h b/src/plotmanager.h --- a/src/plotmanager.h +++ b/src/plotmanager.h @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -30,13 +30,14 @@ #include #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. @@ -77,6 +78,7 @@ private: QScrollArea* scrollArea; QList curves; QList plotWidgets; + ChannelInfoModel* _infoModel; bool isDemoShown; bool _autoScaled; double _yMin; @@ -104,6 +106,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 & roles = QVector ()); }; #endif // PLOTMANAGER_H diff --git a/src/setting_defines.h b/src/setting_defines.h --- a/src/setting_defines.h +++ b/src/setting_defines.h @@ -70,6 +70,8 @@ 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"; diff --git a/src/snapshot.cpp b/src/snapshot.cpp --- a/src/snapshot.cpp +++ b/src/snapshot.cpp @@ -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) diff --git a/src/snapshot.h b/src/snapshot.h --- a/src/snapshot.h +++ b/src/snapshot.h @@ -27,6 +27,8 @@ #include #include +#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> 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; diff --git a/src/snapshotmanager.cpp b/src/snapshotmanager.cpp --- a/src/snapshotmanager.cpp +++ b/src/snapshotmanager.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2015 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -63,7 +63,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 +76,6 @@ Snapshot* SnapshotManager::makeSnapshot( snapshot->data[ci][i] = QPointF(i, _channelMan->channelBuffer(ci)->sample(i)); } } - snapshot->setChannelNames(_channelMan->channelNames()->stringList()); return snapshot; } @@ -189,9 +188,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); } diff --git a/src/snapshotview.cpp b/src/snapshotview.cpp --- a/src/snapshotview.cpp +++ b/src/snapshotview.cpp @@ -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());