diff --git a/Modules/Chart/include/QmitkChartWidget.h b/Modules/Chart/include/QmitkChartWidget.h index 81378133e4..ed95e789e5 100644 --- a/Modules/Chart/include/QmitkChartWidget.h +++ b/Modules/Chart/include/QmitkChartWidget.h @@ -1,329 +1,337 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkC3jsWidget_h #define QmitkC3jsWidget_h #include #include #include #include class QmitkChartxyData; /*! \brief QmitkChartWidget is a widget to display various charts based on the javascript chart library plotly. * \details Data is added via AddData1D() or AddData2D().\n * There can be multiple charts (of different types with different properties) created by calling AddData1D or AddData2D multiple times.\n\n * The following chart types are supported: * * line chart * * bar chart * * spline chart * * pie chart * * scatter chart * * area chart * * area spline chart. * * Technical details: The javascript code is embedded in a QWebEngineView. The actual js code is implemented in resource\Chart.js. * \sa https://plot.ly/javascript/ for further information about the used javaScript library. * \ingroup Modules/Chart */ class MITKCHART_EXPORT QmitkChartWidget : public QWidget { Q_OBJECT public: /*! * \brief enum of diagram types. */ enum class ChartType { bar, /*!< bar chart, see https://plot.ly/javascript/bar-charts/ */ line, /*!< line chart, see https://plot.ly/javascript/line-charts/ */ spline, /*!< spline chart (smoothed line chart), see https://plot.ly/~jduelfer/23/spline/#/ */ pie, /*!< pie chart, see https://plot.ly/javascript/pie-charts/ */ area, /*!< area chart, see https://plot.ly/javascript/filled-area-plots/ */ area_spline, /*!< area-spline chart, similar to https://plot.ly/~jduelfer/23/spline/#/ */ scatter /*!< scatter chart, see https://plot.ly/javascript/line-and-scatter/ */ }; /*! * \brief enum of chart style (modifies background and line color). */ enum class ColorTheme { darkstyle, /*!< background color: dark gray, foreground color: white*/ lightstyle /*!< background color: white, foreground color: black */ }; enum class LineStyle { solid, dashed }; enum class ChartColor { red, orange, yellow, green, blue, purple, brown, magenta, tan, cyan, olive, maroon, navy, aquamarine, turqouise, silver, lime, teal, indigo, violet, pink, black, white, grey }; enum class AxisScale { linear, log }; /*! * \brief enum of legend position. * See https://plot.ly/javascript/legend/ */ enum class LegendPosition { bottomMiddle, bottomRight, topRight, topLeft, middleRight }; explicit QmitkChartWidget(QWidget* parent = nullptr); //for UnitTests explicit QmitkChartWidget(QWidget *parent, bool unitTest); ~QmitkChartWidget() override; /*! * \brief Adds 1D data to the widget * \details internally, the list is converted to a map with increasing integers keys starting at 0. * \param label the name of the data that is also used as identifier. * \param chartType the chart type that should be used for this data entry * \note the data can be cleared with ClearDiagram() * \note If the label name already exists, the name is replaced with a unique one by concatenating numbers to it. * \warning Pie chart is significantly different than the other chart types. Here, the data given by AddData1D is summed. Each entry represents a different category. */ void AddData1D(const std::vector& data1D, const std::string& label, ChartType chartType = ChartType::bar); /*! * \brief Updates data of an existing label * \param data1D the 1D data , \sa AddData1D * \param label the (existing) label * \note if the label does not exist, nothing happens */ void UpdateData1D(const std::vector &data1D, const std::string &label); /*! * \sa UpdateData1D * \sa AddData2D */ void UpdateData2D(const std::map &data2D, const std::string &label); /*! * \brief Adds 2D data to the widget. Call repeatedly for displaying multiple charts. * \details each entry represents a data point: key: value --> x-value: y-value. * \param label the name of the data that is also used as identifier. * \param chartType the chart type that should be used for this data entry * \note the data can be cleared with ClearDiagram() * \note If the label name already exists, the name is replaced with a unique one by concatenating numbers to it. * \warning Pie chart is significantly different than the other chart types. Here, the data given by AddData1D is summed. Each entry represents a different category. */ + + void UpdateChartExampleData(const std::map& data2D, + const std::string& label, + const std::string& type, + const std::string& color, + const std::string& lineStyle, + const std::string& pieLabelsData = 0); + void AddData2D(const std::map &data2D, const std::string &label, ChartType chartType = ChartType::bar); //Add Function for the ChartExample void AddChartExampleData(const std::map& data2D, const std::string& label, const std::string& type, const std::string& color, const std::string& style, - std::string pieLabelsData = 0); + const std::string& pieLabelsData = 0); /*! * \brief Removes data from the widget, works for 1D and 2D Data * \param label the name of the data that is also used as identifier. * \note All data can be cleared with ClearDiagram() * \throws Invalid Argument Exception when the label cannot be found */ void RemoveData(const std::string& label); void UpdateLabel(const std::string& existingLabel, const std::string& newLabel); QmitkChartxyData *GetDataElementByLabel(const std::string& label) const; /*! * \brief Sets the color of one data entry (identifier is previously assigned label) * \details the color name can be "red" or a hex number (#FF0000). * \warning Either define all data entries with a color or no data entry. If a mixed approach is used, * plotly choses the color of the data entry (that could be the same as a user defined color). * \note If an unknown label is given, nothing happens. * \sa https://www.w3schools.com/cssref/css_colors.asp */ void SetColor(const std::string& label, const std::string& colorName); /*! * \brief Sets the line style of one data entry (identifier is previously assigned label) * \details two line styles are possible: LineStyle::solid and LineStyle::dashed. * The default line style is solid. * \note If an unknown label is given, nothing happens. * \warning only sets the line style if the current chart type is ChartType::line. * However, the line style remains also if the chart changes (e.g. new chart type) */ void SetLineStyle(const std::string& label, LineStyle style); /*! * \brief Sets the axis scale to either linear (default) or logarithmic. * \sa https://plot.ly/javascript/log-plot/ */ void SetYAxisScale(AxisScale scale); void SetXAxisLabel(const std::string& label); void SetYAxisLabel(const std::string& label); /*! * \brief Sets labels for pie chart data. * \note in AddData1D, the label still has to be given that acts as a unique id. However, the label is omitted then. */ void SetPieLabels(const std::vector &pieLabels, const std::string &label); /*! * \brief Sets a title for the chart. */ void SetTitle(const std::string &title); /*! * \brief Sets the chart type for a data entry * \details for available types, see ChartType * \note If an unknown label is given, nothing happens. * \warning Pie chart is significantly different than the other chart types. Here, the data given by AddData1D is summed. Each entry represents a different category. * \sa DiagramType for available types */ void SetChartType(const std::string& label, ChartType type); /*! * \brief Sets error bars for data in x direction * \note If only error plus is provided, the error bars are symmetrical * \param label the name of the data that is also used as identifier. * \param errorPlus the error in positive direction * \param errorMinus the error in negative direction. Same as error plus if omitted */ void SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector& errorMinus = std::vector()); /*! * \brief Sets error bars for data in y direction * \details for parameters, see SetXErrorBars * \note If only error plus is provided, the error bars are symmetrical */ void SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus = std::vector()); /*! * \brief Sets the legend position. * \details Default position is bottom. * \sa LegendPosition for available types */ void SetLegendPosition(LegendPosition position); void SetShowLegend(bool show); void SetStackedData(bool stacked); /*! * \brief Displays the chart in the widget * \param showSubChart if a subchart is displayed inside the widget or not. * \note if no data has been provided, (\sa AddData1D AddData2D), an empty chart is displayed. */ void Show(bool showSubChart=false); /*! * \brief Either displays the dataPoints or not * \param showDataPoints if dataPoints are displayed inside the widget or not. * \details: example for not showing points: https://plot.ly/javascript/line-charts/#styling-line-plot * example for showing the points: https://plot.ly/javascript/pointcloud/ */ void SetShowDataPoints(bool showDataPoints); /*! * \brief Clears all data inside and resets the widget. */ void Clear(); /*! * \brief Sets the theme of the widget. * \details default is dark theme as in MITK. * \warning has to be called before Show() or Reload() to work */ void SetTheme(ColorTheme themeEnabled); /*! * \brief Sets whether the subchart shall be shown. * \details Changes the state of the current chart object. * \note Needs to be reloaded with Reload() to display changes. */ void SetShowSubchart(bool showSubChart); /*! * \brief Sets whether the error bars shall be shown. * \details Changes the state of the current chart object. * \note Needs to be reloaded with Reload() to display changes. * \param showErrorBars if error bars are displayed or not. */ void SetShowErrorBars(bool showErrorBars); /*! * \brief Sets the min and max x values of the chart * \details Zooms in to view the values between minValue and maxValue in x direction */ void SetMinMaxValueXView(double minValueX,double maxValueX); /*! * \brief Sets the min and max y values of the chart * \details Zooms in to view the values between minValue and maxValue in y direction */ void SetMinMaxValueYView(double minValueY, double maxValueY); /*! * \brief Reloads the chart in the widget * \details reloading may be needed to display added data in an existing chart */ void Reload(); QSize sizeHint() const override; public slots: void OnLoadFinished(bool isLoadSuccessful); void OnPageSuccessfullyLoaded(); signals: void PageSuccessfullyLoaded(); private: /*! source: https://stackoverflow.com/questions/29383/converting-bool-to-text-in-c*/ std::string convertBooleanValue(bool value) const; class Impl; std::unique_ptr m_Impl; }; #endif diff --git a/Modules/Chart/src/QmitkChartWidget.cpp b/Modules/Chart/src/QmitkChartWidget.cpp index 1bd5af1c05..9063bccac7 100644 --- a/Modules/Chart/src/QmitkChartWidget.cpp +++ b/Modules/Chart/src/QmitkChartWidget.cpp @@ -1,857 +1,912 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include "mitkExceptionMacro.h" #include #include class CustomPage : public QWebEnginePage { public: CustomPage(QObject *parent = nullptr) : QWebEnginePage(parent) {} virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, const QString &message, int lineNumber, const QString & /*sourceID*/) override { MITK_INFO << "JS > " << lineNumber << ": " << message.toStdString(); } }; class QmitkChartWidget::Impl final { public: explicit Impl(QWidget *parent); ~Impl(); Impl(const Impl &) = delete; Impl &operator=(const Impl &) = delete; void AddData1D(const std::vector &data1D, const std::string &label, QmitkChartWidget::ChartType chartType); void AddData2D(const std::map &data2D, const std::string &label, QmitkChartWidget::ChartType chartType); void AddChartExampleData(const std::map& data2D, const std::string& label, const std::string& type, const std::string& color, const std::string& style, - std::string pieLabelsData = 0); + const std::string& pieLabelsData = 0); void UpdateData1D(const std::vector &data1D, const std::string &label); void UpdateData2D(const std::map &data2D, const std::string &label); + void UpdateChartExampleData(const std::map& data2D, + const std::string& label, + const std::string& type, + const std::string& color, + const std::string& lineStyle, + const std::string& pieLabelsData = 0); void RemoveData(const std::string &label); void UpdateLabel(const std::string &existingLabel, const std::string &newLabel); QmitkChartxyData* GetDataElementByLabel(const std::string& label) const; void ClearData(); void SetColor(const std::string &label, const std::string &colorName); void SetLineStyle(const std::string &label, LineStyle style); void SetYAxisScale(AxisScale scale); void SetXAxisLabel(const std::string &label); void SetYAxisLabel(const std::string &label); void SetPieLabels(const std::vector &pieLabels, const std::string &label); void SetTitle(const std::string &title); void SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus = std::vector()); void SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus = std::vector()); std::string GetThemeName() const; void SetThemeName(ColorTheme style); void SetLegendPosition(LegendPosition position); void Show(bool showSubChart); void SetShowLegend(bool show); void SetShowErrorBars(bool show); void SetStackedData(bool stacked); void SetShowDataPoints(bool showDataPoints = false); void SetShowSubchart(bool showSubChart); void SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType); void SetMinMaxValueXView(double minValueX, double maxValueX); void SetMinMaxValueYView(double minValueY, double maxValueY); QList ConvertErrorVectorToQList(const std::vector &error); QList ConvertVectorToQList(const std::vector &vec); std::string ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const; void ClearJavaScriptChart(); void InitializeJavaScriptChart(); void CallJavaScriptFuntion(const QString &command); QSize sizeHint() const; int GetIndexByString(std::string); private: using ChartxyDataVector = std::vector>; std::string GetUniqueLabelName(const QList &labelList, const std::string &label) const; QList GetDataLabels(const ChartxyDataVector &c3xyData) const; QWebChannel *m_WebChannel; QWebEngineView *m_WebEngineView; QmitkChartData m_C3Data; ChartxyDataVector m_C3xyData; std::map m_ChartTypeToName; std::map m_ChartColorToName; std::map m_ColorThemeToName; std::map m_LegendPositionToName; std::map m_LineStyleToName; std::map m_AxisScaleToName; }; QmitkChartWidget::Impl::Impl(QWidget *parent) : m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent)) { // disable context menu for QWebEngineView m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu); m_WebEngineView->setPage(new CustomPage()); // Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated. m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html"))); m_WebEngineView->page()->setWebChannel(m_WebChannel); m_WebEngineView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); connect(m_WebEngineView, SIGNAL(loadFinished(bool)), parent, SLOT(OnLoadFinished(bool))); auto layout = new QGridLayout(parent); layout->setMargin(0); layout->addWidget(m_WebEngineView); m_ChartTypeToName.emplace(ChartType::bar, "bar"); m_ChartTypeToName.emplace(ChartType::line, "line"); m_ChartTypeToName.emplace(ChartType::spline, "spline"); m_ChartTypeToName.emplace(ChartType::pie, "pie"); m_ChartTypeToName.emplace(ChartType::area, "area"); m_ChartTypeToName.emplace(ChartType::area_spline, "area-spline"); m_ChartTypeToName.emplace(ChartType::scatter, "scatter"); m_ChartColorToName.emplace(ChartColor::red, "red"); m_ChartColorToName.emplace(ChartColor::orange, "orange"); m_ChartColorToName.emplace(ChartColor::yellow, "yellow"); m_ChartColorToName.emplace(ChartColor::green, "green"); m_ChartColorToName.emplace(ChartColor::blue, "blue"); m_ChartColorToName.emplace(ChartColor::purple, "purple"); m_ChartColorToName.emplace(ChartColor::brown, "brown"); m_ChartColorToName.emplace(ChartColor::magenta, "magenta"); m_ChartColorToName.emplace(ChartColor::tan, "tan"); m_ChartColorToName.emplace(ChartColor::cyan, "cyan"); m_ChartColorToName.emplace(ChartColor::olive, "olive"); m_ChartColorToName.emplace(ChartColor::maroon, "maroon"); m_ChartColorToName.emplace(ChartColor::navy, "navy"); m_ChartColorToName.emplace(ChartColor::aquamarine, "aquamarine"); m_ChartColorToName.emplace(ChartColor::turqouise, "turqouise"); m_ChartColorToName.emplace(ChartColor::silver, "silver"); m_ChartColorToName.emplace(ChartColor::lime, "lime"); m_ChartColorToName.emplace(ChartColor::teal, "teal"); m_ChartColorToName.emplace(ChartColor::indigo, "indigo"); m_ChartColorToName.emplace(ChartColor::violet, "violet"); m_ChartColorToName.emplace(ChartColor::pink, "pink"); m_ChartColorToName.emplace(ChartColor::black, "black"); m_ChartColorToName.emplace(ChartColor::white, "white"); m_ChartColorToName.emplace(ChartColor::grey, "grey"); m_LegendPositionToName.emplace(LegendPosition::bottomMiddle, "bottomMiddle"); m_LegendPositionToName.emplace(LegendPosition::bottomRight, "bottomRight"); m_LegendPositionToName.emplace(LegendPosition::topRight, "topRight"); m_LegendPositionToName.emplace(LegendPosition::topLeft, "topLeft"); m_LegendPositionToName.emplace(LegendPosition::middleRight, "middleRight"); m_LineStyleToName.emplace(LineStyle::solid, "solid"); m_LineStyleToName.emplace(LineStyle::dashed, "dashed"); m_AxisScaleToName.emplace(AxisScale::linear, ""); m_AxisScaleToName.emplace(AxisScale::log, "log"); m_ColorThemeToName.emplace(ColorTheme::lightstyle, "light"); m_ColorThemeToName.emplace(ColorTheme::darkstyle, "dark"); } QmitkChartWidget::Impl::~Impl() {} std::string QmitkChartWidget::Impl::GetThemeName() const { return m_C3Data.GetThemeName().toString().toStdString(); } std::string CheckForCorrectHex(const std::string &colorName) { std::regex rgx("([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); std::smatch match; if (!colorName.empty() && colorName.at(0) != '#' && std::regex_search(colorName.begin(), colorName.end(), match, rgx)) { return "#" + colorName; } else { return colorName; } } void QmitkChartWidget::Impl::AddData1D(const std::vector &data1D, const std::string &label, QmitkChartWidget::ChartType type) { std::map transformedData2D; unsigned int count = 0; // transform the 1D data to 2D data for (const auto &ele : data1D) { transformedData2D[count] = ele; count++; } AddData2D(transformedData2D, label, type); } void QmitkChartWidget::Impl::AddData2D(const std::map &data2D, const std::string &label, QmitkChartWidget::ChartType type) { QMap data2DConverted; for (const auto &aValue : data2D) { data2DConverted.insert(aValue.first, aValue.second); } const std::string chartTypeName(m_ChartTypeToName.at(type)); auto definedLabels = GetDataLabels(m_C3xyData); auto uniqueLabel = GetUniqueLabelName(definedLabels, label); if (type == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } unsigned int sizeOfC3xyData = static_cast(m_C3xyData.size()); m_C3xyData.push_back(std::make_unique(data2DConverted, QVariant(QString::fromStdString(uniqueLabel)), QVariant(QString::fromStdString(chartTypeName)), QVariant(sizeOfC3xyData))); } void QmitkChartWidget::Impl::AddChartExampleData(const std::map& data2D, const std::string& label, const std::string& type, const std::string& color, const std::string& lineStyle, std::string pieLabelsData) { QList pieLabelsDataList; while (pieLabelsData.size() != 0) { QVariant oneElement = QString::fromStdString(pieLabelsData.substr(0, pieLabelsData.find(";"))); pieLabelsDataList.push_back(oneElement); if (pieLabelsData.find(";") != std::string::npos) { pieLabelsData.erase(0, pieLabelsData.find(";") + 1); } else { pieLabelsData.erase(pieLabelsData.begin(), pieLabelsData.end()); } } QMap data2DConverted; for (const auto& aValue : data2D) { data2DConverted.insert(aValue.first, aValue.second); } auto definedLabels = GetDataLabels(m_C3xyData); auto uniqueLabel = GetUniqueLabelName(definedLabels, label); if (type == "Scatter") { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } unsigned int sizeOfC3xyData = static_cast(m_C3xyData.size()); std::unique_ptr chartData = std::make_unique( data2DConverted, QVariant(QString::fromStdString(uniqueLabel)), QVariant(QString::fromStdString(type)), QVariant(sizeOfC3xyData)); chartData->SetColor(QVariant(QString::fromStdString(color))); chartData->SetLineStyle(QVariant(QString::fromStdString(lineStyle))); chartData->SetPieLabels(pieLabelsDataList); m_C3xyData.push_back(std::move(chartData)); } void QmitkChartWidget::Impl::UpdateData1D(const std::vector &data1D, const std::string &label) { std::map transformedData2D; unsigned int count = 0; // transform the 1D data to 2D data for (const auto &ele : data1D) { transformedData2D[count] = ele; count++; } UpdateData2D(transformedData2D, label); } void QmitkChartWidget::Impl::UpdateData2D(const std::map &data2D, const std::string &label) { auto element = GetDataElementByLabel(label); if (element) { QMap data2DConverted; for (const auto &aValue : data2D) { data2DConverted.insert(aValue.first, aValue.second); } element->SetData(data2DConverted); } } +void QmitkChartWidget::Impl::UpdateChartExampleData(const std::map& data2D, + const std::string& label, + const std::string& type, + const std::string& color, + const std::string& lineStyle, + const std::string& pieLabelsData) +{ + UpdateData2D(data2D, label); + + auto element = GetDataElementByLabel(label); + if (element) + { + element->SetChartType(QString::fromStdString(type)); + element->SetColor(QString::fromStdString(color)); + element->SetLineStyle(QString::fromStdString(lineStyle)); + + if (pieLabelsData != "") + { + std::string pieLabelsDataWorkingString = pieLabelsData; + + QList pieLabelsDataList; + while (pieLabelsDataWorkingString.size() != 0) + { + QVariant oneElement = QString::fromStdString(pieLabelsDataWorkingString.substr(0, pieLabelsDataWorkingString.find(";"))); + pieLabelsDataList.push_back(oneElement); + + if (pieLabelsDataWorkingString.find(";") != std::string::npos) + { + pieLabelsDataWorkingString.erase(0, pieLabelsDataWorkingString.find(";") + 1); + } + else + { + pieLabelsDataWorkingString.erase(pieLabelsDataWorkingString.begin(), pieLabelsDataWorkingString.end()); + } + } + + element->SetPieLabels(pieLabelsDataList); + } + } +} + void QmitkChartWidget::Impl::RemoveData(const std::string &label) { for (ChartxyDataVector::iterator iter = m_C3xyData.begin(); iter != m_C3xyData.end(); ++iter) { if ((*iter)->GetLabel().toString().toStdString() == label) { m_C3xyData.erase(iter); return; } } throw std::invalid_argument("Cannot Remove Data because the label does not exist."); } void QmitkChartWidget::Impl::ClearData() { for (auto &xyData : m_C3xyData) { m_WebChannel->deregisterObject(xyData.get()); } m_C3xyData.clear(); } void QmitkChartWidget::Impl::UpdateLabel(const std::string &existingLabel, const std::string &newLabel) { auto element = GetDataElementByLabel(existingLabel); if (element) { auto definedLabels = GetDataLabels(m_C3xyData); auto uniqueLabel = GetUniqueLabelName(definedLabels, newLabel); element->SetLabel(QString::fromStdString(uniqueLabel)); } } void QmitkChartWidget::Impl::SetColor(const std::string &label, const std::string &colorName) { auto element = GetDataElementByLabel(label); if (element) { auto colorChecked = CheckForCorrectHex(colorName); element->SetColor(QVariant(QString::fromStdString(colorChecked))); } } void QmitkChartWidget::Impl::SetLineStyle(const std::string &label, LineStyle style) { auto element = GetDataElementByLabel(label); const std::string lineStyleName(m_LineStyleToName.at(style)); element->SetLineStyle(QVariant(QString::fromStdString(lineStyleName))); } void QmitkChartWidget::Impl::SetYAxisScale(AxisScale scale) { const std::string axisScaleName(m_AxisScaleToName.at(scale)); m_C3Data.SetYAxisScale(QString::fromStdString(axisScaleName)); } QmitkChartxyData *QmitkChartWidget::Impl::GetDataElementByLabel(const std::string &label) const { for (const auto &qmitkChartxyData : m_C3xyData) { if (qmitkChartxyData->GetLabel().toString() == label.c_str()) { return qmitkChartxyData.get(); } } MITK_WARN << "label " << label << " not found in QmitkChartWidget"; return nullptr; } QList QmitkChartWidget::Impl::GetDataLabels(const ChartxyDataVector &c3xyData) const { QList dataLabels; for (auto element = c3xyData.begin(); element != c3xyData.end(); ++element) { dataLabels.push_back((*element)->GetLabel()); } return dataLabels; } void QmitkChartWidget::Impl::SetXAxisLabel(const std::string &label) { m_C3Data.SetXAxisLabel(QString::fromStdString(label)); } void QmitkChartWidget::Impl::SetYAxisLabel(const std::string &label) { m_C3Data.SetYAxisLabel(QString::fromStdString(label)); } void QmitkChartWidget::Impl::SetPieLabels(const std::vector &pieLabels, const std::string &label) { auto element = GetDataElementByLabel(label); if (element) { if (element->GetChartType() == QVariant("pie")) { auto dataY = element->GetYData(); element->SetPieLabels(ConvertVectorToQList(pieLabels)); if (static_cast(dataY.size()) != pieLabels.size()) { MITK_INFO << "data has " << dataY.size() << " entries whereas pie labels have " << pieLabels.size() << " entries. Unnamed pie labels automatically get a numerical label."; } } else { MITK_INFO << "label" << label << "has chart type " << element->GetChartType().toString().toStdString() << ", but pie is required"; } } } void QmitkChartWidget::Impl::SetTitle(const std::string &title) { m_C3Data.SetTitle(QString::fromStdString(title)); } void QmitkChartWidget::Impl::SetThemeName(QmitkChartWidget::ColorTheme style) { const std::string themeName(m_ColorThemeToName.at(style)); m_C3Data.SetThemeName(QString::fromStdString(themeName)); } void QmitkChartWidget::Impl::SetLegendPosition(QmitkChartWidget::LegendPosition legendPosition) { const std::string legendPositionName(m_LegendPositionToName.at(legendPosition)); m_C3Data.SetLegendPosition(QString::fromStdString(legendPositionName)); } void QmitkChartWidget::Impl::Show(bool showSubChart) { if (m_C3xyData.empty()) { MITK_WARN << "no data available for display in chart"; } else { m_C3Data.SetAppearance(showSubChart, m_C3xyData.front()->GetChartType() == QVariant("pie")); } InitializeJavaScriptChart(); } void QmitkChartWidget::Impl::SetShowLegend(bool show) { m_C3Data.SetShowLegend(show); } void QmitkChartWidget::Impl::SetStackedData(bool stacked) { m_C3Data.SetStackedData(stacked); } void QmitkChartWidget::Impl::SetShowErrorBars(bool show) { m_C3Data.SetShowErrorBars(show); } void QmitkChartWidget::Impl::SetShowDataPoints(bool showDataPoints) { if (showDataPoints == true) { m_C3Data.SetDataPointSize(6.5); } else { m_C3Data.SetDataPointSize(0); } } void QmitkChartWidget::Impl::SetShowSubchart(bool showSubChart) { m_C3Data.SetShowSubchart(showSubChart); } void QmitkChartWidget::Impl::SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType) { auto element = GetDataElementByLabel(label); if (element) { if (chartType == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } const std::string chartTypeName(m_ChartTypeToName.at(chartType)); element->SetChartType(QVariant(QString::fromStdString(chartTypeName))); } } void QmitkChartWidget::Impl::SetMinMaxValueXView(double minValueX, double maxValueX) { m_C3Data.SetMinValueXView(minValueX); m_C3Data.SetMaxValueXView(maxValueX); } void QmitkChartWidget::Impl::SetMinMaxValueYView(double minValueY, double maxValueY) { m_C3Data.SetMinValueYView(minValueY); m_C3Data.SetMaxValueYView(maxValueY); } QList QmitkChartWidget::Impl::ConvertErrorVectorToQList(const std::vector &error) { QList errorConverted; for (const auto &aValue : error) { errorConverted.append(aValue); } return errorConverted; } QList QmitkChartWidget::Impl::ConvertVectorToQList(const std::vector &vec) { QList vecConverted; for (const auto &aValue : vec) { vecConverted.append(QString::fromStdString(aValue)); } return vecConverted; } void QmitkChartWidget::Impl::SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { auto element = GetDataElementByLabel(label); if (element) { auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus); auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus); element->SetXErrorDataPlus(errorConvertedPlus); element->SetXErrorDataMinus(errorConvertedMinus); } } void QmitkChartWidget::Impl::SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { auto element = GetDataElementByLabel(label); if (element) { auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus); auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus); element->SetYErrorDataPlus(errorConvertedPlus); element->SetYErrorDataMinus(errorConvertedMinus); } } std::string QmitkChartWidget::Impl::ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const { return m_ChartTypeToName.at(chartType); } QSize QmitkChartWidget::Impl::sizeHint() const { return QSize(400, 300); } void QmitkChartWidget::Impl::CallJavaScriptFuntion(const QString &command) { m_WebEngineView->page()->runJavaScript(command); } void QmitkChartWidget::Impl::ClearJavaScriptChart() { m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html"))); } void QmitkChartWidget::Impl::InitializeJavaScriptChart() { auto alreadyRegisteredObjects = m_WebChannel->registeredObjects(); auto alreadyRegisteredObjectsValues = alreadyRegisteredObjects.values(); // only register objects that have not been registered yet if (alreadyRegisteredObjectsValues.indexOf(&m_C3Data) == -1) { m_WebChannel->registerObject(QStringLiteral("chartData"), &m_C3Data); } unsigned count = 0; for (auto &xyData : m_C3xyData) { // only register objects that have not been registered yet if (alreadyRegisteredObjectsValues.indexOf(xyData.get()) == -1) { QString variableName = "xyData" + QString::number(count); m_WebChannel->registerObject(variableName, xyData.get()); } count++; } m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/QmitkChartWidget.html"))); } std::string QmitkChartWidget::Impl::GetUniqueLabelName(const QList &labelList, const std::string &label) const { QString currentLabel = QString::fromStdString(label); int counter = 0; while (labelList.contains(currentLabel)) { currentLabel = QString::fromStdString(label + std::to_string(counter)); counter++; } return currentLabel.toStdString(); } QmitkChartWidget::QmitkChartWidget(QWidget *parent) : QWidget(parent), m_Impl(new Impl(this)) { connect(this, &QmitkChartWidget::PageSuccessfullyLoaded, this, &QmitkChartWidget::OnPageSuccessfullyLoaded); } QmitkChartWidget::~QmitkChartWidget() {} void QmitkChartWidget::SetColor(const std::string &label, const std::string &colorName) { m_Impl->SetColor(label, colorName); } void QmitkChartWidget::SetLineStyle(const std::string &label, LineStyle style) { m_Impl->SetLineStyle(label, style); } void QmitkChartWidget::SetYAxisScale(AxisScale scale) { m_Impl->SetYAxisScale(scale); } void QmitkChartWidget::AddData1D(const std::vector &data1D, const std::string &label, ChartType type) { m_Impl->AddData1D(data1D, label, type); } void QmitkChartWidget::AddData2D(const std::map& data2D, const std::string& label, ChartType type) { m_Impl->AddData2D(data2D, label, type); } void QmitkChartWidget::AddChartExampleData(const std::map& data2D, const std::string& label, const std::string& type, const std::string& color, const std::string& lineStyle, - std::string pieLabelsData) + const std::string& pieLabelsData) { m_Impl->AddChartExampleData(data2D, label, type, color, lineStyle, pieLabelsData); } void QmitkChartWidget::UpdateData1D(const std::vector &data1D, const std::string &label) { m_Impl->UpdateData1D(data1D, label); } void QmitkChartWidget::UpdateData2D(const std::map &data2D, const std::string &label) { m_Impl->UpdateData2D(data2D, label); } +void QmitkChartWidget::UpdateChartExampleData(const std::map& data2D, + const std::string& label, + const std::string& type, + const std::string& color, + const std::string& lineStyle, + const std::string& pieLabelsData) +{ + m_Impl->UpdateChartExampleData(data2D, label, type, color, lineStyle, pieLabelsData); void QmitkChartWidget::RemoveData(const std::string &label) { m_Impl->RemoveData(label); } void QmitkChartWidget::UpdateLabel(const std::string &existingLabel, const std::string &newLabel) { m_Impl->UpdateLabel(existingLabel, newLabel); } QmitkChartxyData* QmitkChartWidget::GetDataElementByLabel(const std::string& label) const { return m_Impl->GetDataElementByLabel(label); } void QmitkChartWidget::SetXAxisLabel(const std::string &label) { m_Impl->SetXAxisLabel(label); } void QmitkChartWidget::SetYAxisLabel(const std::string &label) { m_Impl->SetYAxisLabel(label); } void QmitkChartWidget::SetPieLabels(const std::vector &pieLabels, const std::string &label) { m_Impl->SetPieLabels(pieLabels, label); } void QmitkChartWidget::SetTitle(const std::string &title) { m_Impl->SetTitle(title); } void QmitkChartWidget::SetShowDataPoints(bool showDataPoints) { m_Impl->SetShowDataPoints(showDataPoints); } void QmitkChartWidget::SetChartType(const std::string &label, ChartType type) { m_Impl->SetChartType(label, type); } void QmitkChartWidget::SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { m_Impl->SetXErrorBars(label, errorPlus, errorMinus); } void QmitkChartWidget::SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { m_Impl->SetYErrorBars(label, errorPlus, errorMinus); } void QmitkChartWidget::SetLegendPosition(LegendPosition position) { m_Impl->SetLegendPosition(position); } void QmitkChartWidget::SetShowLegend(bool show) { m_Impl->SetShowLegend(show); } void QmitkChartWidget::SetStackedData(bool stacked) { m_Impl->SetStackedData(stacked); } void QmitkChartWidget::Show(bool showSubChart) { m_Impl->Show(showSubChart); } void QmitkChartWidget::Clear() { m_Impl->ClearData(); m_Impl->ClearJavaScriptChart(); } void QmitkChartWidget::OnLoadFinished(bool isLoadSuccessful) { if (isLoadSuccessful) { emit PageSuccessfullyLoaded(); } } void QmitkChartWidget::OnPageSuccessfullyLoaded() { auto themeName = m_Impl->GetThemeName(); QString command; if (themeName == "dark") { command = QString("changeTheme('dark')"); } else { command = QString("changeTheme('light')"); } m_Impl->CallJavaScriptFuntion(command); } void QmitkChartWidget::SetTheme(ColorTheme themeEnabled) { m_Impl->SetThemeName(themeEnabled); } void QmitkChartWidget::SetShowSubchart(bool showSubChart) { m_Impl->SetShowSubchart(showSubChart); } void QmitkChartWidget::SetShowErrorBars(bool showErrorBars) { m_Impl->SetShowErrorBars(showErrorBars); } void QmitkChartWidget::SetMinMaxValueXView(double minValueX, double maxValueX) { m_Impl->SetMinMaxValueXView(minValueX, maxValueX); } void QmitkChartWidget::SetMinMaxValueYView(double minValueY, double maxValueY) { m_Impl->SetMinMaxValueYView(minValueY, maxValueY); } void QmitkChartWidget::Reload() { const QString command = QString("Reload()"); m_Impl->CallJavaScriptFuntion(command); } QSize QmitkChartWidget::sizeHint() const { return m_Impl->sizeHint(); } diff --git a/Plugins/org.mitk.gui.qt.chartExample/src/internal/QmitkChartExampleView.cpp b/Plugins/org.mitk.gui.qt.chartExample/src/internal/QmitkChartExampleView.cpp index 879d4ed3e8..9291093e7c 100644 --- a/Plugins/org.mitk.gui.qt.chartExample/src/internal/QmitkChartExampleView.cpp +++ b/Plugins/org.mitk.gui.qt.chartExample/src/internal/QmitkChartExampleView.cpp @@ -1,464 +1,475 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkChartExampleView.h" #include const std::string QmitkChartExampleView::VIEW_ID = "org.mitk.views.qmitkchartexample"; void QmitkChartExampleView::SetFocus() { m_Controls.m_buttonCreateChart->setFocus(); } void QmitkChartExampleView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); CreateConnectionsForGUIElements(); connect(m_Controls.m_comboBoxChartType, &QComboBox::currentTextChanged, this, &QmitkChartExampleView::AdaptDataGUI); m_Controls.m_lineEditDataXVector->setText("0;1;2;3;4;5;6;7;8;9"); m_Controls.m_lineEditDataYVector->setText("0;1;2;3;4;5;6;7;8;9"); m_Controls.m_lineEditDataLabel->setText("Test"); m_Controls.m_lineEditXAxisLabel->setText("X-Axis"); m_Controls.m_lineEditYAxisLabel->setText("Y-Axis"); m_Controls.m_lineEditTitle->setText("Title"); m_Controls.m_labelPieData->setVisible(false); m_Controls.m_lineEditPieDataLabel->setVisible(false); m_Controls.m_groupBoxErrors->setVisible(false); m_Controls.m_groupBoxXErrors->setVisible(false); m_Controls.m_groupBoxYErrors->setVisible(false); m_Controls.m_doubleSpinBox_maxZoomX->setValue(10); m_Controls.m_doubleSpinBox_maxZoomY->setValue(10); m_AxisScaleNameToAxisScaleType.emplace("linear", QmitkChartWidget::AxisScale::linear); m_AxisScaleNameToAxisScaleType.emplace("logarithmic", QmitkChartWidget::AxisScale::log); m_LegendPositionNameToLegendPositionType.emplace("bottom middle", QmitkChartWidget::LegendPosition::bottomMiddle); m_LegendPositionNameToLegendPositionType.emplace("bottom right", QmitkChartWidget::LegendPosition::bottomRight); m_LegendPositionNameToLegendPositionType.emplace("top right", QmitkChartWidget::LegendPosition::topRight); m_LegendPositionNameToLegendPositionType.emplace("top left", QmitkChartWidget::LegendPosition::topLeft); m_LegendPositionNameToLegendPositionType.emplace("middle right", QmitkChartWidget::LegendPosition::middleRight); } void QmitkChartExampleView::CreateConnectionsForGUIElements() { connect(m_Controls.m_buttonCreateChart, &QPushButton::clicked, this, &QmitkChartExampleView::CreateChart); connect(m_Controls.m_buttonUpdateData, &QPushButton::clicked, this, &QmitkChartExampleView::UpdateData); connect(m_Controls.m_buttonClearChart, &QPushButton::clicked, this, &QmitkChartExampleView::ClearChart); connect(m_Controls.m_buttonAddData, &QPushButton::clicked, this, &QmitkChartExampleView::AddData); connect(m_Controls.m_comboBoxExistingData, &QComboBox::currentTextChanged, this, &QmitkChartExampleView::UpdateSelectedData); connect(m_Controls.m_checkBoxEnableErrors, &QCheckBox::toggled, this, &QmitkChartExampleView::ShowErrorOptions); connect(m_Controls.m_checkBoxEnableXErrors, &QCheckBox::toggled, this, &QmitkChartExampleView::ShowXErrorOptions); connect(m_Controls.m_checkBoxEnableYErrors, &QCheckBox::toggled, this, &QmitkChartExampleView::ShowYErrorOptions); connect(m_Controls.m_doubleSpinBox_minZoomX, &QSpinBox::editingFinished, this, &QmitkChartExampleView::AdaptZoomX); connect(m_Controls.m_doubleSpinBox_maxZoomX, &QSpinBox::editingFinished, this, &QmitkChartExampleView::AdaptZoomX); connect(m_Controls.m_doubleSpinBox_minZoomY, &QSpinBox::editingFinished, this, &QmitkChartExampleView::AdaptZoomY); connect(m_Controls.m_doubleSpinBox_maxZoomY, &QSpinBox::editingFinished, this, &QmitkChartExampleView::AdaptZoomY); connect(m_Controls.m_comboBoxLegendPosition, &QComboBox::currentTextChanged, this, &QmitkChartExampleView::OnLegendPositionChanged); connect(m_Controls.m_lineEditTitle, &QLineEdit::editingFinished, this, &QmitkChartExampleView::OnTitleChanged); connect(m_Controls.m_lineEditXAxisLabel, &QLineEdit::editingFinished, this, &QmitkChartExampleView::OnXAxisLabelChanged); connect(m_Controls.m_lineEditYAxisLabel, &QLineEdit::editingFinished, this, &QmitkChartExampleView::OnYAxisLabelChanged); connect(m_Controls.m_comboBoxYAxisScale, &QComboBox::currentTextChanged, this, &QmitkChartExampleView::OnYAxisScaleChanged); connect(m_Controls.m_checkBoxShowLegend, &QCheckBox::stateChanged, this, &QmitkChartExampleView::OnShowLegendChanged); connect(m_Controls.m_checkBoxStackedData, &QCheckBox::stateChanged, this, &QmitkChartExampleView::OnStackedDataChanged); connect(m_Controls.m_checkBoxShowDataPoints, &QCheckBox::stateChanged, this, &QmitkChartExampleView::OnShowDataPointsChanged); connect(m_Controls.m_checkBoxShowSubchart, &QCheckBox::stateChanged, this, &QmitkChartExampleView::OnShowSubchartChanged); } void QmitkChartExampleView::AddData() { QString lineEditDataX = m_Controls.m_lineEditDataXVector->text(); QString lineEditDataY = m_Controls.m_lineEditDataYVector->text(); auto dataX = ConvertToDoubleVector(lineEditDataX); auto dataY = ConvertToDoubleVector(lineEditDataY); auto dataXandY = CreateMap(dataX, dataY); QString data = QString::fromStdString(ConvertToText(dataXandY)); std::string dataLabel = m_Controls.m_lineEditDataLabel->text().toStdString(); std::string chartTypeAsString = m_Controls.m_comboBoxChartType->currentText().toStdString(); std::string chartColorAsString = m_Controls.m_comboBoxColor->currentText().toStdString(); std::string chartLineStyleAsString = m_Controls.m_comboBoxLineStyle->currentText().toStdString(); std::string pieLabelsAsString = m_Controls.m_lineEditPieDataLabel->text().toStdString(); if (std::find(labelStorage.begin(), labelStorage.end(), dataLabel) != labelStorage.end()) { m_Controls.m_labelInfo->setText("This data already exists"); m_Controls.m_labelInfo->setStyleSheet("color: red;"); return; } if (dataX.size() != dataY.size()) { m_Controls.m_labelInfo->setText("Data x and y size have to be equal"); m_Controls.m_labelInfo->setStyleSheet("color: red;"); return; } QString pieLabelsData = m_Controls.m_lineEditPieDataLabel->text(); if (chartTypeAsString == "Pie") { if (!pieLabelsData.isEmpty()) { auto pieLabels = ConvertToStringVector(pieLabelsData); m_Controls.m_Chart->SetPieLabels(pieLabels, dataLabel); } } labelStorage.push_back(dataLabel); m_Controls.m_Chart->AddChartExampleData(dataXandY, dataLabel, chartTypeAsString, chartColorAsString, chartLineStyleAsString, pieLabelsData.toStdString()); m_Controls.m_comboBoxExistingData->addItem(m_Controls.m_lineEditDataLabel->text()); if (m_Controls.m_checkBoxEnableErrors->isChecked()) { if (m_Controls.m_checkBoxEnableXErrors->isChecked()) { auto errorsPlus = ConvertToDoubleVector(m_Controls.m_lineEditXErrorPlus->text()); auto errorsMinus = ConvertToDoubleVector(m_Controls.m_lineEditXErrorMinus->text()); m_Controls.m_Chart->SetXErrorBars(m_Controls.m_lineEditDataLabel->text().toStdString(), errorsPlus, errorsMinus); } if (m_Controls.m_checkBoxEnableYErrors->isChecked()) { auto errorsPlus = ConvertToDoubleVector(m_Controls.m_lineEditYErrorPlus->text()); auto errorsMinus = ConvertToDoubleVector(m_Controls.m_lineEditYErrorMinus->text()); m_Controls.m_Chart->SetYErrorBars(m_Controls.m_lineEditDataLabel->text().toStdString(), errorsPlus, errorsMinus); } } QString dataOverview; dataOverview.append(m_Controls.m_lineEditDataLabel->text()); dataOverview.append("(").append(m_Controls.m_comboBoxChartType->currentText()); dataOverview.append(", ").append(m_Controls.m_comboBoxLineStyle->currentText()); dataOverview.append(")"); dataOverview.append(":").append(data); m_Controls.m_plainTextEditDataView->appendPlainText(dataOverview); } void QmitkChartExampleView::CreateChart() { auto dataYAxisScaleType = m_AxisScaleNameToAxisScaleType.at(m_Controls.m_comboBoxYAxisScale->currentText().toStdString()); auto xAxisLabel = m_Controls.m_lineEditXAxisLabel->text().toStdString(); auto yAxisLabel = m_Controls.m_lineEditYAxisLabel->text().toStdString(); auto showLegend = m_Controls.m_checkBoxShowLegend->isChecked(); auto legendPosition = m_LegendPositionNameToLegendPositionType.at(m_Controls.m_comboBoxLegendPosition->currentText().toStdString()); auto showDataPoints = m_Controls.m_checkBoxShowDataPoints->isChecked(); auto stackedData = m_Controls.m_checkBoxStackedData->isChecked(); auto showSubchart = m_Controls.m_checkBoxShowSubchart->isChecked(); auto title = m_Controls.m_lineEditTitle->text().toStdString(); m_Controls.m_Chart->SetTitle(title); m_Controls.m_Chart->SetYAxisScale(dataYAxisScaleType); m_Controls.m_Chart->SetXAxisLabel(xAxisLabel); m_Controls.m_Chart->SetYAxisLabel(yAxisLabel); m_Controls.m_Chart->SetShowLegend(showLegend); m_Controls.m_Chart->SetLegendPosition(legendPosition); m_Controls.m_Chart->SetShowErrorBars(true); m_Controls.m_Chart->SetShowDataPoints(showDataPoints); m_Controls.m_Chart->SetStackedData(stackedData); m_Controls.m_Chart->Show(showSubchart); } void QmitkChartExampleView::UpdateData() { if (m_Controls.m_comboBoxExistingData->currentText().isEmpty()) { m_Controls.m_labelInfo->setText("Please enter a valid Datalabel"); m_Controls.m_labelInfo->setStyleSheet("color: red;"); return; } if (m_Controls.m_lineEditDataLabel->text() != m_Controls.m_comboBoxExistingData->currentText()) { return; } - auto dataLabel = m_Controls.m_lineEditDataLabel->text().toStdString(); - m_Controls.m_Chart->RemoveData(dataLabel); + QString lineEditDataX = m_Controls.m_lineEditDataXVector->text(); + QString lineEditDataY = m_Controls.m_lineEditDataYVector->text(); + auto dataX = ConvertToDoubleVector(lineEditDataX); + auto dataY = ConvertToDoubleVector(lineEditDataY); + auto dataXandY = CreateMap(dataX, dataY); + QString data = QString::fromStdString(ConvertToText(dataXandY)); - auto it = std::find(labelStorage.begin(), labelStorage.end(), dataLabel); - labelStorage.erase(it); + std::string dataLabel = m_Controls.m_lineEditDataLabel->text().toStdString(); + std::string chartTypeAsString = m_Controls.m_comboBoxChartType->currentText().toStdString(); + std::string chartColorAsString = m_Controls.m_comboBoxColor->currentText().toStdString(); + std::string chartLineStyleAsString = m_Controls.m_comboBoxLineStyle->currentText().toStdString(); + std::string pieLabelsAsString = m_Controls.m_lineEditPieDataLabel->text().toStdString(); - auto index = m_Controls.m_comboBoxExistingData->findText(QString::fromStdString(dataLabel)); - m_Controls.m_comboBoxExistingData->removeItem(index); + if (dataX.size() != dataY.size()) + { + m_Controls.m_labelInfo->setText("Data x and y size have to be equal"); + m_Controls.m_labelInfo->setStyleSheet("color: red;"); + return; + } - AddData(); + m_Controls.m_Chart->UpdateChartExampleData(dataXandY, dataLabel, chartTypeAsString, chartColorAsString, chartLineStyleAsString, pieLabelsAsString); } void QmitkChartExampleView::UpdateSelectedData() { std::string label = m_Controls.m_comboBoxExistingData->currentText().toStdString(); auto data = m_Controls.m_Chart->GetDataElementByLabel(label); if (data == nullptr) { return; } auto x = data->GetXData(); auto y = data->GetYData(); auto xVector = x.toVector().toStdVector(); auto yVector = y.toVector().toStdVector(); QString xString = QString::fromStdString(ConvertToText(xVector)); QString yString = QString::fromStdString(ConvertToText(yVector)); auto color = data->GetColor(); auto type = data->GetChartType(); auto style = data->GetLineStyle(); int colorIndex = m_Controls.m_Chart->GetIndexByString(color.toString().toStdString()); int typeIndex = m_Controls.m_Chart->GetIndexByString(type.toString().toStdString()); int styleIndex = m_Controls.m_Chart->GetIndexByString(style.toString().toStdString()); if (type.toString() == "Pie") { m_Controls.m_comboBoxLineStyle->setVisible(false); m_Controls.m_labelLineStyle->setVisible(false); m_Controls.m_lineEditPieDataLabel->setVisible(true); m_Controls.m_labelPieData->setVisible(true); auto pieLabels = data->GetPieLabels(); auto pieLabelsVector = pieLabels.toVector().toStdVector(); QString pieLabelsString = QString::fromStdString(ConvertToText(pieLabelsVector)); m_Controls.m_lineEditPieDataLabel->setText(pieLabelsString); } else { m_Controls.m_lineEditPieDataLabel->setVisible(false); m_Controls.m_labelPieData->setVisible(false); m_Controls.m_comboBoxLineStyle->setVisible(true); m_Controls.m_labelLineStyle->setVisible(true); m_Controls.m_comboBoxLineStyle->setCurrentIndex(styleIndex); } m_Controls.m_lineEditDataXVector->setText(xString); m_Controls.m_lineEditDataYVector->setText(yString); m_Controls.m_lineEditDataLabel->setText(QString::fromStdString(label)); m_Controls.m_comboBoxColor->setCurrentIndex(colorIndex); m_Controls.m_comboBoxChartType->setCurrentIndex(typeIndex); } void QmitkChartExampleView::ClearChart() { m_Controls.m_Chart->Clear(); m_Controls.m_plainTextEditDataView->clear(); m_Controls.m_comboBoxExistingData->clear(); labelStorage.clear(); } void QmitkChartExampleView::ShowErrorOptions(bool show) { m_Controls.m_groupBoxErrors->setVisible(show); } void QmitkChartExampleView::ShowXErrorOptions(bool show) { m_Controls.m_groupBoxXErrors->setVisible(show); } void QmitkChartExampleView::ShowYErrorOptions(bool show) { m_Controls.m_groupBoxYErrors->setVisible(show); } void QmitkChartExampleView::AdaptZoomX() { m_Controls.m_Chart->SetMinMaxValueXView(m_Controls.m_doubleSpinBox_minZoomX->value(), m_Controls.m_doubleSpinBox_maxZoomX->value()); m_Controls.m_Chart->Show(); } void QmitkChartExampleView::AdaptZoomY() { m_Controls.m_Chart->SetMinMaxValueYView(m_Controls.m_doubleSpinBox_minZoomY->value(), m_Controls.m_doubleSpinBox_maxZoomY->value()); m_Controls.m_Chart->Show(); } void QmitkChartExampleView::AdaptDataGUI(QString chartType) { if (chartType == "Pie") { m_Controls.m_labelPieData->setVisible(true); m_Controls.m_lineEditPieDataLabel->setVisible(true); m_Controls.m_labelLineStyle->setVisible(false); m_Controls.m_comboBoxLineStyle->setVisible(false); } else { m_Controls.m_labelLineStyle->setVisible(true); m_Controls.m_comboBoxLineStyle->setVisible(true); m_Controls.m_labelPieData->setVisible(false); m_Controls.m_lineEditPieDataLabel->setVisible(false); } } void QmitkChartExampleView::OnLegendPositionChanged(const QString &newText) { auto legendPosition = m_LegendPositionNameToLegendPositionType.at(newText.toStdString()); m_Controls.m_Chart->SetLegendPosition(legendPosition); } void QmitkChartExampleView::OnTitleChanged() { auto newTitle = m_Controls.m_lineEditTitle->text(); m_Controls.m_Chart->SetTitle(newTitle.toStdString()); } void QmitkChartExampleView::OnXAxisLabelChanged() { auto newXAxisLabel = m_Controls.m_lineEditXAxisLabel->text(); m_Controls.m_Chart->SetXAxisLabel(newXAxisLabel.toStdString()); } void QmitkChartExampleView::OnYAxisLabelChanged() { auto newYAxisLabel = m_Controls.m_lineEditYAxisLabel->text(); m_Controls.m_Chart->SetYAxisLabel(newYAxisLabel.toStdString()); } void QmitkChartExampleView::OnYAxisScaleChanged(const QString &newYAxisScale) { auto yAxisScale = m_AxisScaleNameToAxisScaleType.at(newYAxisScale.toStdString()); m_Controls.m_Chart->SetYAxisScale(yAxisScale); } void QmitkChartExampleView::OnShowLegendChanged(int newState) { m_Controls.m_Chart->SetShowLegend(newState == Qt::Checked); } void QmitkChartExampleView::OnStackedDataChanged(int newState) { m_Controls.m_Chart->SetStackedData(newState == Qt::Checked); } void QmitkChartExampleView::OnShowDataPointsChanged(int newState) { m_Controls.m_Chart->SetShowDataPoints(newState == Qt::Checked); } void QmitkChartExampleView::OnShowSubchartChanged(int newState) { m_Controls.m_Chart->SetShowSubchart(newState == Qt::Checked); } std::map QmitkChartExampleView::CreateMap(std::vector keys, std::vector values) const { std::map aMap; std::transform(keys.begin(), keys.end(), values.begin(), std::inserter(aMap, aMap.end()), [](double a, double b) { return std::make_pair(a, b); }); return aMap; } std::string QmitkChartExampleView::ConvertToText(std::map numbers, std::string delimiter) const { std::ostringstream oss; oss.precision(3); if (!numbers.empty()) { for (const auto keyValue : numbers) { oss << keyValue.first << ":" << keyValue.second << delimiter; } } auto aString = oss.str(); aString.pop_back(); return aString; } std::string QmitkChartExampleView::ConvertToText(std::vector numbers, std::string delimiter) const { std::ostringstream oss; oss.precision(3); if (!numbers.empty()) { for (auto number : numbers) { oss << number.toDouble() << delimiter; } } auto aString = oss.str(); aString.pop_back(); return aString; } std::string QmitkChartExampleView::ConvertToText(QVariantList list, std::string delimiter) const { std::ostringstream oss; if (!list.isEmpty()) { for (auto element : list) { oss << element.toString().toStdString() << delimiter; } auto aString = oss.str(); aString.pop_back(); return aString; } return ""; } std::vector QmitkChartExampleView::ConvertToDoubleVector(const QString& data, QChar delimiter) const { std::vector output; if (data.isEmpty()) { return output; } for (const QString entry : data.split(delimiter)) { output.push_back(entry.toDouble()); } return output; } std::vector QmitkChartExampleView::ConvertToStringVector(const QString& data, QChar delimiter) const { std::vector output; if (data.isEmpty()) { return output; } for (const QString entry : data.split(delimiter)) { output.push_back(entry.toStdString()); } return output; }