diff --git a/Modules/C3js/resource/C3js.qrc b/Modules/C3js/resource/C3js.qrc index ef0618eb41..b968c60d12 100644 --- a/Modules/C3js/resource/C3js.qrc +++ b/Modules/C3js/resource/C3js.qrc @@ -1,13 +1,14 @@ c3.min.css Histogram.css Histogram_dark.css c3.min.js d3.min.js empty.html + empty_dark.html QmitkC3jsWidget.html Histogram.js diff --git a/Modules/C3js/resource/Histogram.css b/Modules/C3js/resource/Histogram.css index 9cd7975c64..ae37a3ae09 100644 --- a/Modules/C3js/resource/Histogram.css +++ b/Modules/C3js/resource/Histogram.css @@ -1,29 +1,28 @@ -/*--body { - background-color: darkgrey !important; -}--*/ - +body { + background-color: rgb(240, 240, 240) !important; +} /*-- Bar --*/ .c3-bar { fill-opacity: 1; } .c3-bar._expanded_ { fill: red !important; /*-- The !important tag prevnts the color from being overwritten at rendering--*/ fill-opacity: 1; } /*-- Line --*/ .c3-line { stroke-width: 1px; /*stroke-dasharray: 5,5; /*to make it dashed*/ } /*-- Point --*/ .c3-circle._expanded_ { stroke-width: 1px; stroke: red !important; fill: red !important; /*-- The !important tag prevnts the color from being overwritten at rendering--*/} /*path.domain { stroke: white; } .tick text { stroke: white; } .c3-legend-item text { stroke: grey; }*/ \ No newline at end of file diff --git a/Modules/C3js/resource/Histogram.js b/Modules/C3js/resource/Histogram.js index c34e9730c8..beb467a345 100644 --- a/Modules/C3js/resource/Histogram.js +++ b/Modules/C3js/resource/Histogram.js @@ -1,343 +1,341 @@ -document.body.style.backgroundColor = 'rgb(240, 240, 240)'; - var greyvalue; //needed to display the tooltip in the right format var binSize = 10; var min; var max; var minHeight = 255; var chart = c3.generate({ data: { x : 'x', //use first "column" as x axis values columns: [], //initialize empty. Data will be loaded in function setupChart(initValues) type: 'bar', selection: { enabled: false, multiple: false, } }, legend: { position: 'inset' }, grid: { y: { lines: [{value:0}] //Draws a horizontal line at y=0 } }, bar: { width: { ratio: 0.95 // this makes bar width 95% of length between ticks } }, zoom: { enabled: true, }, subchart: { show: true //Shows a subchart that shows the region the primary chart is zoomed in to by overlay. }, axis: { x: { type: 'category', //only for type 'category' the bars will be rescaled in width on zoom tick: { multiline: false, fit: false, //to make more x labels appear on zoom centered: true, }, }, y: { tick: { format: d3.format("s"), }, //for some reason, there is an offset for our linegraph. This is prevented by the following lines min: 0, padding: { top: 0, bottom: 0 } } }, //Style data points in linegraph point: { r: 0.2, focus: { expand: { r: 4 } } }, tooltip: { format: { title: function (d) { var endValue = (parseFloat(greyvalue[d]) + binSize); endValue = endValue.toFixed(3); return 'Greyvalue: ' + greyvalue[d] + '...' + endValue; }, } } }); var initValues; window.onload = function() { new QWebChannel(qt.webChannelTransport, function(channel) { initValues = channel.objects.initValues; setupChart(initValues) }); } //This is necessary to resize the chart, after the size of the parent changed window.onresize = function () { var size = window.innerHeight-(window.innerHeight/100*10); //subtract 5% of height to hide vertical scrool bar if (size < minHeight) { size = minHeight; } chart.resize({ height: size, }); } function ReloadChart(useLineChart, showSubchart) { initValues.m_UseLineChart = useLineChart; initValues.m_ShowSubchart = showSubchart; var chartType = 'bar'; if (initValues.m_UseLineChart) { chartType = 'line'; } if (initValues.m_ShowSubchart) { ShowSubchart(chartType) } else { HideSubchart(chartType) } setupChart(initValues); } function setupChart(initValues) { window.onresize(); calcBinSize(initValues); //copy measurements to xValues for x-axis-labels and to greyvalues for tooltips var xValues = initValues.m_XData.slice(0); greyvalue = initValues.m_XData.slice(0); for (var i = 0; i < xValues.length; i++) { greyvalue[i] = greyvalue[i] - (binSize / 2); greyvalue[i] = greyvalue[i].toFixed(3); //change number format for x axis. Need to do it here, because it is not working on chart generation. xValues[i] = xValues[i]; xValues[i] = xValues[i].toFixed(); xValues[i] = d3.format("s")(xValues[i]); } xValues.unshift('x'); //add label to x array xValues.push(null); //append null value, to make sure the last tick on x-axis is displayed correctly var yValues = initValues.m_YData; yValues.unshift('Frequency'); //add label to y array xValues.push(null); //append null value, to make sure the last tick on x-axis is displayed correctly var chartType = 'bar'; if (initValues.m_UseLineChart) { chartType = 'line'; } if (initValues.m_ShowSubchart) { ShowSubchart(chartType) } else { HideSubchart(chartType) } chart.unload(); //unload data before loading new data chart.load({ columns:[ xValues, yValues ] }); } /* * Calculation of the bin size. */ function calcBinSize(initValues) { if (1 < initValues.m_XData.length) { min = d3.min(initValues.m_XData); max = d3.max(initValues.m_XData); binSize = ((max - min) / (initValues.m_XData.length - 1)); } else { binSize = 10; } } //Transforamtion between bar and line chart //takes the name of the chart type as a parameter //calles by QmitkC3jsWidget function transformView(TransformTo) { chart.transform(TransformTo); }; function changeTheme(color) { if (color == 'dark') { link = document.getElementsByTagName("link")[0]; link.href = "Histogram_dark.css"; } else { link = document.getElementsByTagName("link")[0]; link.href = "Histogram.css"; } }; function ShowSubchart(chartType) { chart = c3.generate({ data: { x : 'x', //use first "column" as x axis values columns: [], //initialize empty. Data will be loaded in function setupChart(initValues) type: chartType, selection: { enabled: false, multiple: false, } }, legend: { position: 'inset' }, grid: { y: { lines: [{value:0}] //Draws a horizontal line at y=0 } }, bar: { width: { ratio: 0.95 // this makes bar width 95% of length between ticks } }, zoom: { enabled: true, }, subchart: { show: true //Shows a subchart that shows the region the primary chart is zoomed in to by overlay. }, axis: { x: { type: 'category', //only for type 'category' the bars will be rescaled in width on zoom tick: { multiline: false, fit: false, //to make more x labels appear on zoom centered: true, }, }, y: { tick: { format: d3.format("s"), }, //for some reason, there is an offset for our linegraph. This is prevented by the following lines //min: 0, //padding: { top: 0, bottom: 0 } } }, //Style data points in linegraph point: { r: 0.2, focus: { expand: { r: 4 } } }, tooltip: { format: { title: function (d) { var endValue = (parseFloat(greyvalue[d]) + binSize); endValue = endValue.toFixed(3); return 'Greyvalue: ' + greyvalue[d] + '...' + endValue; }, } } }); } function HideSubchart(chartType) { chart = c3.generate({ data: { x : 'x', //use first "column" as x axis values columns: [], //initialize empty. Data will be loaded in function setupChart(initValues) type: chartType, selection: { enabled: false, multiple: false, } }, legend: { position: 'inset' }, grid: { y: { lines: [{value:0}] //Draws a horizontal line at y=0 } }, bar: { width: { ratio: 0.95 // this makes bar width 95% of length between ticks } }, zoom: { enabled: true, }, subchart: { show: false //Shows a subchart that shows the region the primary chart is zoomed in to by overlay. }, axis: { x: { type: 'category', //only for type 'category' the bars will be rescaled in width on zoom tick: { multiline: false, fit: false, //to make more x labels appear on zoom centered: true, }, }, y: { tick: { format: d3.format("s"), }, //for some reason, there is an offset for our linegraph. This is prevented by the following lines //min: 0, //padding: { top: 0, bottom: 0 } } }, //Style data points in linegraph point: { r: 0.2, focus: { expand: { r: 4 } } }, tooltip: { format: { title: function (d) { var endValue = (parseFloat(greyvalue[d]) + binSize); endValue = endValue.toFixed(3); return 'Greyvalue: ' + greyvalue[d] + '...' + endValue; }, } } }); } \ No newline at end of file diff --git a/Modules/C3js/resource/Histogram_dark.css b/Modules/C3js/resource/Histogram_dark.css index a69610d1b1..9e09ba3384 100644 --- a/Modules/C3js/resource/Histogram_dark.css +++ b/Modules/C3js/resource/Histogram_dark.css @@ -1,36 +1,36 @@ body { - background-color: darkgrey !important; + background-color: #323231 !important; } /*-- Bar --*/ .c3-bar { fill-opacity: 1; } .c3-bar._expanded_ { fill: red !important; /*-- The !important tag prevents the color from being overwritten at rendering--*/ fill-opacity: 1; } /*-- Line --*/ .c3-line { stroke-width: 1px; - stroke: white; + stroke: #ADB1B6; /*stroke-dasharray: 5,5; /*to make it dashed*/ } /*-- Point --*/ .c3-circle._expanded_ { stroke-width: 1px; stroke: red !important; fill: red !important; /*-- The !important tag prevents the color from being overwritten at rendering--*/} -path.domain { stroke: white !important; } +path.domain { stroke: #ADB1B6 !important; } .c3 .c3-axis-x path, .c3 .c3-axis-x line { - stroke: white !important; + stroke: #ADB1B6 !important; } .c3 .c3-axis-y path, .c3 .c3-axis-y line { - stroke: white !important; + stroke: #ADB1B6 !important; } -.tick text { stroke: white !important; } -.c3-legend-item text { stroke: white !important; } \ No newline at end of file +.tick text { stroke: #ADB1B6 !important; } +.c3-legend-item text { stroke: #323231 !important; } \ No newline at end of file diff --git a/Modules/C3js/resource/empty.html b/Modules/C3js/resource/empty.html index 0051579c81..31ee449e24 100644 --- a/Modules/C3js/resource/empty.html +++ b/Modules/C3js/resource/empty.html @@ -1,11 +1,10 @@ - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/Modules/C3js/resource/empty_dark.html b/Modules/C3js/resource/empty_dark.html new file mode 100644 index 0000000000..4146f9e1f7 --- /dev/null +++ b/Modules/C3js/resource/empty_dark.html @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/Modules/C3js/src/QmitkC3jsWidget.cpp b/Modules/C3js/src/QmitkC3jsWidget.cpp index 1bee6b8939..ba13e9c3aa 100644 --- a/Modules/C3js/src/QmitkC3jsWidget.cpp +++ b/Modules/C3js/src/QmitkC3jsWidget.cpp @@ -1,285 +1,285 @@ /*=================================================================== 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 "mitkImageTimeSelector.h" class QmitkC3jsWidget::Impl final { public: explicit Impl(QWidget* parent); ~Impl(); Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; QWebChannel* GetWebChannel(); void ClearJavaScriptChart(); void initializeJavaScriptChart(); void callJavaScriptFuntion(QString command); QmitkC3Data* GetC3Data() { return &m_c3Data; }; mitk::Image::Pointer GetImage() { return m_Image; }; const void SetImage(const mitk::Image::Pointer image) { m_Image = image; }; mitk::PlanarFigure::ConstPointer GetPlanarFigure() { return m_PlanarFigure; }; const void SetPlanarFigure(const mitk::PlanarFigure::ConstPointer planarFigure) { m_PlanarFigure = planarFigure; }; private: QWidget* m_Parent; QWebChannel* m_WebChannel; QWebEngineView* m_WebEngineView; QmitkC3Data m_c3Data; /** * \brief Reference image. * * Holds the image to calculate an intensity profile. */ mitk::Image::Pointer m_Image; /** * \brief Pathelement. * * Holds a not closed planar figure to calculate an intensity profile. */ mitk::PlanarFigure::ConstPointer m_PlanarFigure; }; QmitkC3jsWidget::Impl::Impl(QWidget* parent) : m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent)), m_Parent(parent) { //disable context menu for QWebEngineView m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu); //Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated. m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); m_WebEngineView->page()->setWebChannel(m_WebChannel); connect( m_WebEngineView, SIGNAL( loadFinished(bool) ), parent, SLOT( OnLoadFinished(bool) ) ); auto layout = new QGridLayout(parent); layout->setMargin(0); layout->addWidget(m_WebEngineView); parent->setLayout(layout); } QmitkC3jsWidget::Impl::~Impl() { } QWebChannel* QmitkC3jsWidget::Impl::GetWebChannel() { return m_WebChannel; } QmitkC3jsWidget::QmitkC3jsWidget(QWidget* parent) : QWidget(parent), m_Impl(new Impl(this)) { m_Statistics = mitk::ImageStatisticsCalculator::StatisticsContainer::New(); } void QmitkC3jsWidget::Impl::callJavaScriptFuntion(QString command) { m_WebEngineView->page()->runJavaScript(command); } void QmitkC3jsWidget::Impl::ClearJavaScriptChart() { m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); } void QmitkC3jsWidget::Impl::initializeJavaScriptChart() { m_WebChannel->registerObject(QStringLiteral("initValues"), &m_c3Data); - m_WebEngineView->load(QUrl(QStringLiteral("qrc:///C3js/QmitkC3jsWidget.html"))); + m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/QmitkC3jsWidget.html"))); } QmitkC3jsWidget::QmitkC3jsWidget(const QString& id, QObject* object, QWidget* parent) : QWidget(parent), m_Impl(new Impl(this)) { if (!id.isEmpty() && object != nullptr) m_Impl->GetWebChannel()->registerObject(id, object); m_Statistics = mitk::ImageStatisticsCalculator::StatisticsContainer::New(); } QmitkC3jsWidget::~QmitkC3jsWidget() { delete m_Impl; } void QmitkC3jsWidget::OnLoadFinished(bool isLoadSuccessfull) { emit PageSuccessfullyLoaded(); } void QmitkC3jsWidget::TransformView(QString transformTo) { QString command = QString("transformView('" + transformTo + "')"); m_Impl->callJavaScriptFuntion(command); } void QmitkC3jsWidget::SendCommand(QString command) { m_Impl->callJavaScriptFuntion(command); } void QmitkC3jsWidget::SetAppearance(bool useLineChart, bool showSubChart) { this->m_Impl->GetC3Data()->SetAppearance(useLineChart, showSubChart); } // method to expose data to JavaScript by using properties void QmitkC3jsWidget::ComputeHistogram(HistogramType* histogram, bool useLineChart, bool showSubChart) { this->m_Impl->GetC3Data()->SetHistogram(histogram); SetAppearance(useLineChart, showSubChart); HistogramConstIteratorType startIt = this->m_Impl->GetC3Data()->GetHistogram()->End(); HistogramConstIteratorType endIt = this->m_Impl->GetC3Data()->GetHistogram()->End(); HistogramConstIteratorType it = this->m_Impl->GetC3Data()->GetHistogram()->Begin(); //Clear old data befor loading new data. this->m_Impl->GetC3Data()->ClearData(); unsigned int i = 0; bool firstValue = false; // removes frequencies of 0, which are outside the first and last bin for (; it != this->m_Impl->GetC3Data()->GetHistogram()->End(); ++it) { if (it.GetFrequency() > 0.0) { endIt = it; if (!firstValue) { firstValue = true; startIt = it; } } } ++endIt; // generating Lists of measurement and frequencies for (it = startIt; it != endIt; ++it, ++i) { QVariant frequency = QVariant::fromValue(it.GetFrequency()); QVariant measurement = it.GetMeasurementVector()[0]; this->m_Impl->GetC3Data()->GetYDataPointer()->insert(i, frequency); this->m_Impl->GetC3Data()->GetXDataPointer()->insert(i, measurement); } m_Impl->initializeJavaScriptChart(); } void QmitkC3jsWidget::ComputeIntensityProfile(unsigned int timeStep, bool computeStatistics) { this->ClearHistogram(); //m_Impl->GetC3Data()->ClearData(); //m_ParametricPath->Initialize(); if (m_Impl->GetPlanarFigure().IsNull()) { mitkThrow() << "PlanarFigure not set!"; } if (m_Impl->GetImage().IsNull()) { mitkThrow() << "Image not set!"; } mitk::Image::Pointer image; if (m_Impl->GetImage()->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Impl->GetImage()); timeSelector->SetTimeNr(timeStep); timeSelector->Update(); image = timeSelector->GetOutput(); } else { image = m_Impl->GetImage(); } mitk::IntensityProfile::Pointer intensityProfile = mitk::ComputeIntensityProfile( image, const_cast(m_Impl->GetPlanarFigure().GetPointer())); //m_Frequency.clear(); //m_Measurement.clear(); int i = -1; mitk::IntensityProfile::ConstIterator end = intensityProfile->End(); for (mitk::IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { m_Impl->GetC3Data()->GetYDataPointer()->push_back(it.GetMeasurementVector()[0]); //m_Impl->GetC3Data()->GetFrequencyPointer()->push_back(50000); m_Impl->GetC3Data()->GetXDataPointer()->push_back(++i); } if (computeStatistics) { mitk::ComputeIntensityProfileStatistics(intensityProfile, m_Statistics); } m_Impl->initializeJavaScriptChart(); } void QmitkC3jsWidget::ClearHistogram() { m_Impl->GetC3Data()->ClearData(); m_Impl->ClearJavaScriptChart(); } mitk::Image::Pointer QmitkC3jsWidget::GetImage() const { return m_Impl->GetImage(); } void QmitkC3jsWidget::SetImage(const mitk::Image::Pointer image) { m_Impl->SetImage(image); } mitk::PlanarFigure::ConstPointer QmitkC3jsWidget::GetPlanarFigure() const { return m_Impl->GetPlanarFigure(); } void QmitkC3jsWidget::SetPlanarFigure(const mitk::PlanarFigure::ConstPointer planarFigure) { m_Impl->SetPlanarFigure(planarFigure); } \ No newline at end of file