diff --git a/Project/QtCreator/qctools-gui/qctools-gui.pro b/Project/QtCreator/qctools-gui/qctools-gui.pro index 2c7112ec7..320ab5611 100644 --- a/Project/QtCreator/qctools-gui/qctools-gui.pro +++ b/Project/QtCreator/qctools-gui/qctools-gui.pro @@ -75,7 +75,8 @@ HEADERS += \ $$SOURCES_PATH/GUI/filterselector.h \ $$SOURCES_PATH/GUI/playercontrol.h \ $$SOURCES_PATH/GUI/panelsview.h \ - $$SOURCES_PATH/GUI/plotschooser.h + $$SOURCES_PATH/GUI/plotschooser.h \ + $$SOURCES_PATH/GUI/yminmaxselector.h SOURCES += \ $$SOURCES_PATH/GUI/FilesList.cpp \ @@ -108,7 +109,8 @@ SOURCES += \ $$SOURCES_PATH/GUI/filterselector.cpp \ $$SOURCES_PATH/GUI/playercontrol.cpp \ $$SOURCES_PATH/GUI/panelsview.cpp \ - $$SOURCES_PATH/GUI/plotschooser.cpp + $$SOURCES_PATH/GUI/plotschooser.cpp \ + $$SOURCES_PATH/GUI/yminmaxselector.cpp include(../zlib.pri) @@ -121,7 +123,8 @@ FORMS += \ $$SOURCES_PATH/GUI/barchartconditioninput.ui \ $$SOURCES_PATH/GUI/managebarchartconditions.ui \ $$SOURCES_PATH/GUI/playercontrol.ui \ - $$SOURCES_PATH/GUI/plotschooser.ui + $$SOURCES_PATH/GUI/plotschooser.ui \ + $$SOURCES_PATH/GUI/yminmaxselector.ui \ RESOURCES += \ $$SOURCES_PATH/Resource/Resources.qrc diff --git a/Source/Core/Core.h b/Source/Core/Core.h index 972d0c426..a6b112605 100755 --- a/Source/Core/Core.h +++ b/Source/Core/Core.h @@ -50,6 +50,7 @@ struct per_group const bool CheckedByDefault; const char* Description; activefilter ActiveFilterGroup; + const char* YAxisMinMaxMode; }; struct per_item diff --git a/Source/Core/VideoCore.cpp b/Source/Core/VideoCore.cpp index 8f74696a9..d042d6ddd 100644 --- a/Source/Core/VideoCore.cpp +++ b/Source/Core/VideoCore.cpp @@ -17,6 +17,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= "LOW (10th percentile), and HIGH (90th percentile) values for each frame.\n" "The Y plane provides information about video brightness or luminance.", ActiveFilter_Video_signalstats, + "MinMaxOfThePlot", }, //U { @@ -25,6 +26,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= "LOW (10th percentile), and HIGH (90th percentile) values for each frame.\n" "The U plane (along with V) provides information about video color.", ActiveFilter_Video_signalstats, + "Formula", }, //V { @@ -33,6 +35,7 @@ struct per_group VideoPerGroup [Group_VideoMax]= "LOW (10th percentile), and HIGH (90th percentile) values for each frame.\n" "The V plane (along with U) provides information about video color.", ActiveFilter_Video_signalstats, + "Custom;0;55" }, //YDiff { diff --git a/Source/GUI/Plot.cpp b/Source/GUI/Plot.cpp index fdc1b0740..74bbe4c34 100644 --- a/Source/GUI/Plot.cpp +++ b/Source/GUI/Plot.cpp @@ -27,6 +27,8 @@ #include #include "Core/FileInformation.h" +#include +#include #include #include @@ -438,31 +440,35 @@ void Plot::initYAxis() } else { + auto yMin = 0.0; + auto yMax = 0.0; const size_t plotType = type(); const size_t plotGroup = group(); CommonStats* stat = stats( streamPos() ); const struct per_group& group = PerStreamType[plotType].PerGroup[plotGroup]; - auto yMin = 0.0; - if(m_minValue.isNull() || m_minValue.isError() || m_minValue.isUndefined()) { - yMin = stat->y_Min[plotGroup]; // auto-select min - } else { + if(m_yminMaxMode == Formula) + { if(m_minValue.isNumber()) yMin = m_minValue.toNumber(); else if(m_minValue.isCallable()) yMin = m_minValue.call().toNumber(); - } - auto yMax = 0.0; - if(m_maxValue.isNull() || m_maxValue.isError() || m_maxValue.isUndefined()) { - yMax = stat->y_Max[plotGroup]; // auto-select min - } else { if(m_maxValue.isNumber()) yMax = m_maxValue.toNumber(); else if(m_maxValue.isCallable()) yMax = m_maxValue.call().toNumber(); } + else if(m_yminMaxMode == MinMaxOfThePlot) + { + yMin = stat->y_Min[plotGroup]; // auto-select min + yMax = stat->y_Max[plotGroup]; // auto-select max + } else if(m_yminMaxMode == Custom) + { + yMin = m_customYMin; + yMax = m_customYMax; + } setYAxis( yMin, yMax, group.StepsCount ); } @@ -520,6 +526,61 @@ bool Plot::isBarchart() const return m_barchart; } +void Plot::setYAxisMinMaxMode(YMinMaxMode mode) +{ + m_yminMaxMode = mode; + QColor color; + switch(m_yminMaxMode) { + case MinMaxOfThePlot: + color = "darkblue"; + break; + case Formula: + color = "black"; + break; + case Custom: + color = QColor(85, 0, 127); + break; + } + + setYAxisColor(color); + initYAxis(); +} + +Plot::YMinMaxMode Plot::yAxisMinMaxMode() const +{ + return m_yminMaxMode; +} + +void Plot::setYAxisCustomMinMax(double min, double max) +{ + m_customYMin = min; + m_customYMax = max; +} + +void Plot::getYAxisCustomMinMax(double &min, double &max) +{ + min = m_customYMin; + max = m_customYMax; +} + +bool Plot::hasMinMaxFormula() const +{ + if(m_minValue.isNull() || m_minValue.isError() || m_minValue.isUndefined()) { + return false; + } + + if(m_maxValue.isNull() || m_maxValue.isError() || m_maxValue.isUndefined()) { + return false; + } + + return true; +} + +const CommonStats *Plot::getStats() const +{ + return stats( streamPos() ); +} + void Plot::setBarchart(bool value) { if(m_barchart != value) @@ -751,6 +812,39 @@ Plot::Plot( size_t streamPos, size_t Type, size_t Group, const FileInformation* #else panner->setMouseButton( Qt::MiddleButton ); #endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + + auto applyYMinMaxMode = [&](QString value) { + QMetaEnum metaEnum = QMetaEnum::fromType(); + auto splitted = value.split(";"); + auto yMinMaxMode = (Plot::YMinMaxMode) metaEnum.keyToValue(splitted[0].toLatin1().constData()); + + if(yMinMaxMode == Plot::Custom) { + auto min = splitted[1].toDouble(); + auto max = splitted[2].toDouble(); + + setYAxisCustomMinMax(min, max); + } + + setYAxisMinMaxMode(yMinMaxMode); + }; + + if(group.YAxisMinMaxMode) { + QString yMinMaxModeStringValue = group.YAxisMinMaxMode; + qDebug() << "applying default yMinMaxMode: " << yMinMaxModeStringValue; + applyYMinMaxMode(yMinMaxModeStringValue); + } + + QSettings settings; + settings.beginGroup("yminmax"); + + QString value = settings.value(QString::number(m_group)).toString(); + if(!value.isEmpty()) { + qDebug() << "applying yMinMaxMode from settings: " << value; + applyYMinMaxMode(value); + } + + settings.endGroup(); + } //--------------------------------------------------------------------------- @@ -791,6 +885,15 @@ QSize Plot::minimumSizeHint() const return QSize( hint.width(), -1 ); } +void Plot::setYAxisColor(QColor color) +{ + auto yAxis = axisWidget(QwtPlot::yLeft); + QPalette palette = yAxis->palette(); + palette.setColor(QPalette::WindowText, color); // for ticks + palette.setColor(QPalette::Text, color); // for ticks' labels + yAxis->setPalette(palette); +} + const QwtPlotCurve* Plot::curve( int index ) const { const QwtPlotItemList curves = itemList( QwtPlotItem::Rtti_PlotCurve ); diff --git a/Source/GUI/Plot.h b/Source/GUI/Plot.h index 12138aa3a..0a06d8987 100644 --- a/Source/GUI/Plot.h +++ b/Source/GUI/Plot.h @@ -437,6 +437,13 @@ class Plot : public QwtPlot Q_OBJECT public: + enum YMinMaxMode { + MinMaxOfThePlot, + Formula, + Custom + }; + Q_ENUM(YMinMaxMode); + explicit Plot( size_t streamPos, size_t Type, size_t Group, const FileInformation* fileInformation, QWidget *parent ); virtual ~Plot(); @@ -446,6 +453,7 @@ class Plot : public QwtPlot virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; + void setYAxisColor(QColor color); void setYAxis( double min, double max, int numSteps ); void setCursorPos( double x ); @@ -472,6 +480,15 @@ class Plot : public QwtPlot void updateSymbols(); bool isBarchart() const; + void setYAxisMinMaxMode(YMinMaxMode mode); + YMinMaxMode yAxisMinMaxMode() const; + + void setYAxisCustomMinMax(double min, double max); + void getYAxisCustomMinMax(double& min, double& max); + + bool hasMinMaxFormula() const; + + const CommonStats* getStats() const; Q_SIGNALS: void cursorMoved(const QPointF& point, int index); void visibilityChanged(bool visible); @@ -488,6 +505,9 @@ private Q_SLOTS: const QwtPlotCurve* curve( int index ) const; QColor curveColor( int index ) const; + YMinMaxMode m_yminMaxMode { MinMaxOfThePlot }; + double m_customYMin { 0.0 }; + double m_customYMax { 0.0 }; QJSValue m_maxValue; QJSValue m_minValue; QJSEngine m_engine; diff --git a/Source/GUI/Plots.cpp b/Source/GUI/Plots.cpp index 8473e1e48..37f90d0c2 100755 --- a/Source/GUI/Plots.cpp +++ b/Source/GUI/Plots.cpp @@ -17,6 +17,7 @@ #include "Core/Core.h" #include #include "playercontrol.h" +#include "yminmaxselector.h" #include #include #include @@ -123,6 +124,18 @@ void Plots::showEditBarchartProfileDialog(const size_t plotGroup, Plot* plot, co } } +void Plots::showYMinMaxConfigDialog(const size_t plotGroup, Plot *plot, const stream_info &streamInfo, QToolButton* button) +{ + auto globalButtonPos = button->mapToGlobal(QPoint(0, 0)); + auto geometry = m_yMinMaxSelector->geometry(); + + m_yMinMaxSelector->setPlot(plot); + m_yMinMaxSelector->enableFormulaMinMax(plot->hasMinMaxFormula()); + m_yMinMaxSelector->move(QPoint(globalButtonPos.x() - geometry.width(), globalButtonPos.y())); + if(!m_yMinMaxSelector->isVisible()) + m_yMinMaxSelector->show(); +} + Plots::Plots( QWidget *parent, FileInformation* fileInformation ) : QWidget( parent ), m_zoomFactor ( 0 ), @@ -282,11 +295,19 @@ Plots::Plots( QWidget *parent, FileInformation* fileInformation ) : barchartPlotSwitch->setIcon(switchToBarcharts ? QIcon(":/icon/chart_chart.png") : QIcon(":/icon/bar_chart.png")); }); + QToolButton* yMinMaxConfigButton = new QToolButton(); + yMinMaxConfigButton->setIcon(QIcon(":/icon/signalserver_upload.png")); + connect(plot, SIGNAL(visibilityChanged(bool)), yMinMaxConfigButton, SLOT(setVisible(bool))); + connect(yMinMaxConfigButton, &QToolButton::clicked, [=]() { + showYMinMaxConfigDialog(plotGroup, plot, streamInfo, yMinMaxConfigButton); + }); + QHBoxLayout* barchartAndConfigurationLayout = new QHBoxLayout(); barchartAndConfigurationLayout->setAlignment(Qt::AlignLeft); barchartAndConfigurationLayout->setSpacing(5); barchartAndConfigurationLayout->addWidget(barchartPlotSwitch); barchartAndConfigurationLayout->addWidget(barchartConfigButton); + barchartAndConfigurationLayout->addWidget(yMinMaxConfigButton); legendLayout->addItem(barchartAndConfigurationLayout); legendLayout->addWidget(plot->plotLegend()); @@ -496,6 +517,9 @@ Plots::Plots( QWidget *parent, FileInformation* fileInformation ) : m_scaleWidget->setScale( m_timeInterval.from, m_timeInterval.to); setCursorPos( framePos() ); + + m_yMinMaxSelector = new YMinMaxSelector(this); + m_yMinMaxSelector->setWindowFlag(Qt::Popup); } //--------------------------------------------------------------------------- diff --git a/Source/GUI/Plots.h b/Source/GUI/Plots.h index 65f9a303c..c0586e4c8 100755 --- a/Source/GUI/Plots.h +++ b/Source/GUI/Plots.h @@ -22,6 +22,8 @@ class QwtPlot; class Plot; class PlotScaleWidget; class PlayerControl; +class YMinMaxSelector; +class QToolButton; void showEditFrameCommentsDialog(QWidget* parentWidget, FileInformation* info, CommonStats* stats, size_t frameIndex); @@ -116,6 +118,7 @@ class Plots : public QWidget void loadBarchartsProfile(const QJsonObject& profile); void showEditBarchartProfileDialog(const size_t plotGroup, Plot* plot, const stream_info& streamInfo); + void showYMinMaxConfigDialog(const size_t plotGroup, Plot* plot, const stream_info& streamInfo, QToolButton* button); Q_SIGNALS: void visibleFramesChanged(int from, int to); @@ -146,6 +149,7 @@ private Q_SLOTS: void setFramePos( size_t framePos, size_t statsPos = (size_t)-1 ) const { m_fileInfoData->Frames_Pos_Set(framePos, statsPos); } private: + YMinMaxSelector* m_yMinMaxSelector; PlotScaleWidget* m_scaleWidget; CommentsPlot* m_commentsPlot; std::vector m_PanelsViews; diff --git a/Source/GUI/yminmaxselector.cpp b/Source/GUI/yminmaxselector.cpp new file mode 100644 index 000000000..a65fb6c96 --- /dev/null +++ b/Source/GUI/yminmaxselector.cpp @@ -0,0 +1,195 @@ +#include "yminmaxselector.h" +#include "ui_yminmaxselector.h" +#include "Plot.h" + +#include +#include +#include + +YMinMaxSelector::YMinMaxSelector(QWidget *parent) + : QWidget(parent) + , ui(new Ui::YMinMaxSelector) +{ + ui->setupUi(this); + + connect(ui->customMinMax_radioButton, &QRadioButton::toggled, this, &YMinMaxSelector::enableCustomMinMax); + + m_palette = ui->min_doubleSpinBox->palette(); + m_redPalette = ui->min_doubleSpinBox->palette(); + m_redPalette.setColor(QPalette::Base, QColor("red")); +} + +YMinMaxSelector::~YMinMaxSelector() +{ + delete ui; +} + +bool YMinMaxSelector::isMinMaxFromThePlot() const +{ + return ui->minMaxOfThePlot_radioButton->isChecked(); +} + +bool YMinMaxSelector::isFormula() const +{ + return ui->minMaxSystemProvided_radioButton->isChecked(); +} + +bool YMinMaxSelector::isCustom() const +{ + return ui->customMinMax_radioButton->isChecked(); +} + +void YMinMaxSelector::enableCustomMinMax(bool value) +{ + ui->min_label->setEnabled(value); + ui->min_doubleSpinBox->setEnabled(value); + ui->max_label->setEnabled(value); + ui->max_doubleSpinBox->setEnabled(value); +} + +void YMinMaxSelector::enableFormulaMinMax(bool value) +{ + ui->minMaxSystemProvided_radioButton->setEnabled(value); +} + +void YMinMaxSelector::setPlot(Plot *plot) +{ + m_plot = plot; + + if(plot->yAxisMinMaxMode() == Plot::MinMaxOfThePlot) { + ui->minMaxOfThePlot_radioButton->setChecked(true); + } else if(plot->yAxisMinMaxMode() == Plot::Formula) { + ui->minMaxSystemProvided_radioButton->setChecked(true); + } else if(plot->yAxisMinMaxMode() == Plot::Custom) { + ui->customMinMax_radioButton->setChecked(true); + } + + double min, max; + m_plot->getYAxisCustomMinMax(min, max); + + ui->min_doubleSpinBox->setValue(min); + ui->max_doubleSpinBox->setValue(max); + + if(qFuzzyCompare(ui->min_doubleSpinBox->value(), 0) && qFuzzyCompare(ui->max_doubleSpinBox->value(), 0)) { + auto stat = m_plot->getStats(); + auto plotGroup = m_plot->group(); + + auto yMin = stat->y_Min[plotGroup]; // auto-select min + auto yMax = stat->y_Max[plotGroup]; // auto-select max + + ui->min_doubleSpinBox->setValue(yMin); + ui->max_doubleSpinBox->setValue(yMax); + } + + updateApplyButton(); + updateMinMaxStyling(); +} + +Plot *YMinMaxSelector::getPlot() const +{ + return m_plot; +} + +void YMinMaxSelector::on_apply_pushButton_clicked() +{ + if(isFormula()) + { + m_plot->setYAxisMinMaxMode(Plot::Formula); + } + else if(isMinMaxFromThePlot()) + { + m_plot->setYAxisMinMaxMode(Plot::MinMaxOfThePlot); + } + else if(isCustom()) + { + m_plot->setYAxisCustomMinMax(ui->min_doubleSpinBox->value(), ui->max_doubleSpinBox->value()); + m_plot->setYAxisMinMaxMode(Plot::Custom); + } + + QSettings settings; + settings.beginGroup("yminmax"); + + QMetaEnum metaEnum = QMetaEnum::fromType(); + QString stringValue = metaEnum.valueToKey(m_plot->yAxisMinMaxMode()); + if(m_plot->yAxisMinMaxMode() == Plot::Custom) { + stringValue.append(";"); + stringValue.append(QString::number(ui->min_doubleSpinBox->value())); + stringValue.append(";"); + stringValue.append(QString::number(ui->max_doubleSpinBox->value())); + } + + settings.setValue(QString::number(m_plot->group()), stringValue); + settings.endGroup(); + + m_plot->replot(); + hide(); +} + + +void YMinMaxSelector::on_min_doubleSpinBox_valueChanged(double arg1) +{ + Q_UNUSED(arg1) + updateApplyButton(); + updateMinMaxStyling(); +} + + +void YMinMaxSelector::on_max_doubleSpinBox_valueChanged(double arg1) +{ + Q_UNUSED(arg1) + updateApplyButton(); + updateMinMaxStyling(); +} + +void YMinMaxSelector::updateApplyButton() +{ + bool minBiggerThanMax = ui->min_doubleSpinBox->value() > ui->max_doubleSpinBox->value(); + bool customSelected = ui->customMinMax_radioButton->isChecked(); + + if(customSelected) + ui->apply_pushButton->setEnabled(!minBiggerThanMax); + else + ui->apply_pushButton->setEnabled(true); +} + +void YMinMaxSelector::updateMinMaxStyling() +{ + bool minBiggerThanMax = ui->min_doubleSpinBox->value() > ui->max_doubleSpinBox->value(); + bool customSelected = ui->customMinMax_radioButton->isChecked(); + + auto minControl = ui->min_doubleSpinBox; + auto maxControl = ui->max_doubleSpinBox; + + QPalette palette = m_palette; + if(customSelected) + { + if(minBiggerThanMax) { + palette = m_redPalette; + } + } + + minControl->setPalette(palette); + maxControl->setPalette(palette); +} + + +void YMinMaxSelector::on_minMaxOfThePlot_radioButton_clicked() +{ + updateApplyButton(); + updateMinMaxStyling(); +} + + +void YMinMaxSelector::on_minMaxSystemProvided_radioButton_clicked() +{ + updateApplyButton(); + updateMinMaxStyling(); +} + + +void YMinMaxSelector::on_customMinMax_radioButton_clicked() +{ + updateApplyButton(); + updateMinMaxStyling(); +} + diff --git a/Source/GUI/yminmaxselector.h b/Source/GUI/yminmaxselector.h new file mode 100644 index 000000000..8f79e5f70 --- /dev/null +++ b/Source/GUI/yminmaxselector.h @@ -0,0 +1,46 @@ +#ifndef YMINMAXSELECTOR_H +#define YMINMAXSELECTOR_H + +#include + +namespace Ui { +class YMinMaxSelector; +} + +class Plot; +class YMinMaxSelector : public QWidget +{ + Q_OBJECT + +public: + explicit YMinMaxSelector(QWidget *parent = nullptr); + ~YMinMaxSelector(); + + bool isMinMaxFromThePlot() const; + bool isFormula() const; + bool isCustom() const; + + void enableFormulaMinMax(bool value); + void setPlot(Plot* plot); + Plot* getPlot() const; + +private Q_SLOTS: + void enableCustomMinMax(bool value); + void on_apply_pushButton_clicked(); + void on_min_doubleSpinBox_valueChanged(double arg1); + void on_max_doubleSpinBox_valueChanged(double arg1); + void on_minMaxOfThePlot_radioButton_clicked(); + void on_minMaxSystemProvided_radioButton_clicked(); + void on_customMinMax_radioButton_clicked(); + +private: + void updateApplyButton(); + void updateMinMaxStyling(); + + Plot* m_plot { nullptr }; + QPalette m_palette; + QPalette m_redPalette; + Ui::YMinMaxSelector *ui; +}; + +#endif // YMINMAXSELECTOR_H diff --git a/Source/GUI/yminmaxselector.ui b/Source/GUI/yminmaxselector.ui new file mode 100644 index 000000000..ab399218d --- /dev/null +++ b/Source/GUI/yminmaxselector.ui @@ -0,0 +1,203 @@ + + + YMinMaxSelector + + + + 0 + 0 + 550 + 192 + + + + Form + + + true + + + + + + Set the y-axis range + + + + + + Full Range + + + false + + + + + + + Fit to the plotted data + + + true + + + + + + + + + Custom + + + + + + + false + + + min: + + + + + + + false + + + + 0 + 0 + + + + -100000000000000004384584304507619735463404765184.000000000000000 + + + 100000000000000004384584304507619735463404765184.000000000000000 + + + + + + + false + + + max: + + + + + + + false + + + + 0 + 0 + + + + -100000000000000004384584304507619735463404765184.000000000000000 + + + 100000000000000004384584304507619735463404765184.000000000000000 + + + + + + + + + + 20 + 0 + + + + false + + + background-color: "black"; + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + background-color: "dark blue"; + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + background-color: rgb(85, 0, 127) + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + Qt::Horizontal + + + + 133 + 20 + + + + + + + + Apply + + + + + + + Qt::Horizontal + + + + 132 + 20 + + + + + + + + +