diff --git a/Modules/Chart/include/QmitkChartData.h b/Modules/Chart/include/QmitkChartData.h index dcdb092114..0c0ba8cda7 100644 --- a/Modules/Chart/include/QmitkChartData.h +++ b/Modules/Chart/include/QmitkChartData.h @@ -1,124 +1,128 @@ /*=================================================================== 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 QmitkC3Data_h #define QmitkC3Data_h #include #include /** \brief This class holds the relevant properties for the chart generation with C3 such as labels and diagram type. * It is derived from QObject, because we need Q_PROPERTIES to send Data via QWebChannel to JavaScript. * \sa The actual data for the chart generation is in QmitkC3xyData! */ class QmitkChartData : public QObject { Q_OBJECT Q_PROPERTY(QVariant m_xAxisLabel READ GetXAxisLabel WRITE SetXAxisLabel NOTIFY SignalXAxisLabelChanged); Q_PROPERTY(QVariant m_yAxisLabel READ GetYAxisLabel WRITE SetYAxisLabel NOTIFY SignalYAxisLabelChanged); Q_PROPERTY(QVariant m_chartTitle READ GetTitle WRITE SetTitle NOTIFY SignalTitleChanged); Q_PROPERTY(QVariant m_themeName READ GetThemeName WRITE SetThemeName NOTIFY SignalThemeNameChanged); Q_PROPERTY(QVariant m_LegendPosition READ GetLegendPosition WRITE SetLegendPosition NOTIFY SignalLegendPositionChanged); Q_PROPERTY(QVariant m_ShowLegend READ GetShowLegend WRITE SetShowLegend NOTIFY SignalShowLegendChanged); Q_PROPERTY(QVariant m_ShowErrorBars READ GetShowErrorBars WRITE SetShowErrorBars NOTIFY SignalShowErrorBarsChanged); Q_PROPERTY(QVariant m_YAxisScale READ GetYAxisScale WRITE SetYAxisScale NOTIFY SignalYAxisScaleChanged); Q_PROPERTY(QVariant m_ShowSubchart READ GetShowSubchart WRITE SetShowSubchart NOTIFY SignalShowSubchartChanged); Q_PROPERTY(QVariant m_UsePercentageInPieChart READ GetUsePercentageInPieChart WRITE SetUsePercentageInPieChart NOTIFY SignalUsePercentageInPieChartChanged); Q_PROPERTY(QVariant m_DataPointSize READ GetDataPointSize WRITE SetDataPointSize NOTIFY SignalDataPointSizeChanged); Q_PROPERTY(QVariant m_StackedData READ GetStackedData WRITE SetStackedData NOTIFY SignalStackedDataChanged); - + Q_PROPERTY(QVariant m_UseMinMaxValues READ GetUseMinMaxValues WRITE UseMinMaxValues MOTIFY SignalUseMinMaxValues); public: QmitkChartData(); void SetAppearance(bool showSubChart = true, bool usePercentageInPieChart = false); Q_INVOKABLE QVariant GetXAxisLabel() const { return m_xAxisLabel; }; Q_INVOKABLE void SetXAxisLabel(const QVariant& label) { m_xAxisLabel = label; emit SignalXAxisLabelChanged(label); }; Q_INVOKABLE QVariant GetYAxisLabel() const { return m_yAxisLabel; }; Q_INVOKABLE void SetYAxisLabel(const QVariant& label) { m_yAxisLabel = label; emit SignalYAxisLabelChanged(label); }; Q_INVOKABLE QVariant GetTitle() const { return m_chartTitle; }; Q_INVOKABLE void SetTitle(const QVariant& title) { m_chartTitle = title; emit SignalTitleChanged(title); }; Q_INVOKABLE QVariant GetThemeName() const { return m_themeName; }; Q_INVOKABLE void SetThemeName(const QVariant &themeName) { m_themeName = themeName; emit SignalThemeNameChanged(themeName); }; Q_INVOKABLE QVariant GetLegendPosition() const { return m_LegendPosition; }; Q_INVOKABLE void SetLegendPosition(const QVariant& legendPosition) { m_LegendPosition = legendPosition; emit SignalLegendPositionChanged(legendPosition); }; Q_INVOKABLE QVariant GetShowLegend() const { return m_ShowLegend; }; Q_INVOKABLE void SetShowLegend(const QVariant& show) { m_ShowLegend = show; emit SignalShowLegendChanged(show); }; Q_INVOKABLE QVariant GetShowErrorBars() const { return m_ShowErrorBars; }; Q_INVOKABLE void SetShowErrorBars(const QVariant &show) { m_ShowErrorBars = show; emit SignalShowErrorBarsChanged(show); }; Q_INVOKABLE QVariant GetYAxisScale() const { return m_YAxisScale; }; Q_INVOKABLE void SetYAxisScale(const QVariant& YAxisScale) { m_YAxisScale = YAxisScale; emit SignalYAxisScaleChanged(YAxisScale); }; Q_INVOKABLE QVariant GetShowSubchart() const { return m_ShowSubchart; }; Q_INVOKABLE void SetShowSubchart(const QVariant& showSubchart) { m_ShowSubchart = showSubchart; emit SignalShowSubchartChanged(showSubchart); }; Q_INVOKABLE QVariant GetUsePercentageInPieChart() const { return m_UsePercentageInPieChart; }; Q_INVOKABLE void SetUsePercentageInPieChart(const QVariant& usePercentageInPieChart) { m_UsePercentageInPieChart = usePercentageInPieChart; emit SignalUsePercentageInPieChartChanged(usePercentageInPieChart); }; Q_INVOKABLE QVariant GetDataPointSize() const { return m_DataPointSize; }; Q_INVOKABLE void SetDataPointSize(const QVariant& showDataPoints) { m_DataPointSize = showDataPoints; emit SignalDataPointSizeChanged(showDataPoints); }; Q_INVOKABLE QVariant GetStackedData() const { return m_StackedData; }; Q_INVOKABLE void SetStackedData(const QVariant& stackedData) { m_StackedData = stackedData; emit SignalStackedDataChanged(m_StackedData); }; + Q_INVOKABLE QVariant GetUseMinMaxValues() const { return m_UseMinMaxValues; }; + Q_INVOKABLE void SetUseMinMaxValues(const QVariant& useMinMaxValues) { m_UseMinMaxValues = useMinMaxValues; emit SignalUseMinMaxValues(useMinMaxValues); }; signals: void SignalYAxisLabelChanged(const QVariant label); void SignalXAxisLabelChanged(const QVariant label); void SignalLegendPositionChanged(const QVariant legendPosition); void SignalShowLegendChanged(const QVariant show); void SignalShowErrorBarsChanged(const QVariant show); void SignalYAxisScaleChanged(const QVariant YAxisScale); void SignalTitleChanged(const QVariant title); void SignalThemeNameChanged(const QVariant themeName); void SignalShowSubchartChanged(const QVariant showSubchart); void SignalUsePercentageInPieChartChanged(const QVariant usePercentageInPieChart); void SignalDataPointSizeChanged(const QVariant showDataPoints); void SignalStackedDataChanged(const QVariant stackedData); + void SignalUseMinMaxValues(const QVariant useMinMaxValues); private: QVariant m_xAxisLabel; QVariant m_yAxisLabel; QVariant m_chartTitle; QVariant m_themeName = "dark"; QVariant m_ShowLegend = true; QVariant m_ShowErrorBars; QVariant m_LegendPosition = "topRight"; QVariant m_ShowSubchart; QVariant m_YAxisScale; QVariant m_UsePercentageInPieChart; QVariant m_numberDatasets; QVariant m_DataPointSize = 0; QVariant m_StackedData; + QVariant m_UseMinMaxValues; }; #endif //QmitkC3Data_h diff --git a/Modules/Chart/resource/Chart.js b/Modules/Chart/resource/Chart.js index 4bb5df80b6..bead0ae8b4 100644 --- a/Modules/Chart/resource/Chart.js +++ b/Modules/Chart/resource/Chart.js @@ -1,378 +1,386 @@ document.body.style.backgroundColor = 'rgb(240, 240, 240)'; const minHeight = 255; var chart; var chartData; var xErrorValuesPlus=[]; var xErrorValuesMinus=[]; var yErrorValuesPlus=[]; var yErrorValuesMinus=[]; var xValues=[]; var yValues=[]; var dataLabels=[]; var xs = {}; var dataColors = {}; var chartTypes = {}; var lineStyle = {}; var backgroundColor = '#f0f0f0'; var foregroundColor = 'black'; var dataProperties = {}; // Important loading function. This will be executed at first in this whole script. // Fetching data from QWebChannel and storing them for display purposes. window.onload = function() { initHeight(); new QWebChannel(qt.webChannelTransport, function(channel) { chartData = channel.objects.chartData; let count = 0; for(let propertyName in channel.objects) { if (propertyName != 'chartData') { let xDataTemp = channel.objects[propertyName].m_XData; let yDataTemp = channel.objects[propertyName].m_YData; let xErrorsTempPlus = channel.objects[propertyName].m_XErrorDataPlus; let xErrorsTempMinus = channel.objects[propertyName].m_XErrorDataMinus; let yErrorsTempPlus = channel.objects[propertyName].m_YErrorDataPlus; let yErrorsTempMinus = channel.objects[propertyName].m_YErrorDataMinus; let dataLabel = channel.objects[propertyName].m_Label; dataLabels.push(dataLabel); console.log("loading datalabel: "+dataLabel); //add label to x array xDataTemp.unshift('x'+count.toString()) xs[dataLabel] = 'x' + count.toString() xDataTemp.push(null); //append null value, to make sure the last tick on x-axis is displayed correctly yDataTemp.unshift(dataLabel) yDataTemp.push(null); //append null value, to make sure the last tick on y-axis is displayed correctly xValues[count] = xDataTemp yValues[count] = yDataTemp xErrorValuesPlus[count] = xErrorsTempPlus; xErrorValuesMinus[count] = xErrorsTempMinus; yErrorValuesPlus[count] = yErrorsTempPlus; yErrorValuesMinus[count] = yErrorsTempMinus; var tempLineStyle = ''; if (channel.objects[propertyName].m_LineStyleName == "solid") { tempLineStyle = '' } else { tempLineStyle = "dashed" } dataProperties[dataLabel] = { "color" : channel.objects[propertyName].m_Color, "chartType": channel.objects[propertyName].m_ChartType, "style": tempLineStyle } count++; } } var theme = chartData.m_themeName; setThemeColors(theme); generateChart(chartData); }); } /** * Inits the height of the chart element to 90% of the full window height. */ function initHeight() { var size = window.innerHeight-(window.innerHeight/100*10); //subtract 10% of height to hide vertical scrool bar let chart = document.getElementById("chart"); chart.style.height = `${size}px`; } function getPlotlyChartType(inputType){ let plotlyType = inputType; if (inputType == "line"){ plotlyType = "scatter"; } else if (inputType == "scatter"){ plotlyType = "scatterOnly" } return plotlyType; } /** * Generate error bars object * * @param {array} errors - contains error bar values * @return error bar object */ function generateErrorBars(errors, visible){ let errorObject = { type: 'data', array: errors, visible: visible } return errorObject; } function generateErrorBarsAsymmetric(errorsPlus, errorsMinus, visible){ let errorObject = generateErrorBars(errorsPlus, visible); errorObject["arrayminus"] = errorsMinus; errorObject["symmetric"] = false; return errorObject; } function generateStackPlotData(){ let data = []; for (let index = 0; index < dataLabels.length; index++){ let trace = { x: xValues[index].slice(1), y: yValues[index].slice(1), stackgroup: 'one', name: dataLabels[index] }; data.push(trace); } return data; } function generatePlotData(){ let data = []; for (let index = 0; index < dataLabels.length; index++){ let inputType = dataProperties[dataLabels[index]]["chartType"]; let chartType = getPlotlyChartType(inputType); let trace = { x: xValues[index].slice(1), y: yValues[index].slice(1), type: chartType, name: dataLabels[index] }; if(typeof xErrorValuesPlus[index] !== 'undefined'){ if(typeof xErrorValuesMinus[index] !== 'undefined' && xErrorValuesMinus[index].length > 0) { trace["error_x"] = generateErrorBarsAsymmetric(xErrorValuesPlus[index], xErrorValuesMinus[index], chartData.m_ShowErrorBars); }else{ trace["error_x"] = generateErrorBars(xErrorValuesPlus[index], chartData.m_ShowErrorBars); } } if(typeof yErrorValuesPlus[index] !== 'undefined'){ if(typeof yErrorValuesMinus[index] !== 'undefined' && yErrorValuesMinus[index].length > 0) { trace["error_y"] = generateErrorBarsAsymmetric(yErrorValuesPlus[index], yErrorValuesMinus[index], chartData.m_ShowErrorBars); }else{ trace["error_y"] = generateErrorBars(yErrorValuesPlus[index], chartData.m_ShowErrorBars); } } // ===================== CHART TYPE OPTIONS HANDLING =========== // initialize line object trace["line"] = {} if (chartType == "scatter"){ trace["line"]["color"] = dataProperties[dataLabels[index]]["color"] } else if (chartType == "area"){ trace["fill"] = 'tozeroy' } else if (chartType == "spline"){ trace["line"]["shape"] = 'spline' } else if (chartType == "scatterOnly"){ trace["mode"] = 'markers'; } else if (chartType == "area-spline"){ trace["fill"] = 'tozeroy' trace["line"]["shape"] = 'spline' } // handle marker visibility/size trace["marker"] = {size: chartData.m_DataPointSize} if (chartData.m_DataPointSize == 0){ trace["mode"] = "lines"; } if (dataProperties[dataLabels[index]]["style"] == "dashed"){ trace["line"]["dash"] = "dot" } data.push(trace) } return data; } /** * Here, the chart magic takes place. Plot.ly is called. * * @param {object} chartData - containing the options for plotting, not the actual values */ function generateChart(chartData) { console.log("generate chart"); if (chartData == undefined) { chartData = {} } if (dataLabels == undefined) { dataLabels = [] } //=============================== DATA ======================== var data = []; if (chartData.m_StackedData){ data = generateStackPlotData(); } else { data = generatePlotData(); } //=============================== STYLE ======================== let marginTop = chartData.m_chartTitle == undefined ? 10 : 50; if (chartData.m_LegendPosition == "bottomMiddle"){ var legendX = 0.5; var legendY = -0.75; } else if (chartData.m_LegendPosition == "bottomRight"){ var legendX = 1; var legendY = 0; } else if (chartData.m_LegendPosition == "topRight"){ var legendX = 1; var legendY = 1; } else if (chartData.m_LegendPosition == "topLeft"){ var legendX = 0; var legendY = 1; } else if (chartData.m_LegendPosition == "middleRight"){ var legendX = 1; var legendY = 0.5; } var layout = { paper_bgcolor : backgroundColor, plot_bgcolor : backgroundColor, title: { text:chartData.m_chartTitle, font: { color: foregroundColor } }, xaxis: { title: { text: chartData.m_xAxisLabel }, color: foregroundColor }, yaxis: { title: { text:chartData.m_yAxisLabel }, color: foregroundColor }, margin: { l: 50, r: 10, b: 50, t: marginTop, pad: 4 }, showlegend: chartData.m_ShowLegend, legend: { x: legendX, y: legendY, font : { color: foregroundColor } } }; if (chartData.m_YAxisScale){ layout.yaxis["type"] = "log" } if (chartData.m_ShowSubchart){ layout.xaxis.rangeslider = {}; // adds range slider below x axis } + /* + if (chartData.m_UseMinMaxValues){ + layout ={ + xaxis:{ + range:[,] + } + }; + }*/ Plotly.newPlot('chart', data, layout, {displayModeBar: false, responsive: true}); } /** * Change theme of chart. * * @param {string} color - dark or not dark */ function changeTheme(color) { setThemeColors(color); link = document.getElementsByTagName("link")[0]; if (color == 'dark') { link.href = "Chart_dark.css"; } else { link.href = "Chart.css"; } }; /** * Reload the chart with the given arguments. * * This method is called by C++. Changes on signature with caution. */ function Reload(){ console.log("Reload chart"); generateChart(chartData); } function SetShowSubchart(showSubchart) { chartData.m_ShowSubchart = showSubchart; } function setThemeColors(theme){ if (theme == 'dark'){ backgroundColor = '#2d2d30'; foregroundColor = 'white'; } else { backgroundColor = '#f0f0f0'; foregroundColor = 'black'; } } function SetStackDataString(stackDataString) { chartData.m_StackedData = stackDataString; } function SetShowErrorBars(showErrorBars) { chartData.m_ShowErrorBars = showErrorBars; } /** * Transforms the view to another chart type. * * This method is called by C++. Changes on signature with caution. * @param {string} transformTo - 'line' or 'bar' */ function transformView(transformTo) { console.log("transform view"); console.log(transformTo); dataProperties[dataLabels[0]]["chartType"] = transformTo; // preserve chartType for later updates let plotlyType = getPlotlyChartType(transformTo); let chart = document.getElementById("chart"); let update = {type : plotlyType} Plotly.restyle(chart, update, 0); // updates the given plotly trace at index 0 with an update object built of a standard trace object }; diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.ui.autosave b/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.ui.autosave new file mode 100644 index 0000000000..d2e46b343a --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.ui.autosave @@ -0,0 +1,238 @@ + + + QmitkHistogramVisualizationControls + + + + 0 + 0 + 540 + 394 + + + + Form + + + + + + false + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 0 + + + + Default + + + + + + Show Subchart + + + true + + + + + + + Use default #bins + + + true + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 60 + 0 + + + + + 100 + 16777215 + + + + # bins: + + + + + + + false + + + Press enter to recalculate statistics with new bin size. + + + 1 + + + 10000 + + + 100 + + + + + + + + + + + Overwrite + + + + + + Overwrite min/max values + + + + + + + + + + Minimum value + + + + + + + + + + Maximum value + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Copy to Clipboard + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + QmitkChartWidget + QWidget +
QmitkChartWidget.h
+
+
+ + +