diff --git a/libshvvisu/CMakeLists.txt b/libshvvisu/CMakeLists.txt index 5483d6f65..d97a549f6 100644 --- a/libshvvisu/CMakeLists.txt +++ b/libshvvisu/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(libshvvisu images/images.qrc + src/logview/dataviewwidget.cpp src/logview/dlgloginspector.cpp src/logview/logmodel.cpp src/logview/logsortfilterproxymodel.cpp @@ -28,6 +29,7 @@ add_library(libshvvisu src/timeline/sample.cpp src/widgets/timezonecombobox.cpp + include/shv/visu/logview/dataviewwidget.h include/shv/visu/logview/dlgloginspector.h include/shv/visu/logview/logsortfilterproxymodel.h include/shv/visu/logview/logmodel.h diff --git a/libshvvisu/images/filter-off.svg b/libshvvisu/images/filter-off.svg new file mode 100644 index 000000000..476c929a3 --- /dev/null +++ b/libshvvisu/images/filter-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libshvvisu/images/filter.svg b/libshvvisu/images/filter.svg new file mode 100644 index 000000000..5844d0eca --- /dev/null +++ b/libshvvisu/images/filter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libshvvisu/images/images.qrc b/libshvvisu/images/images.qrc index 2c41c3148..75fbc19a7 100644 --- a/libshvvisu/images/images.qrc +++ b/libshvvisu/images/images.qrc @@ -1,5 +1,7 @@ + filter.svg + filter-off.svg pencil.svg clock.svg ok.svg diff --git a/libshvvisu/include/shv/visu/logview/dataviewwidget.h b/libshvvisu/include/shv/visu/logview/dataviewwidget.h new file mode 100644 index 000000000..4a62abdd1 --- /dev/null +++ b/libshvvisu/include/shv/visu/logview/dataviewwidget.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace shv { +namespace visu { +namespace logview { + +namespace Ui { +class DataViewWidget; +} + +class DataViewWidget : public QWidget +{ + Q_OBJECT + +public: + explicit DataViewWidget(QWidget *parent = nullptr); + ~DataViewWidget(); + + void init(const QString &site_path, timeline::Graph *graph); + +private: + void onShowChannelFilterClicked(); + + timeline::Graph *m_graph = nullptr; + QString m_sitePath; + + Ui::DataViewWidget *ui; +}; + +}}} diff --git a/libshvvisu/include/shv/visu/logview/dlgloginspector.h b/libshvvisu/include/shv/visu/logview/dlgloginspector.h index ad4d8b544..8265bd9c2 100644 --- a/libshvvisu/include/shv/visu/logview/dlgloginspector.h +++ b/libshvvisu/include/shv/visu/logview/dlgloginspector.h @@ -54,16 +54,8 @@ class SHVVISU_DECL_EXPORT DlgLogInspector : public QDialog void setTimeZone(const QTimeZone &tz); #endif - void applyFilters(const QStringList &channel_paths); - void onChannelsFilterClicked(); void onGraphChannelFilterChanged(); - void initVisualSettingSelector(const QString &shv_path); - void onSaveViewClicked(); - void onDeleteViewClicked(); - void onViewSelected(int index); - void setView(const QString &name); - private: Ui::DlgLogInspector *ui; @@ -79,7 +71,6 @@ class SHVVISU_DECL_EXPORT DlgLogInspector : public QDialog shv::visu::timeline::GraphModel *m_graphModel = nullptr; shv::visu::timeline::Graph *m_graph = nullptr; shv::visu::timeline::GraphWidget *m_graphWidget = nullptr; - shv::visu::timeline::ChannelFilterDialog *m_channelFilterDialog = nullptr; }; }}} diff --git a/libshvvisu/include/shv/visu/logview/logsortfilterproxymodel.h b/libshvvisu/include/shv/visu/logview/logsortfilterproxymodel.h index 299bb45b9..72d3771db 100644 --- a/libshvvisu/include/shv/visu/logview/logsortfilterproxymodel.h +++ b/libshvvisu/include/shv/visu/logview/logsortfilterproxymodel.h @@ -5,6 +5,7 @@ #include "../timeline/fulltextfilter.h" #include +#include namespace shv { namespace visu { @@ -18,7 +19,7 @@ class SHVVISU_DECL_EXPORT LogSortFilterProxyModel : public QSortFilterProxyModel public: explicit LogSortFilterProxyModel(QObject *parent = nullptr); - void setChannelFilter(const shv::visu::timeline::ChannelFilter &filter); + void setChannelFilter(const std::optional &filter); void setShvPathColumn(int column); void setValueColumn(int column); void setFulltextFilter(const shv::visu::timeline::FullTextFilter &filter); @@ -26,7 +27,7 @@ class SHVVISU_DECL_EXPORT LogSortFilterProxyModel : public QSortFilterProxyModel bool filterAcceptsRow(int source_rrow, const QModelIndex &source_parent) const override; private: - shv::visu::timeline::ChannelFilter m_channelFilter; + std::optional m_channelFilter; shv::visu::timeline::FullTextFilter m_fulltextFilter; int m_shvPathColumn = -1; int m_valueColumn = -1; diff --git a/libshvvisu/include/shv/visu/timeline/channelfilter.h b/libshvvisu/include/shv/visu/timeline/channelfilter.h index 4a487e5fe..9ca4b4817 100644 --- a/libshvvisu/include/shv/visu/timeline/channelfilter.h +++ b/libshvvisu/include/shv/visu/timeline/channelfilter.h @@ -13,19 +13,20 @@ class SHVVISU_DECL_EXPORT ChannelFilter { public: ChannelFilter(); - ChannelFilter(const QStringList &matching_paths); + ChannelFilter(const QSet &permitted_paths, const QString &name = {}); - void addMatchingPath(const QString &shv_path); - void removeMatchingPath(const QString &shv_path); + void addPermittedPath(const QString &path); + void removePermittedPath(const QString &path); - QStringList matchingPaths() const; - void setMatchingPaths(const QStringList &paths); + QSet permittedPaths() const; + void setPermittedPaths(const QSet &paths); + + bool isPathPermitted(const QString &path) const; + QString name(); - bool isPathMatch(const QString &path) const; - bool isValid() const; private: - QStringList m_matchingPaths; - bool m_isValid; + QString m_name; + QSet m_permittedPaths; }; } diff --git a/libshvvisu/include/shv/visu/timeline/channelfilterdialog.h b/libshvvisu/include/shv/visu/timeline/channelfilterdialog.h index c72d1e1e8..0ae932e67 100644 --- a/libshvvisu/include/shv/visu/timeline/channelfilterdialog.h +++ b/libshvvisu/include/shv/visu/timeline/channelfilterdialog.h @@ -1,8 +1,10 @@ #pragma once #include "../shvvisuglobal.h" +#include #include +#include namespace shv { namespace visu { @@ -20,20 +22,29 @@ class SHVVISU_DECL_EXPORT ChannelFilterDialog : public QDialog Q_OBJECT public: - explicit ChannelFilterDialog(QWidget *parent = nullptr); + explicit ChannelFilterDialog(QWidget *parent, const QString &site_path, Graph *graph); ~ChannelFilterDialog(); - void init(const QString &site_path, const QStringList &logged_paths); - - QStringList selectedChannels(); - void setSelectedChannels(const QStringList &channels); + std::optional channelFilter(); private: void applyTextFilter(); + void reloadDataViewsCombobox(); + + void saveDataView(); + void saveDataViewAs(); + void discardUserChanges(); + void deleteDataView(); + void exportDataView(); + void importDataView(); + + void refreshActions(); void setVisibleItemsCheckState(Qt::CheckState state); void setVisibleItemsCheckState_helper(const QModelIndex &mi, Qt::CheckState state); + void loadChannelFilterFomGraph(); + void onDataViewComboboxChanged(int index); void onCustomContextMenuRequested(QPoint pos); void onPbCheckItemsClicked(); @@ -43,9 +54,19 @@ class SHVVISU_DECL_EXPORT ChannelFilterDialog : public QDialog void onChbFindRegexChanged(int state); Ui::ChannelFilterDialog *ui; + shv::visu::timeline::Graph *m_graph = nullptr; ChannelFilterModel *m_channelsFilterModel = nullptr; ChannelFilterSortFilterProxyModel *m_channelsFilterProxyModel = nullptr; QString m_sitePath; + QString m_recentSettingsDir; + + QAction *m_saveViewAction = nullptr; + QAction *m_saveViewAsAction = nullptr; + QAction *m_revertViewAction = nullptr; + QAction *m_resetViewAction = nullptr; + QAction *m_deleteViewAction = nullptr; + QAction *m_exportViewAction = nullptr; + QAction *m_importViewAction = nullptr; }; } diff --git a/libshvvisu/include/shv/visu/timeline/channelfiltermodel.h b/libshvvisu/include/shv/visu/timeline/channelfiltermodel.h index b2a3c7766..0f5f0b622 100644 --- a/libshvvisu/include/shv/visu/timeline/channelfiltermodel.h +++ b/libshvvisu/include/shv/visu/timeline/channelfiltermodel.h @@ -21,10 +21,10 @@ class SHVVISU_DECL_EXPORT ChannelFilterModel : public QStandardItemModel ChannelFilterModel(QObject *parent = nullptr); ~ChannelFilterModel() Q_DECL_OVERRIDE; - void createNodes(const QStringList &channels); + void createNodes(const QSet &channels); - QStringList selectedChannels(); - void setSelectedChannels(const QStringList &channels); + QSet permittedChannels(); + void setPermittedChannels(const QSet &channels); void setItemCheckState(const QModelIndex &mi, Qt::CheckState check_state); void fixCheckBoxesIntegrity(); protected: @@ -38,7 +38,7 @@ class SHVVISU_DECL_EXPORT ChannelFilterModel : public QStandardItemModel void createNodesForPath(const QString &path); - void selectedChannels_helper(QStringList *channels, QStandardItem *it); + void selectedChannels_helper(QSet *channels, QStandardItem *it); bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; void setChildItemsCheckedState(QStandardItem *it, Qt::CheckState check_state); void fixChildItemsCheckBoxesIntegrity(QStandardItem *it); diff --git a/libshvvisu/include/shv/visu/timeline/graph.h b/libshvvisu/include/shv/visu/timeline/graph.h index b43e43c04..b94a29158 100644 --- a/libshvvisu/include/shv/visu/timeline/graph.h +++ b/libshvvisu/include/shv/visu/timeline/graph.h @@ -1,16 +1,17 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "channelfilter.h" +#include "channelprobe.h" +#include "graphchannel.h" +#include "graphbuttonbox.h" +#include "sample.h" +#include "../shvvisuglobal.h" #include #include #include +#include #include #include #include @@ -93,8 +94,8 @@ class SHVVISU_DECL_EXPORT Graph : public QObject QString toJson() const; static VisualSettings fromJson(const QString &json); - bool isValid() const; QVector channels; + QString name; }; Graph(QObject *parent = nullptr); @@ -111,12 +112,10 @@ class SHVVISU_DECL_EXPORT Graph : public QObject #endif void setSettingsUserName(const QString &user); - - void reset(); + enum class SortChannels { No = 0, Yes }; void createChannelsFromModel(SortChannels sorted = SortChannels::Yes); void resetChannelsRanges(); - bool isInitialView() const; qsizetype channelCount() const; void clearChannels(); @@ -128,10 +127,10 @@ class SHVVISU_DECL_EXPORT Graph : public QObject QString channelName(qsizetype channel) const; void showAllChannels(); - QStringList channelPaths(); + QSet channelPaths(); void hideFlatChannels(); - const ChannelFilter& channelFilter() const; - void setChannelFilter(const ChannelFilter &filter); + const std::optional &channelFilter() const; + void setChannelFilter(const std::optional &filter); void setChannelVisible(qsizetype channel_ix, bool is_visible); void setChannelMaximized(qsizetype channel_ix, bool is_maximized); @@ -226,7 +225,7 @@ class SHVVISU_DECL_EXPORT Graph : public QObject static QString rectToString(const QRect &r); VisualSettings visualSettings() const; - void setVisualSettings(const VisualSettings &settings); + void setVisualSettingsAndChannelFilter(const VisualSettings &settings); void resizeChannelHeight(qsizetype ix, int delta_px); void resizeVerticalHeaderWidth(int delta_px); @@ -234,6 +233,8 @@ class SHVVISU_DECL_EXPORT Graph : public QObject void deleteVisualSettings(const QString &settings_id, const QString &name) const; QStringList savedVisualSettingsNames(const QString &settings_id) const; void loadVisualSettings(const QString &settings_id, const QString &name); + QString loadedVisualSettingsId(); + bool isChannelFilterValid() const; protected: void sanityXRangeZoom(); @@ -270,6 +271,8 @@ class SHVVISU_DECL_EXPORT Graph : public QObject virtual void drawCurrentTime(QPainter *painter, int channel_ix); void drawCurrentTimeMarker(QPainter *painter, time_t time); + virtual void applyCustomChannelStyle(GraphChannel *channel); + QVariantMap mergeMaps(const QVariantMap &base, const QVariantMap &overlay) const; void makeXAxis(); void makeYAxis(qsizetype channel); @@ -290,7 +293,7 @@ class SHVVISU_DECL_EXPORT Graph : public QObject QVector m_channelProbes; QVector m_channels; - ChannelFilter m_channelFilter; + std::optional m_channelFilter; struct SHVVISU_DECL_EXPORT XAxis { diff --git a/libshvvisu/src/logview/dataviewwidget.cpp b/libshvvisu/src/logview/dataviewwidget.cpp new file mode 100644 index 000000000..146812c69 --- /dev/null +++ b/libshvvisu/src/logview/dataviewwidget.cpp @@ -0,0 +1,53 @@ +#include "ui_dataviewwidget.h" + +#include +#include +#include + +#include "shv/core/log.h" + +namespace tl = shv::visu::timeline; + +namespace shv::visu::logview { + +DataViewWidget::DataViewWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::DataViewWidget) +{ + ui->setupUi(this); + + connect(ui->pbShowChannelFilterDialog, &QToolButton::clicked, this, &DataViewWidget::onShowChannelFilterClicked); +} + +DataViewWidget::~DataViewWidget() +{ + delete ui; +} + +void DataViewWidget::init(const QString &site_path, timeline::Graph *graph) +{ + if (m_graph != nullptr) { + shvWarning() << "Dialog is allready initialized."; + return; + } + + m_graph = graph; + m_sitePath = site_path; + + connect(graph, &timeline::Graph::channelFilterChanged, this, [this](){ + ui->pbShowChannelFilterDialog->setIcon(m_graph->isChannelFilterValid()? QIcon(QStringLiteral(":/shv/visu/images/filter.svg")): QIcon(QStringLiteral(":/shv/visu/images/filter-off.svg"))); + }); +} + +void DataViewWidget::onShowChannelFilterClicked() +{ + auto *channel_filter_dialog = new tl::ChannelFilterDialog(this, m_sitePath, m_graph); + + if (channel_filter_dialog->exec() == QDialog::Accepted) { + m_graph->setChannelFilter(channel_filter_dialog->channelFilter()); + } + + channel_filter_dialog->deleteLater(); +} + +} diff --git a/libshvvisu/src/logview/dataviewwidget.ui b/libshvvisu/src/logview/dataviewwidget.ui new file mode 100644 index 000000000..e769098f1 --- /dev/null +++ b/libshvvisu/src/logview/dataviewwidget.ui @@ -0,0 +1,49 @@ + + + shv::visu::logview::DataViewWidget + + + + 0 + 0 + 24 + 25 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Show filter dialog + + + ... + + + + :/shv/visu/images/filter-off.svg:/shv/visu/images/filter-off.svg + + + + + + + + + + diff --git a/libshvvisu/src/logview/dlgloginspector.cpp b/libshvvisu/src/logview/dlgloginspector.cpp index e12444d0a..98ce65e5e 100644 --- a/libshvvisu/src/logview/dlgloginspector.cpp +++ b/libshvvisu/src/logview/dlgloginspector.cpp @@ -222,7 +222,7 @@ DlgLogInspector::DlgLogInspector(const QString &shv_path, QWidget *parent) : m_graph->setModel(m_graphModel); m_graphWidget->setGraph(m_graph); - m_channelFilterDialog = new shv::visu::timeline::ChannelFilterDialog(this); + ui->wDataView->init(ui->edShvPath->text(), m_graph); #if SHVVISU_HAS_TIMEZONE connect(ui->cbxTimeZone, &QComboBox::currentTextChanged, this, [this](const QString &) { @@ -237,22 +237,12 @@ DlgLogInspector::DlgLogInspector(const QString &shv_path, QWidget *parent) : connect(ui->btLoad, &QPushButton::clicked, this, &DlgLogInspector::downloadLog); connect(m_graph, &shv::visu::timeline::Graph::channelFilterChanged, this, &DlgLogInspector::onGraphChannelFilterChanged); - connect(ui->pbChannelsFilter, &QPushButton::clicked, this, &DlgLogInspector::onChannelsFilterClicked); connect(ui->btResizeColumnsToFitWidth, &QAbstractButton::clicked, this, [this]() { ui->tblData->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); }); loadSettings(); - - initVisualSettingSelector(shv_path); - ui->cbViews->setCurrentIndex(VIEW_SELECTOR_NO_VIEW_INDEX); - - connect(ui->cbViews, QOverload::of(&QComboBox::activated), this, &DlgLogInspector::onViewSelected); - - connect(ui->pbDeleteView, &QPushButton::clicked, this, &DlgLogInspector::onDeleteViewClicked); - connect(ui->pbSaveView, &QPushButton::clicked, this, &DlgLogInspector::onSaveViewClicked); - onViewSelected(VIEW_SELECTOR_NO_VIEW_INDEX); } DlgLogInspector::~DlgLogInspector() @@ -268,60 +258,6 @@ void DlgLogInspector::loadSettings() restoreGeometry(ba); } -void DlgLogInspector::initVisualSettingSelector(const QString &shv_path) -{ - ui->cbViews->clear(); - - ui->cbViews->addItem(tr("Initial view")); - for (const QString &view_name : m_graph->savedVisualSettingsNames(shv_path)) { - ui->cbViews->addItem(view_name); - } -} - -void DlgLogInspector::onSaveViewClicked() -{ - QString current_name = ui->cbViews->currentText(); - if (current_name.isEmpty() || current_name == ui->cbViews->itemText(0)) { - return; - } - if (ui->cbViews->findText(current_name) == -1) { - int index = ui->cbViews->count(); - ui->cbViews->addItem(current_name); - ui->cbViews->setCurrentIndex(index); - } - m_graph->saveVisualSettings(shvPath(), current_name); -} - -void DlgLogInspector::onDeleteViewClicked() -{ - int index = ui->cbViews->currentIndex(); - const QString ¤t_name = ui->cbViews->currentText(); - if (ui->cbViews->findText(current_name) == -1) { - ui->cbViews->setEditText(ui->cbViews->itemText(index)); - return; - } - - m_graph->deleteVisualSettings(shvPath(), current_name); - ui->cbViews->removeItem(index); - ui->cbViews->setCurrentIndex(VIEW_SELECTOR_NO_VIEW_INDEX); - onViewSelected(VIEW_SELECTOR_NO_VIEW_INDEX); -} - -void DlgLogInspector::onViewSelected(int index) -{ - if (index == VIEW_SELECTOR_NO_VIEW_INDEX) { - m_graph->reset(); - } - else { - setView(ui->cbViews->currentText()); - } -} - -void DlgLogInspector::setView(const QString &name) -{ - m_graph->loadVisualSettings(shvPath(), name); -} - void DlgLogInspector::saveSettings() { QSettings settings; @@ -485,11 +421,7 @@ void DlgLogInspector::parseLog(shv::chainpack::RpcValue log) } m_graph->createChannelsFromModel(); - - QStringList channel_paths = m_graph->channelPaths(); - m_channelFilterDialog->init(shvPath(), channel_paths); ui->graphView->makeLayout(); - applyFilters(channel_paths); } void DlgLogInspector::showInfo(const QString &msg, bool is_error) @@ -547,22 +479,6 @@ void DlgLogInspector::setTimeZone(const QTimeZone &tz) } #endif -void DlgLogInspector::applyFilters(const QStringList &channel_paths) -{ - auto graph_filter = m_graph->channelFilter(); - graph_filter.setMatchingPaths(channel_paths); - m_graph->setChannelFilter(graph_filter); -} - -void DlgLogInspector::onChannelsFilterClicked() -{ - m_channelFilterDialog->setSelectedChannels(m_graph->channelFilter().matchingPaths()); - - if(m_channelFilterDialog->exec() == QDialog::Accepted) { - applyFilters(m_channelFilterDialog->selectedChannels()); - } -} - void DlgLogInspector::onGraphChannelFilterChanged() { m_logSortFilterProxy->setChannelFilter(m_graph->channelFilter()); diff --git a/libshvvisu/src/logview/dlgloginspector.ui b/libshvvisu/src/logview/dlgloginspector.ui index c0fb98051..fa1489a43 100644 --- a/libshvvisu/src/logview/dlgloginspector.ui +++ b/libshvvisu/src/logview/dlgloginspector.ui @@ -65,7 +65,7 @@ 1999 12 - 20 + 17 @@ -116,7 +116,7 @@ 1999 12 - 19 + 16 @@ -427,47 +427,7 @@ - - - Saved views: - - - - - - - true - - - - - - - - - - - :/shv/visu/images/file-save.svg:/shv/visu/images/file-save.svg - - - - - - - - - - - :/shv/visu/images/delete.svg:/shv/visu/images/delete.svg - - - - - - - Channels filter - - + @@ -505,7 +465,7 @@ 0 0 1152 - 368 + 354 @@ -598,16 +558,22 @@ + + shv::visu::TimeZoneComboBox + QComboBox +
shv/visu/timezonecombobox.h
+
shv::visu::timeline::GraphView QScrollArea -
shv/visu/timeline/graphview.h
+
shv/visu/timeline/graphview.h
1
- shv::visu::TimeZoneComboBox - QComboBox -
src/widgets/timezonecombobox.h
+ shv::visu::logview::DataViewWidget + QWidget +
shv/visu/logview/dataviewwidget.h
+ 1
diff --git a/libshvvisu/src/logview/logsortfilterproxymodel.cpp b/libshvvisu/src/logview/logsortfilterproxymodel.cpp index 31d315e96..519e086d8 100644 --- a/libshvvisu/src/logview/logsortfilterproxymodel.cpp +++ b/libshvvisu/src/logview/logsortfilterproxymodel.cpp @@ -11,7 +11,7 @@ LogSortFilterProxyModel::LogSortFilterProxyModel(QObject *parent) : } -void LogSortFilterProxyModel::setChannelFilter(const shv::visu::timeline::ChannelFilter &filter) +void LogSortFilterProxyModel::setChannelFilter(const std::optional &filter) { m_channelFilter = filter; invalidateFilter(); @@ -50,33 +50,24 @@ bool startsWithPath(const QStringView &str, const QStringView &path) bool LogSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - bool row_accepted = false; + bool is_row_accepted = true; + if (m_shvPathColumn >= 0) { QModelIndex ix = sourceModel()->index(source_row, m_shvPathColumn, source_parent); - row_accepted = !m_channelFilter.isValid(); - if (!row_accepted) { - for (const QString &selected_path : m_channelFilter.matchingPaths()) { - auto row_path = sourceModel()->data(ix).toString(); - if (selected_path.startsWith(row_path)) { - row_accepted = true; - break; - } + is_row_accepted = (m_channelFilter) ? m_channelFilter.value().isPathPermitted(sourceModel()->data(ix).toString()) : true; + + if (is_row_accepted && !m_fulltextFilter.pattern().isEmpty()) { + bool is_fulltext_filter_matched = m_fulltextFilter.matches(sourceModel()->data(ix).toString()); + + if (m_valueColumn >= 0) { + ix = sourceModel()->index(source_row, m_valueColumn, source_parent); + is_fulltext_filter_matched = is_fulltext_filter_matched || m_fulltextFilter.matches(sourceModel()->data(ix).toString()); } + is_row_accepted = is_row_accepted && is_fulltext_filter_matched; } } - if (row_accepted && !m_fulltextFilter.pattern().isEmpty()) { - bool fulltext_match = false; - { - QModelIndex ix = sourceModel()->index(source_row, m_shvPathColumn, source_parent); - fulltext_match = fulltext_match || m_fulltextFilter.matches(sourceModel()->data(ix).toString()); - } - if (m_valueColumn >= 0) { - QModelIndex ix = sourceModel()->index(source_row, m_valueColumn, source_parent); - fulltext_match = fulltext_match || m_fulltextFilter.matches(sourceModel()->data(ix).toString()); - } - row_accepted = fulltext_match; - } - return row_accepted; + + return is_row_accepted; } } diff --git a/libshvvisu/src/timeline/channelfilter.cpp b/libshvvisu/src/timeline/channelfilter.cpp index 80bd0aa4a..34541e8db 100644 --- a/libshvvisu/src/timeline/channelfilter.cpp +++ b/libshvvisu/src/timeline/channelfilter.cpp @@ -4,47 +4,42 @@ namespace shv::visu::timeline { -ChannelFilter::ChannelFilter() - : m_isValid(false) -{ -} +ChannelFilter::ChannelFilter() = default; -ChannelFilter::ChannelFilter(const QStringList &matching_paths) - : m_isValid(true) +ChannelFilter::ChannelFilter(const QSet &permitted_paths, const QString &name) { - setMatchingPaths(matching_paths); + m_permittedPaths = permitted_paths; + m_name = name; } -QStringList ChannelFilter::matchingPaths() const +QSet ChannelFilter::permittedPaths() const { - return m_matchingPaths; + return m_permittedPaths; } -void ChannelFilter::addMatchingPath(const QString &shv_path) +void ChannelFilter::addPermittedPath(const QString &path) { - m_isValid = true; - m_matchingPaths << shv_path; + m_permittedPaths.insert(path); } -void ChannelFilter::removeMatchingPath(const QString &shv_path) +void ChannelFilter::removePermittedPath(const QString &path) { - m_isValid = true; - m_matchingPaths.removeOne(shv_path); + m_permittedPaths.remove(path); } -void ChannelFilter::setMatchingPaths(const QStringList &paths) +void ChannelFilter::setPermittedPaths(const QSet &paths) { - m_isValid = true; - m_matchingPaths = paths; + m_permittedPaths = paths; } -bool ChannelFilter::isPathMatch(const QString &path) const +bool ChannelFilter::isPathPermitted(const QString &path) const { - return m_matchingPaths.contains(path); + return m_permittedPaths.contains(path); } -bool ChannelFilter::isValid() const +QString ChannelFilter::name() { - return m_isValid; + return m_name; } + } diff --git a/libshvvisu/src/timeline/channelfilterdialog.cpp b/libshvvisu/src/timeline/channelfilterdialog.cpp index 9e4a31cd4..504bb41c3 100644 --- a/libshvvisu/src/timeline/channelfilterdialog.cpp +++ b/libshvvisu/src/timeline/channelfilterdialog.cpp @@ -2,22 +2,31 @@ #include "channelfiltersortfilterproxymodel.h" #include - #include #include +#include +#include #include #include +#include +#include namespace shv::visu::timeline { -ChannelFilterDialog::ChannelFilterDialog(QWidget *parent) : +static const QString FLATLINE_VIEW_SETTINGS_FILE_TYPE = QStringLiteral("FlatlineViewSettings"); +static const QString FLATLINE_VIEW_SETTINGS_FILE_EXTENSION = QStringLiteral(".fvs"); + +ChannelFilterDialog::ChannelFilterDialog(QWidget *parent, const QString &site_path, Graph *graph) : QDialog(parent), ui(new Ui::ChannelFilterDialog) { ui->setupUi(this); + m_sitePath = site_path; + m_graph = graph; + m_channelsFilterModel = new ChannelFilterModel(this); m_channelsFilterProxyModel = new ChannelFilterSortFilterProxyModel(this); @@ -28,8 +37,35 @@ ChannelFilterDialog::ChannelFilterDialog(QWidget *parent) : ui->tvChannelsFilter->setModel(m_channelsFilterProxyModel); ui->tvChannelsFilter->header()->hide(); ui->tvChannelsFilter->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); - connect(ui->tvChannelsFilter, &QTreeView::customContextMenuRequested, this, &ChannelFilterDialog::onCustomContextMenuRequested); + auto *view_menu = new QMenu(this); + m_resetViewAction = view_menu->addAction(tr("Discard changes"), this, &ChannelFilterDialog::discardUserChanges); + m_saveViewAction = view_menu->addAction(tr("Save"), this, &ChannelFilterDialog::saveDataView); + m_saveViewAsAction = view_menu->addAction(tr("Save as"), this, &ChannelFilterDialog::saveDataViewAs); + m_deleteViewAction = view_menu->addAction(tr("Delete"), this, &ChannelFilterDialog::deleteDataView); + m_exportViewAction = view_menu->addAction(tr("Export"), this, &ChannelFilterDialog::exportDataView); + m_importViewAction = view_menu->addAction(tr("Import"), this, &ChannelFilterDialog::importDataView); + ui->pbActions->setMenu(view_menu); + ui->gbFilterSettings->setChecked(false); + + m_channelsFilterModel->createNodes(graph->channelPaths()); + + ui->gbFilterSettings->setChecked(m_graph->isChannelFilterValid()); + + loadChannelFilterFomGraph(); + reloadDataViewsCombobox(); + + std::optional chf = m_graph->channelFilter(); + + if (chf && !chf.value().name().isEmpty()) + ui->cbDataView->setCurrentText(chf.value().name()); + else + ui->cbDataView->setCurrentIndex(-1); + + refreshActions(); + + connect(ui->cbDataView, qOverload(&QComboBox::currentIndexChanged), this, &ChannelFilterDialog::onDataViewComboboxChanged); + connect(ui->tvChannelsFilter, &QTreeView::customContextMenuRequested, this, &ChannelFilterDialog::onCustomContextMenuRequested); connect(ui->leMatchingFilterText, &QLineEdit::textChanged, this, &ChannelFilterDialog::onLeMatchingFilterTextChanged); connect(ui->pbClearMatchingText, &QPushButton::clicked, this, &ChannelFilterDialog::onPbClearMatchingTextClicked); connect(ui->pbCheckItems, &QPushButton::clicked, this, &ChannelFilterDialog::onPbCheckItemsClicked); @@ -41,30 +77,117 @@ ChannelFilterDialog::~ChannelFilterDialog() delete ui; } -void ChannelFilterDialog::init(const QString &site_path, const QStringList &logged_paths) +std::optional ChannelFilterDialog::channelFilter() { - m_sitePath = site_path; - m_channelsFilterModel->createNodes(logged_paths); + if (ui->gbFilterSettings->isChecked()) { + return ChannelFilter(m_channelsFilterModel->permittedChannels(), ui->cbDataView->currentText()); + } + + return std::nullopt; } -QStringList ChannelFilterDialog::selectedChannels() +void ChannelFilterDialog::applyTextFilter() { - return m_channelsFilterModel->selectedChannels(); + m_channelsFilterProxyModel->setFilterString(ui->leMatchingFilterText->text()); + + if (m_channelsFilterProxyModel->rowCount() == 1) { + ui->tvChannelsFilter->setCurrentIndex(m_channelsFilterProxyModel->index(0, 0)); + ui->tvChannelsFilter->expandRecursively(ui->tvChannelsFilter->currentIndex()); + } } -void ChannelFilterDialog::setSelectedChannels(const QStringList &channels) +void ChannelFilterDialog::reloadDataViewsCombobox() { - m_channelsFilterModel->setSelectedChannels(channels); + ui->cbDataView->clear(); + ui->cbDataView->addItems(m_graph->savedVisualSettingsNames(m_sitePath)); } -void ChannelFilterDialog::applyTextFilter() +void ChannelFilterDialog::deleteDataView() { - m_channelsFilterProxyModel->setFilterString(ui->leMatchingFilterText->text()); + m_graph->deleteVisualSettings(m_sitePath, ui->cbDataView->currentText()); + reloadDataViewsCombobox(); + ui->cbDataView->setCurrentIndex(ui->cbDataView->count() - 1); +} - if (m_channelsFilterProxyModel->rowCount() == 1){ - ui->tvChannelsFilter->setCurrentIndex(m_channelsFilterProxyModel->index(0, 0)); - ui->tvChannelsFilter->expandRecursively(ui->tvChannelsFilter->currentIndex()); +void ChannelFilterDialog::exportDataView() +{ + QString file_name = QFileDialog::getSaveFileName(this, tr("Input file name"), m_recentSettingsDir, "*" + FLATLINE_VIEW_SETTINGS_FILE_EXTENSION); + if (!file_name.isEmpty()) { + if (!file_name.endsWith(FLATLINE_VIEW_SETTINGS_FILE_EXTENSION)) { + file_name.append(FLATLINE_VIEW_SETTINGS_FILE_EXTENSION); + } + + m_graph->setChannelFilter(channelFilter()); + + QSettings settings(file_name, QSettings::Format::IniFormat); + settings.setValue("fileType", FLATLINE_VIEW_SETTINGS_FILE_TYPE); + settings.setValue("settings", m_graph->visualSettings().toJson()); + + m_recentSettingsDir = QFileInfo(file_name).path(); + } +} + +void ChannelFilterDialog::importDataView() +{ + QString file_name = QFileDialog::getOpenFileName(this, tr("Input file name"), m_recentSettingsDir, "*" + FLATLINE_VIEW_SETTINGS_FILE_EXTENSION); + if (!file_name.isEmpty()) { + QString view_name = QInputDialog::getText(this, tr("Import as"), tr("Input view name")); + + if (!view_name.isEmpty()) { + QSettings settings_file(file_name, QSettings::Format::IniFormat); + if (settings_file.value("fileType").toString() != FLATLINE_VIEW_SETTINGS_FILE_TYPE) { + QMessageBox::warning(this, tr("Error"), tr("This file is not flatline view setting file")); + return; + } + + Graph::VisualSettings visual_settings = Graph::VisualSettings::fromJson(settings_file.value("settings").toString()); + visual_settings.name = view_name; + m_graph->setVisualSettingsAndChannelFilter(visual_settings); + m_graph->saveVisualSettings(m_sitePath, view_name); + reloadDataViewsCombobox(); + ui->cbDataView->setCurrentText(view_name); + + m_recentSettingsDir = QFileInfo(file_name).path(); + } + } +} + +void ChannelFilterDialog::refreshActions() +{ + m_saveViewAction->setEnabled(!ui->cbDataView->currentText().isEmpty()); + m_deleteViewAction->setEnabled(!ui->cbDataView->currentText().isEmpty()); + m_resetViewAction->setEnabled(!ui->cbDataView->currentText().isEmpty()); +} + +void ChannelFilterDialog::saveDataView() +{ + if(ui->cbDataView->currentText().isEmpty()) { + shvWarning() << "Failed to save empty filter name."; + return; + } + + m_graph->setChannelFilter(channelFilter()); + m_graph->saveVisualSettings(m_sitePath, ui->cbDataView->currentText()); +} + +void ChannelFilterDialog::saveDataViewAs() +{ + QString view_name = QInputDialog::getText(this, tr("Save as"), tr("Input view name")); + + if (view_name.isEmpty()) { + QMessageBox::warning(this, tr("Error"), tr("Failed to save view: name is empty.")); } + else { + m_graph->setChannelFilter(channelFilter()); + m_graph->saveVisualSettings(m_sitePath, view_name); + reloadDataViewsCombobox(); + ui->cbDataView->setCurrentText(view_name); + } +} + +void ChannelFilterDialog::discardUserChanges() +{ + onDataViewComboboxChanged(ui->cbDataView->currentIndex()); } void ChannelFilterDialog::onCustomContextMenuRequested(QPoint pos) @@ -118,6 +241,12 @@ void ChannelFilterDialog::onChbFindRegexChanged(int state) applyTextFilter(); } +void ChannelFilterDialog::loadChannelFilterFomGraph() +{ + std::optional f = m_graph->channelFilter(); + m_channelsFilterModel->setPermittedChannels((f) ? f.value().permittedPaths() : QSet{}); +} + void ChannelFilterDialog::setVisibleItemsCheckState(Qt::CheckState state) { for (int row = 0; row < m_channelsFilterProxyModel->rowCount(); row++) { @@ -140,4 +269,13 @@ void ChannelFilterDialog::setVisibleItemsCheckState_helper(const QModelIndex &mi } } +void ChannelFilterDialog::onDataViewComboboxChanged(int index) +{ + if (index >= 0) { //ignore event with index = -1, which is emmited from clear() method + m_graph->loadVisualSettings(m_sitePath, ui->cbDataView->currentText()); + loadChannelFilterFomGraph(); + } + refreshActions(); +} + } diff --git a/libshvvisu/src/timeline/channelfilterdialog.ui b/libshvvisu/src/timeline/channelfilterdialog.ui index 605a948d2..f8cfe573b 100644 --- a/libshvvisu/src/timeline/channelfilterdialog.ui +++ b/libshvvisu/src/timeline/channelfilterdialog.ui @@ -6,81 +6,160 @@ 0 0 - 407 + 391 802 Channels filter dialog - + + + 0 + - - - - - - - - - - - :/shv/visu/images/check-box-outline.svg:/shv/visu/images/check-box-outline.svg - - - - - - - - - - - :/shv/visu/images/checkbox-blank-outline.svg:/shv/visu/images/checkbox-blank-outline.svg - - - - - - - - - Matching text - - - - - - - 0 - - - - - - 1 - 0 - - - - - - - - - - - - :/shv/visu/images/delete.svg:/shv/visu/images/delete.svg - - - - - - - - - + + + Channel filter is enabled + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + false + + + + 9 + + + 9 + + + 9 + + + + + + + + true + + + + Data view + + + + + + + + 1 + 0 + + + + + + + + + + + + :/shv/visu/images/hamburger-menu.svg:/shv/visu/images/hamburger-menu.svg + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 10 + 10 + + + + + + + + + + + + + + :/shv/visu/images/check-box-outline.svg:/shv/visu/images/check-box-outline.svg + + + + + + + + + + + :/shv/visu/images/checkbox-blank-outline.svg:/shv/visu/images/checkbox-blank-outline.svg + + + + + + + Matching text + + + + + + + 0 + + + + + + 1 + 0 + + + + + + + + + + + + :/shv/visu/images/delete.svg:/shv/visu/images/delete.svg + + + + + + + + + + + + @@ -88,7 +167,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Ok diff --git a/libshvvisu/src/timeline/channelfiltermodel.cpp b/libshvvisu/src/timeline/channelfiltermodel.cpp index 82155149d..1c0a329de 100644 --- a/libshvvisu/src/timeline/channelfiltermodel.cpp +++ b/libshvvisu/src/timeline/channelfiltermodel.cpp @@ -17,27 +17,31 @@ ChannelFilterModel::ChannelFilterModel(QObject *parent) ChannelFilterModel::~ChannelFilterModel() = default; -void ChannelFilterModel::createNodes(const QStringList &channels) +void ChannelFilterModel::createNodes(const QSet &channels) { beginResetModel(); - for (const auto &shv_path: channels) { - createNodesForPath(shv_path); + QStringList sorted_channels = QStringList(channels.begin(), channels.end()); + sorted_channels.sort(Qt::CaseInsensitive); + + for (const auto &p: sorted_channels) { + createNodesForPath(p); } + endResetModel(); } -QStringList ChannelFilterModel::selectedChannels() +QSet ChannelFilterModel::permittedChannels() { - QStringList channels; + QSet channels; selectedChannels_helper(&channels, invisibleRootItem()); return channels; } -void ChannelFilterModel::selectedChannels_helper(QStringList *channels, QStandardItem *it) +void ChannelFilterModel::selectedChannels_helper(QSet *channels, QStandardItem *it) { if ((it != invisibleRootItem()) && (it->data(UserData::ValidLogEntry).toBool()) && (it->checkState() == Qt::CheckState::Checked)){ - (*channels) << shvPathFromItem(it); + channels->insert(shvPathFromItem(it)); } for (int row = 0; row < it->rowCount(); row++) { @@ -46,7 +50,7 @@ void ChannelFilterModel::selectedChannels_helper(QStringList *channels, QStandar } } -void ChannelFilterModel::setSelectedChannels(const QStringList &channels) +void ChannelFilterModel::setPermittedChannels(const QSet &channels) { setChildItemsCheckedState(invisibleRootItem(), Qt::CheckState::Unchecked); diff --git a/libshvvisu/src/timeline/graph.cpp b/libshvvisu/src/timeline/graph.cpp index 08575f3de..21e01136d 100644 --- a/libshvvisu/src/timeline/graph.cpp +++ b/libshvvisu/src/timeline/graph.cpp @@ -18,12 +18,12 @@ #include +namespace shv::visu::timeline { + static const QString USER_PROFILES_KEY = QStringLiteral("userProfiles"); static const QString SITES_KEY = QStringLiteral("sites"); static const QString VIEWS_KEY = QStringLiteral("channelViews"); -namespace shv::visu::timeline { - const QString Graph::DEFAULT_USER_PROFILE = QStringLiteral("default"); static const int VALUE_NOT_AVILABLE_Y = std::numeric_limits::max(); @@ -103,58 +103,12 @@ QTimeZone Graph::timeZone() const } #endif -bool Graph::isInitialView() const -{ - if (m_channelFilter.isValid()) { - return false; - } - GraphChannel::Style default_style; - - QMap path_to_model_index; - for (qsizetype i = 0; i < m_model->channelCount(); ++i) { - QString shv_path = m_model->channelShvPath(i); - path_to_model_index[shv_path] = i; - } - QString previous_shv_path; - for (GraphChannel *channel : m_channels) { - if (channel->style().heightMax() != default_style.heightMax()) { - return false; - } - QString channel_shv_path = m_model->channelShvPath(channel->modelIndex()); - if (channel_shv_path < previous_shv_path) { - return false; - } - previous_shv_path = channel_shv_path; - } - return true; -} - -void Graph::reset() -{ - createChannelsFromModel(); - m_channelFilter = ChannelFilter(); - Q_EMIT layoutChanged(); - Q_EMIT channelFilterChanged(); -} - void Graph::createChannelsFromModel(shv::visu::timeline::Graph::SortChannels sorted) { - static QVector colors { - Qt::magenta, - Qt::cyan, - Qt::blue, - QColor(0xe6, 0x3c, 0x33), //red - QColor("orange"), - QColor(0x6d, 0xa1, 0x3a), // green - }; - QMap orig_styles; - for (int i = 0; i < channelCount(); ++i) { - auto *ch = channelAt(i); - orig_styles[ch->shvPath()] = ch->style(); - } clearChannels(); if(!m_model) return; + QList model_ixs; if(sorted == SortChannels::Yes) { // sort channels alphabetically @@ -172,17 +126,11 @@ void Graph::createChannelsFromModel(shv::visu::timeline::Graph::SortChannels sor } for(auto model_ix : model_ixs) { GraphChannel *ch = appendChannel(model_ix); - auto channel_ix = channelCount() - 1; - if (orig_styles.contains(ch->shvPath())) { - ch->setStyle(orig_styles[ch->shvPath()]); - } - else { - GraphChannel::Style style = ch->style(); - style.setColor(colors.value(channel_ix % colors.count())); - ch->setStyle(style); - } + applyCustomChannelStyle(ch); } + resetChannelsRanges(); + setChannelFilter(std::nullopt); } void Graph::resetChannelsRanges() @@ -275,35 +223,36 @@ void Graph::moveChannel(qsizetype channel, qsizetype new_pos) void Graph::showAllChannels() { - m_channelFilter.setMatchingPaths(channelPaths()); - - emit layoutChanged(); - emit channelFilterChanged(); + setChannelFilter(std::nullopt); } -QStringList Graph::channelPaths() +QSet Graph::channelPaths() { - QStringList shv_paths; + QSet ret; for (int i = 0; i < m_channels.count(); ++i) { - shv_paths << m_channels[i]->shvPath(); + ret.insert(m_channels[i]->shvPath()); } - return shv_paths; + return ret; } void Graph::hideFlatChannels() { - QStringList matching_paths = (m_channelFilter.isValid()) ? m_channelFilter.matchingPaths() : channelPaths(); + QSet permitted_paths = (m_channelFilter) ? m_channelFilter.value().permittedPaths() : channelPaths(); for (qsizetype i = 0; i < m_channels.count(); ++i) { GraphChannel *ch = m_channels[i]; if(isChannelFlat(ch)) { - matching_paths.removeOne(ch->shvPath()); + permitted_paths.remove(ch->shvPath()); } } - m_channelFilter.setMatchingPaths(matching_paths); + if (m_channelFilter == std::nullopt) + m_channelFilter = ChannelFilter(); + + if (m_channelFilter) + m_channelFilter.value().setPermittedPaths(permitted_paths); emit layoutChanged(); emit channelFilterChanged(); @@ -315,14 +264,15 @@ bool Graph::isChannelFlat(GraphChannel *ch) return yrange.isEmpty(); } -void Graph::setChannelFilter(const ChannelFilter &filter) +void Graph::setChannelFilter(const std::optional &filter) { m_channelFilter = filter; + emit layoutChanged(); emit channelFilterChanged(); } -const ChannelFilter& Graph::channelFilter() const +const std::optional& Graph::channelFilter() const { return m_channelFilter; } @@ -331,14 +281,17 @@ void Graph::setChannelVisible(qsizetype channel_ix, bool is_visible) { GraphChannel *ch = channelAt(channel_ix); - if (!m_channelFilter.isValid()) { - m_channelFilter.setMatchingPaths(channelPaths()); - } - if (is_visible) { - m_channelFilter.addMatchingPath(ch->shvPath()); + if (!m_channelFilter && !is_visible) { + m_channelFilter = ChannelFilter(channelPaths()); } - else { - m_channelFilter.removeMatchingPath(ch->shvPath()); + + if (m_channelFilter) { + if (is_visible) { + m_channelFilter.value().addPermittedPath(ch->shvPath()); + } + else { + m_channelFilter.value().removePermittedPath(ch->shvPath()); + } } emit layoutChanged(); @@ -1196,10 +1149,10 @@ QVector Graph::visibleChannels() const if (maximized_channel >= 0) { visible_channels << maximized_channel; } - else if(m_channelFilter.isValid()) { + else if (isChannelFilterValid()) { for (int i = 0; i < m_channels.count(); ++i) { QString shv_path = model()->channelInfo(m_channels[i]->modelIndex()).shvPath; - if(m_channelFilter.isPathMatch(shv_path)) { + if(m_channelFilter && m_channelFilter.value().isPathPermitted(shv_path)) { visible_channels << i; } } @@ -1212,31 +1165,24 @@ QVector Graph::visibleChannels() const return visible_channels; } -void Graph::setVisualSettings(const VisualSettings &settings) +void Graph::setVisualSettingsAndChannelFilter(const VisualSettings &settings) { - if (settings.isValid()) { - QStringList new_filter; - createChannelsFromModel(); - for (int i = 0; i < settings.channels.count(); ++i) { - const VisualSettings::Channel &channel_settings = settings.channels[i]; - for (int j = i; j < m_channels.count(); ++j) { - GraphChannel *channel = m_channels[j]; - if (channel->shvPath() == channel_settings.shvPath) { - new_filter << channel_settings.shvPath; - channel->setStyle(channel_settings.style); - m_channels.insert(i, m_channels.takeAt(j)); - break; - } + QSet permitted_paths; + + for (int i = 0; i < settings.channels.count(); ++i) { + const VisualSettings::Channel &channel_settings = settings.channels[i]; + for (int j = i; j < m_channels.count(); ++j) { + GraphChannel *channel = m_channels[j]; + if (channel->shvPath() == channel_settings.shvPath) { + permitted_paths.insert(channel_settings.shvPath); + channel->setStyle(channel_settings.style); + m_channels.insert(i, m_channels.takeAt(j)); + break; } } - - m_channelFilter.setMatchingPaths(new_filter); - Q_EMIT layoutChanged(); - Q_EMIT channelFilterChanged(); - } - else { - reset(); } + + setChannelFilter(ChannelFilter(permitted_paths, settings.name)); } void Graph::resizeChannelHeight(qsizetype ix, int delta_px) @@ -1252,7 +1198,7 @@ void Graph::resizeChannelHeight(qsizetype ix, int delta_px) ch_style.setHeightMax(h); ch_style.setHeightMin(h); ch->setStyle(ch_style); - Q_EMIT layoutChanged(); + emit layoutChanged(); } } } @@ -1263,20 +1209,19 @@ void Graph::resizeVerticalHeaderWidth(int delta_px) if (w > MIN_VERTICAL_HEADER_WIDTH && w < MAX_VERTICAL_HEADER_WIDTH) { m_style.setVerticalHeaderWidth(w); - Q_EMIT layoutChanged(); + emit layoutChanged(); } } Graph::VisualSettings Graph::visualSettings() const { - VisualSettings view; - if (!isInitialView()) { - for (int ix : visibleChannels()) { - const GraphChannel *channel = channelAt(ix); - view.channels << VisualSettings::Channel{ channel->shvPath(), channel->style() }; - } + VisualSettings visual_settings; + for (int ix : visibleChannels()) { + const GraphChannel *channel = channelAt(ix); + visual_settings.channels << VisualSettings::Channel{ channel->shvPath(), channel->style() }; } - return view; + + return visual_settings; } int Graph::maximizedChannelIndex() const @@ -2293,6 +2238,13 @@ void Graph::drawCurrentTimeMarker(QPainter *painter, time_t time) drawCenterTopText(painter, p1, text, m_style.font(), color.darker(400), color); } +void Graph::applyCustomChannelStyle(GraphChannel *channel) +{ + GraphChannel::Style style = channel->style(); + style.setColor(Qt::cyan); + channel->setStyle(style); +} + QString Graph::timeToStringTZ(timemsec_t time) const { QDateTime dt = QDateTime::fromMSecsSinceEpoch(time); @@ -2326,14 +2278,20 @@ void Graph::saveVisualSettings(const QString &settings_id, const QString &name) void Graph::loadVisualSettings(const QString &settings_id, const QString &name) { - VisualSettings graph_view; QSettings settings; settings.beginGroup(USER_PROFILES_KEY); settings.beginGroup(m_settingsUserName); settings.beginGroup(SITES_KEY); settings.beginGroup(settings_id); settings.beginGroup(VIEWS_KEY); - setVisualSettings(VisualSettings::fromJson(settings.value(name).toString())); + VisualSettings vs = VisualSettings::fromJson(settings.value(name).toString()); + vs.name = settings_id; + setVisualSettingsAndChannelFilter(vs); +} + +bool Graph::isChannelFilterValid() const +{ + return (m_channelFilter != std::nullopt); } void Graph::deleteVisualSettings(const QString &settings_id, const QString &name) const @@ -2360,19 +2318,15 @@ QStringList Graph::savedVisualSettingsNames(const QString &settings_id) const QString Graph::VisualSettings::toJson() const { - if (isValid()) { - QJsonArray settings; + QJsonArray settings; - for (int i = 0; i < channels.count(); ++i) { - settings << QJsonObject { - { "shvPath", channels[i].shvPath }, - { "style", QJsonObject::fromVariantMap(channels[i].style) } - }; + for (int i = 0; i < channels.count(); ++i) { + settings << QJsonObject { + { "shvPath", channels[i].shvPath }, + { "style", QJsonObject::fromVariantMap(channels[i].style) } + }; } - return QJsonDocument(QJsonObject{{ "channels", settings }}).toJson(QJsonDocument::Compact); - } - - return QString(); + return QJsonDocument(QJsonObject{{ "channels", settings }}).toJson(QJsonDocument::Compact); } Graph::VisualSettings Graph::VisualSettings::fromJson(const QString &json) @@ -2395,12 +2349,8 @@ Graph::VisualSettings Graph::VisualSettings::fromJson(const QString &json) shvWarning() << "Error on parsing user settings" << parse_error.errorString(); } } - return settings; -} -bool Graph::VisualSettings::isValid() const -{ - return channels.count(); + return settings; } Graph::XAxis::XAxis() = default; diff --git a/libshvvisu/src/timeline/graphwidget.cpp b/libshvvisu/src/timeline/graphwidget.cpp index c2f683b7c..2e45777c0 100644 --- a/libshvvisu/src/timeline/graphwidget.cpp +++ b/libshvvisu/src/timeline/graphwidget.cpp @@ -763,7 +763,11 @@ void GraphWidget::showGraphContextMenu(const QPoint &mouse_pos) menu.addAction(tr("Hide channels without changes"), this, [this]() { m_graph->hideFlatChannels(); }); - menu.addAction(tr("Reset channels to defaults"), m_graph, &Graph::reset); + + menu.addAction(tr("Reset channels to defaults"), this, [this]() { + m_graph->createChannelsFromModel(); + }); + if (m_graph->isYAxisVisible()) { menu.addAction(tr("Hide Y axis"), this, [this]() { m_graph->setYAxisVisible(false);