Skip to content
Snippets Groups Projects
Commit afc203d9 authored by Daniel Savchenko's avatar Daniel Savchenko
Browse files

Added a Base Histogram class, that allows colorcoding for each bar.

parent 0d22695f
No related branches found
No related tags found
2 merge requests!265Mac fix,!264Mac fix
...@@ -93,6 +93,7 @@ set ( headers ...@@ -93,6 +93,7 @@ set ( headers
Math/QuaternionT.hh Math/QuaternionT.hh
Math/VectorT.hh Math/VectorT.hh
QtWidgets/QtApplication.hh QtWidgets/QtApplication.hh
QtWidgets/QtChartHistogram.hh
QtWidgets/QtClippingDialog.hh QtWidgets/QtClippingDialog.hh
QtWidgets/QtColorChooserButton.hh QtWidgets/QtColorChooserButton.hh
QtWidgets/QtColorTranslator.hh QtWidgets/QtColorTranslator.hh
...@@ -209,6 +210,7 @@ set (sources ...@@ -209,6 +210,7 @@ set (sources
GL/stipple_alpha.cc GL/stipple_alpha.cc
Math/BSplineBasis.cc Math/BSplineBasis.cc
QtWidgets/QtApplication.cc QtWidgets/QtApplication.cc
QtWidgets/QtChartHistogram.cc
QtWidgets/QtClippingDialog.cc QtWidgets/QtClippingDialog.cc
QtWidgets/QtColorChooserButton.cc QtWidgets/QtColorChooserButton.cc
QtWidgets/QtColorTranslator.cc QtWidgets/QtColorTranslator.cc
......
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (c) 2001-2024, RWTH-Aachen University *
* Department of Computer Graphics and Multimedia *
* All rights reserved. *
* www.openflipper.org *
* *
*---------------------------------------------------------------------------*
* This file is part of OpenFlipper. *
*---------------------------------------------------------------------------*
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* 1. Redistributions of source code must retain the above copyright notice, *
* this list of conditions and the following disclaimer. *
* *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* *
* 3. Neither the name of the copyright holder nor the names of its *
* contributors may be used to endorse or promote products derived from *
* this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
* *
\*===========================================================================*/
//=============================================================================
//
// CLASS QtChart - IMPLEMENTATION
//
//=============================================================================
//== INCLUDES =================================================================
#include "QtChartHistogram.hh"
#include <QChart>
#include <QValueAxis>
#include <QBarSeries>
#include <QLineSeries>
#include <QStackedBarSeries>
#include <QBarCategoryAxis>
#include <QBarSet>
#include <QChartView>
#include <QRubberBand>
#include <cfloat>
#include <Utils/ColorCoder.hh>
//== NAMESPACES ===============================================================
namespace ACG {
namespace QtWidgets {
//This class creates a Histogram in a QChartView. The Histogram can be zoomed in and out by using a rubberband.
//The x-axis values are updated when the rubberband is moved. The y-axis values are updated automatically.
//The Histogram can be color coded.
//!!!!!IMPORTANT!!!! This class allows for colored bars in the Histogram, which is not possible with QCharts alone.
//In order to achieve that we have to create a new QBarSet for each bar. And "stack" them on top of each other.
//This is not very efficient and should be avoided if possible. In addition to that, the x-axis is detached in order to allow
//a correct zoom behavior. (This is also very hacky and should be avoided but I didnt see any other way to make it work)
//However I think there is a bug in the QCharts library and as soon as it is fixed, the workaround is not needed anymore..
QtChartHistogram::QtChartHistogram(QString title_X, QString title_Y, const std::vector<double>& _values, QWidget* _parent) : QChartView(_parent)
{
//create chart
chart_ = new QChart();
//create x-axis
axisX = new QValueAxis();
axisX->setTitleText(title_X);
axisX->setRange(0, 100);
chart_->addAxis(axisX, Qt::AlignBottom);
// Create y-axis
axisY = new QValueAxis();
axisY->setTitleText(title_Y);
axisY->setRange(0, 500);
chart_->addAxis(axisY, Qt::AlignLeft);
// Set the chart to the chartView(this widget)
setChart(chart_);
setRubberBand(QChartView::RectangleRubberBand);
// Create the series and attach it to the chart
series = new QStackedBarSeries();
chart_->addSeries(series);
series->attachAxis(axisY);
// Set the values
setValues(_values);
// Get the rubberband from the chartView
rubberBand_ = findChild<QRubberBand *>();
rubberBand_->installEventFilter(this);
// Install eventfilter for the chartView, so we can update the cursor type if inside the chart
this->installEventFilter(this);
// Connect the rubberband signal to the slot
connect(this, &QtChartHistogram::rubberBandChanged, this, &QtChartHistogram::rubberBandChangedSlot);
// Set the render hint to antialiasing, so the chart looks nicer
setRenderHint(QPainter::Antialiasing);
// Hide the legend
chart_->legend()->setVisible(false);
replot();
}
//destructor
QtChartHistogram::~QtChartHistogram()
{}
//------------------------------------------------------------------------------
// Override the mouseReleaseEvent to emit a signal when the right mouse button is released
void QtChartHistogram::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
// There might be a more elegant way to reset the zoom, but this is simple and works well
replot();
}
else {
QChartView::mouseReleaseEvent(event);
}
}
//------------------------------------------------------------------------------
// Event filter
bool QtChartHistogram::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && event->type() == QEvent::Enter){
// if we are inside the chart, we wanna change the cursor to a cross
setCursor(Qt::CrossCursor);
}
else if (obj == chart_ && event->type() == QEvent::Leave){
// if we leave the chart, we wanna change the cursor back to the default
setCursor(Qt::ArrowCursor);
}
// if we use rubberband on the chartView, we wanna update the x-axis values (as the x-axis is detached from the series)
if (obj == rubberBand_ && (event->type() == QEvent::HideToParent)){
//transform the rubberband coordinates to the x-axis values
auto rubberminX = rubberBand_->geometry().x() - chart_->plotArea().x();
auto rubbermaxX = rubberBand_->geometry().x() - chart_->plotArea().x() + rubberBand_->geometry().width();
emit rubberBandChanged(rubberminX, rubbermaxX);
}
return false;
}
//------------------------------------------------------------------------------
void QtChartHistogram::rubberBandChangedSlot(double rubberminX, double rubbermaxX)
{
//get the x values of the chart
qreal minX = static_cast<const QValueAxis*>(chart_->axes(Qt::Horizontal).back())->min();
qreal maxX = static_cast<const QValueAxis*>(chart_->axes(Qt::Horizontal).back())->max();
//compute the new x-axis values
double newMin = minX + ((rubberminX)/ chart_->plotArea().width()) * (maxX - minX);
double newMax = minX + (rubbermaxX / chart_->plotArea().width()) * (maxX - minX);
//set the new x-axis values
dynamic_cast<QValueAxis*>(chart_->axes(Qt::Horizontal).back())->setRange(newMin, newMax);
}
//------------------------------------------------------------------------------
// Set the values
void QtChartHistogram::setValues(const std::vector<double>& _values)
{
values_ = _values;
}
//------------------------------------------------------------------------------
// Replot the chart
void QtChartHistogram::replot()
{
// Create intervals
const int intervalCount = 101;
std::vector<int> intervals(intervalCount, 0);
double realMin = FLT_MAX;
double realMax = -FLT_MAX;
// Compute realMin and realMax
for (const auto& value : values_) {
if (!std::isnan(value)) { // Check for NaN
realMin = std::min(realMin, value);
realMax = std::max(realMax, value);
}
}
// Check for the edge case where realMin equals realMax
// In this case we have only one value and we want to create some margins around the value
// In order to display the value correctly in the histogram
if (realMin == realMax) {
realMax = realMin + 50;
realMin = realMin - 50;
}
float width = (realMax - realMin) / intervalCount;
// Populate the intervals
for (const auto& value : values_) {
if (!std::isnan(value)) { // Check for NaN
int index = std::min(intervalCount - 1, static_cast<int>((value - realMin) / width));
intervals[index]++;
}
}
std::vector<QColor> colors;
// If we want to color code the histogram
if (colorCoding_){
// Create Colors for the intervals
ACG::ColorCoder cCoder(realMin,realMax);
for (int i = 0; i < intervalCount; i++) {
const double intervalCenter = realMin + (i + 0.5) * width;
colors.push_back(cCoder.color_qcolor(intervalCenter));
}
}
else {
// Use just one color
for (int i = 0; i < intervalCount; i++) {
const double intervalCenter = realMin + (i + 0.5) * width;
colors.push_back(Qt::black);
}
}
// Remove the old series and create a new one. For some reason you have to remove the series. Simply clearing the series and appending new values does not work.
chart_->removeSeries(series);
// Clear the series
series = new QStackedBarSeries();
// Set the axes range
axisX->setRange(realMin, realMax);
axisY->setRange(0, *std::max_element(intervals.begin(), intervals.end()));
// Create for each Bar a new set. This is needed to add different colors to each bar.
for (int i = 0; i < intervalCount; i++) {
QBarSet* set = new QBarSet("Values");
for (int j = 0; j < intervalCount; j++){
if (j == i){
*set << intervals[j];
set->setColor(colors[j]);
}
else{
*set << 0;
}
}
set->setColor(colors[i]);
series->append(set);
}
chart_->addSeries(series);
series->attachAxis(axisY);
}
void QtChartHistogram::setColorCoding(bool _colorCoding)
{
colorCoding_ = _colorCoding;
}
} // namespace QtWidgets
} // namespace ACG
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (c) 2001-2024, RWTH-Aachen University *
* Department of Computer Graphics and Multimedia *
* All rights reserved. *
* www.openflipper.org *
* *
*---------------------------------------------------------------------------*
* This file is part of OpenFlipper. *
*---------------------------------------------------------------------------*
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* 1. Redistributions of source code must retain the above copyright notice, *
* this list of conditions and the following disclaimer. *
* *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* *
* 3. Neither the name of the copyright holder nor the names of its *
* contributors may be used to endorse or promote products derived from *
* this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
* *
\*===========================================================================*/
//=============================================================================
//
// CLASS QtChart - IMPLEMENTATION
//
//=============================================================================
//== INCLUDES =================================================================
#include <QChart>
#include <QBarSeries>
#include <QLineSeries>
#include <QStackedBarSeries>
#include <QChartView>
#include <QRubberBand>
#include <cfloat>
#include <QValueAxis>
#include <QBarSet>
//== NAMESPACES ===============================================================
namespace ACG {
namespace QtWidgets {
//This class creates a Histogram in a QChartView. The Histogram can be zoomed in and out by using a rubberband.
//The x-axis values are updated when the rubberband is moved. The y-axis values are updated automatically.
//The Histogram can be color coded.
//!!!!!IMPORTANT!!!! This class allows for colored bars in the Histogram, which is not possible with QCharts alone.
//In order to achieve that we have to create a new QBarSet for each bar. And "stack" them on top of each other.
//This is not very efficient and should be avoided if possible. In addition to that, the x-axis is detached in order to allow
//a correct zoom behavior. (This is also very hacky and should be avoided but I didnt see any other way to make it work)
//However I think there is a bug in the QCharts library and as soon as it is fixed, the workaround is not needed anymore.
class QtChartHistogram : public QChartView {
Q_OBJECT
public:
/// Default constructor
explicit QtChartHistogram(QString title_X, QString title_Y, const std::vector<double>& _values, QWidget* _parent = 0);
/// Destructor
~QtChartHistogram();
void setColorCoding(bool _colorCoding);
void setValues(const std::vector<double>& _values);
void replot();
public slots:
void rubberBandChangedSlot(double rubber_min, double rubber_max);
signals:
void rubberBandChanged(double rubber_min, double rubber_max);
private:
//variables
QChart* chart_;
QValueAxis* axisX;
QValueAxis* axisY;
QStackedBarSeries* series;
QRubberBand* rubberBand_;
bool colorCoding_;
std::vector<double> values_;
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
};
} // namespace QtWidgets
} // namespace ACG
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment