#4158 Implement info box for grid cross plots and improve plot overlay legends

This commit is contained in:
Gaute Lindkvist 2019-03-25 14:47:55 +01:00
parent c47f2a30d8
commit aa32caae33
16 changed files with 351 additions and 38 deletions

View File

@ -48,6 +48,7 @@ RimGridCrossPlot::RimGridCrossPlot()
{
CAF_PDM_InitObject("Grid Cross Plot", ":/SummaryXPlotLight16x16.png", "", "");
CAF_PDM_InitField(&m_showInfoBox, "ShowInfoBox", true, "Show Info Box", "", "", "");
CAF_PDM_InitField(&m_showLegend, "ShowLegend", true, "Show Legend", "", "", "");
CAF_PDM_InitField(&m_legendFontSize, "LegendFontSize", 10, "Legend Font Size", "", "", "");
CAF_PDM_InitFieldNoDefault(&m_nameConfig, "NameConfig", "Name Config", "", "", "");
@ -94,13 +95,18 @@ RimGridCrossPlotCurveSet* RimGridCrossPlot::createCurveSet()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimGridCrossPlot::indexOfCurveSet(const RimGridCrossPlotCurveSet* curveSet) const
int RimGridCrossPlot::indexOfCurveSet(const RimGridCrossPlotCurveSet* curveSetToCheck) const
{
for (size_t i = 0; i < m_crossPlotCurveSets.size(); ++i)
int index = 0;
for (auto curveSet : m_crossPlotCurveSets())
{
if (curveSet == m_crossPlotCurveSets[i])
if (curveSet == curveSetToCheck)
{
return static_cast<int>(i);
return index;
}
if (curveSet->isChecked() && curveSet->visibleCurveCount() > 0u)
{
index++;
}
}
return -1;
@ -247,6 +253,14 @@ QString RimGridCrossPlot::createAutoName() const
return autoName.join(" ");
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimGridCrossPlot::showInfoBox() const
{
return m_showInfoBox();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -399,6 +413,7 @@ void RimGridCrossPlot::onLoadDataAndUpdate()
//--------------------------------------------------------------------------------------------------
void RimGridCrossPlot::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering)
{
uiOrdering.add(&m_showInfoBox);
uiOrdering.add(&m_showLegend);
if (m_showLegend())

View File

@ -66,6 +66,7 @@ public:
void reattachCurvesToQwtAndReplot();
QString createAutoName() const override;
bool showInfoBox() const;
caf::PdmFieldHandle* userDescriptionField() override;
void detachAllCurves();
void performAutoNameUpdate() override;
@ -112,6 +113,7 @@ protected:
RimGridCrossPlotNameConfig* nameConfig();
private:
caf::PdmField<bool> m_showInfoBox;
caf::PdmField<bool> m_showLegend;
caf::PdmField<int> m_legendFontSize;
caf::PdmChildField<RimGridCrossPlotNameConfig*> m_nameConfig;

View File

@ -92,6 +92,14 @@ int RimGridCrossPlotCurve::groupIndex() const
return m_groupIndex;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimGridCrossPlotCurve::sampleCount() const
{
return m_qwtPlotCurve->dataSize();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -43,9 +43,9 @@ public:
void setGroupingInformation(int curveSetIndex, int groupIndex);
void setSamples(const std::vector<double>& xValues, const std::vector<double>& yValues);
void setCurveAutoAppearance();
int groupIndex() const;
void setCurveAutoAppearance();
int groupIndex() const;
size_t sampleCount() const;
protected:
void determineSymbol();

View File

@ -198,6 +198,36 @@ QString RimGridCrossPlotCurveSet::yAxisName() const
return m_yAxisProperty->resultVariableUiShortName();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimGridCrossPlotCurveSet::infoText() const
{
if (!m_case()) return "";
if (visibleCurveCount() == 0u) return "";
QStringList textLines;
textLines += QString("<b>Case:</b> %1").arg(m_case()->caseUserDescription());
textLines += QString("<b>Parameters:</b>: %1 x %2")
.arg(m_xAxisProperty->resultVariableUiShortName())
.arg(m_yAxisProperty->resultVariableUiShortName());
if (m_timeStep != -1)
{
textLines += QString("<b>Time step:</b> %1").arg(timeStepString());
}
if (m_grouping != NO_GROUPING)
{
textLines += QString("<b>Grouped By:</b> %1").arg(groupParameter());
}
if (m_cellFilterView())
{
textLines += QString("<b>Filter view:</b> %1").arg(m_cellFilterView->name());
}
textLines += QString("<b>Sample Count:</b> %1").arg(sampleCount());
return textLines.join("<br/>\n");
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -606,6 +636,32 @@ void RimGridCrossPlotCurveSet::destroyCurves()
m_crossPlotCurves.deleteAllChildObjects();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimGridCrossPlotCurveSet::visibleCurveCount() const
{
size_t visibleCurves = 0;
for (auto curve : m_crossPlotCurves)
{
if (curve->isCurveVisible()) visibleCurves++;
}
return visibleCurves;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimGridCrossPlotCurveSet::sampleCount() const
{
size_t sampleCount = 0;
for (auto curve : m_crossPlotCurves)
{
if (curve->isCurveVisible()) sampleCount += curve->sampleCount();
}
return sampleCount;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -97,6 +97,7 @@ public:
void setParentQwtPlotNoReplot(QwtPlot* parent);
QString xAxisName() const;
QString yAxisName() const;
QString infoText() const;
int indexInPlot() const;
QString createAutoName() const override;
@ -131,6 +132,8 @@ public:
void setCustomColor(const cvf::Color3f color);
void destroyCurves();
size_t visibleCurveCount() const;
size_t sampleCount() const;
protected:
void initAfterRead() override;
void onLoadDataAndUpdate(bool updateParentPlot);

View File

@ -80,6 +80,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotItemGroup.h
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotTools.h
${CMAKE_CURRENT_LIST_DIR}/RiuWellPathComponentPlotItem.h
${CMAKE_CURRENT_LIST_DIR}/RiuMeasurementViewEventFilter.h
${CMAKE_CURRENT_LIST_DIR}/RiuDraggableOverlayFrame.h
)
set (SOURCE_GROUP_SOURCE_FILES
@ -159,6 +160,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RiuDockWidgetTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotItemGroup.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuWellPathComponentPlotItem.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuDraggableOverlayFrame.cpp
)
list(APPEND CODE_HEADER_FILES
@ -205,6 +207,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RiuMessagePanel.h
${CMAKE_CURRENT_LIST_DIR}/RiuExpressionContextMenuManager.h
${CMAKE_CURRENT_LIST_DIR}/RiuCalculationsContextMenuManager.h
${CMAKE_CURRENT_LIST_DIR}/RiuMohrsCirclePlot.h
${CMAKE_CURRENT_LIST_DIR}/RiuDraggableOverlayFrame.h
)
list(APPEND QT_UI_FILES

View File

@ -41,16 +41,11 @@
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuCvfOverlayItemWidget::RiuCvfOverlayItemWidget(QWidget* parent/*=0*/)
: QWidget(parent)
RiuCvfOverlayItemWidget::RiuCvfOverlayItemWidget(QWidget* parent/*=0*/, QWidget* widgetToSnapTo)
: RiuDraggableOverlayFrame(parent, widgetToSnapTo)
{
auto hblayout = new QHBoxLayout(this);
hblayout->setMargin(0);
hblayout->setSpacing(0);
this->setLayout(hblayout);
m_overlayItemLabel = new QLabel(this);
this->layout()->addWidget(m_overlayItemLabel);
this->layout()->setMargin(0);
this->layout()->setSpacing(0);
}
//--------------------------------------------------------------------------------------------------

View File

@ -18,6 +18,8 @@
#pragma once
#include "RiuDraggableOverlayFrame.h"
#include <QWidget>
class QLabel;
@ -31,18 +33,15 @@ namespace cvf
//
//
//==================================================================================================
class RiuCvfOverlayItemWidget : public QWidget
class RiuCvfOverlayItemWidget : public RiuDraggableOverlayFrame
{
Q_OBJECT
public:
explicit RiuCvfOverlayItemWidget(QWidget* parent = nullptr);
explicit RiuCvfOverlayItemWidget(QWidget* parent = nullptr, QWidget* widgetToSnapTo = nullptr);
~RiuCvfOverlayItemWidget() override;
void updateFromOverlayItem( cvf::OverlayItem * item);
// virtual QSize sizeHint() const override;
// virtual QSize minimumSizeHint() const override;
protected:
QLabel* m_overlayItemLabel;
};

View File

@ -0,0 +1,61 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2019- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RiuDraggableOverlayFrame.h"
#include "RiuWidgetDragger.h"
#include <QGraphicsDropShadowEffect>
#include <QLabel>
#include <QVBoxLayout>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuDraggableOverlayFrame::RiuDraggableOverlayFrame(QWidget* parent, QWidget* widgetToSnapTo, const QColor& backgroundColor)
: QFrame(parent)
{
RiuWidgetDragger* dragger = new RiuWidgetDragger(this, widgetToSnapTo);
QPalette pal = this->palette();
pal.setColor(QPalette::Background, backgroundColor);
setAutoFillBackground(true);
setPalette(pal);
setFrameShape(QFrame::Box);
QGraphicsDropShadowEffect* dropShadowEffect = new QGraphicsDropShadowEffect(this);
dropShadowEffect->setOffset(1.0, 1.0);
dropShadowEffect->setBlurRadius(3.0);
dropShadowEffect->setColor(QColor(100, 100, 100, 100));
setGraphicsEffect(dropShadowEffect);
auto hblayout = new QVBoxLayout(this);
this->setLayout(hblayout);
m_overlayItemLabel = new QLabel(this);
hblayout->addWidget(m_overlayItemLabel);
m_overlayItemLabel->setObjectName("OverlayFrameLabel");
m_overlayItemLabel->setGraphicsEffect(nullptr);
m_overlayItemLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
dragger->addWidget(m_overlayItemLabel);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QLabel* RiuDraggableOverlayFrame::label()
{
return m_overlayItemLabel;
}

View File

@ -0,0 +1,35 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2019- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <QFrame>
#include <QPointer>
class QColor;
class QLabel;
class RiuDraggableOverlayFrame : public QFrame
{
Q_OBJECT
public:
RiuDraggableOverlayFrame(QWidget* parent, QWidget* widgetToSnapTo = nullptr, const QColor& backgroundColor = QColor(255, 255, 255, 100));
QLabel* label();
protected:
QPointer<QLabel> m_overlayItemLabel;
};

View File

@ -32,8 +32,10 @@
#include "RimPlotAxisProperties.h"
#include "RiuPlotAnnotationTool.h"
#include <QLabel>
#include <QMenu>
#include <QResizeEvent>
#include <QVBoxLayout>
//--------------------------------------------------------------------------------------------------
///
@ -42,6 +44,7 @@ RiuGridCrossQwtPlot::RiuGridCrossQwtPlot(RimViewWindow* ownerViewWindow, QWidget
: RiuQwtPlot(ownerViewWindow, parent)
{
m_annotationTool = std::unique_ptr<RiuPlotAnnotationTool>(new RiuPlotAnnotationTool());
m_infoBox = new RiuDraggableOverlayFrame(this, canvas());
}
//--------------------------------------------------------------------------------------------------
@ -54,10 +57,7 @@ void RiuGridCrossQwtPlot::addOrUpdateCurveSetLegend(RimGridCrossPlotCurveSet* cu
auto it = m_legendWidgets.find(curveSet);
if (it == m_legendWidgets.end() || it->second == nullptr)
{
overlayWidget = new RiuCvfOverlayItemWidget(this);
new RiuWidgetDragger(overlayWidget);
overlayWidget = new RiuCvfOverlayItemWidget(this, canvas());
m_legendWidgets[curveSet] = overlayWidget;
}
else
@ -144,9 +144,66 @@ void RiuGridCrossQwtPlot::updateAnnotationObjects(RimPlotAxisProperties* axisPro
void RiuGridCrossQwtPlot::updateLayout()
{
QwtPlot::updateLayout();
updateInfoBoxLayout();
updateLegendLayout();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGridCrossQwtPlot::updateInfoBoxLayout()
{
RimGridCrossPlot* crossPlot = dynamic_cast<RimGridCrossPlot*>(ownerPlotDefinition());
if (!crossPlot) return;
bool showInfo = false;
if (crossPlot->showInfoBox())
{
QStringList curveInfoTexts;
for (auto curveSet : crossPlot->curveSets())
{
QString curveInfoText = curveSet->infoText();
if (curveSet->isChecked() && !curveInfoText.isEmpty())
{
curveInfoTexts += curveInfoText;
}
}
QStringList infoText;
if (curveInfoTexts.size() > 1)
{
infoText += QString("<ol style=\"margin-top: 0px; margin-left: 15px; -qt-list-indent:0;\">");
for (QString curveInfoText : curveInfoTexts)
{
infoText += QString("<li>%1</li>").arg(curveInfoText);
}
infoText += QString("</ol>");
}
else if (curveInfoTexts.size() > 0)
{
infoText += curveInfoTexts.front();
}
if (!infoText.empty())
{
m_infoBox->label()->setText(infoText.join("\n"));
m_infoBox->adjustSize();
QRect infoRect = m_infoBox->frameGeometry();
QRect canvasRect = canvas()->frameGeometry();
infoRect.moveTop(canvasRect.top() + 4);
infoRect.moveRight(canvasRect.right() - 4);
m_infoBox->move(infoRect.topLeft());
showInfo = true;
}
}
if (showInfo)
{
m_infoBox->show();
}
else
{
m_infoBox->hide();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -29,6 +29,7 @@
class RimGridCrossPlotCurveSet;
class RiuCvfOverlayItemWidget;
class RiuDraggableOverlayFrame;
class RiuPlotAnnotationTool;
class RimPlotAxisProperties;
@ -55,12 +56,18 @@ public:
protected:
void updateLayout() override;
void updateInfoBoxLayout();
void updateLegendLayout();
void resizeEvent(QResizeEvent* e) override;
bool resizeOverlayItemToFitPlot(caf::TitledOverlayFrame* overlayItem);
void contextMenuEvent(QContextMenuEvent*) override;
private:
std::map<caf::PdmPointer<RimGridCrossPlotCurveSet>, QPointer<RiuCvfOverlayItemWidget>> m_legendWidgets;
std::unique_ptr<RiuPlotAnnotationTool> m_annotationTool;
typedef caf::PdmPointer<RimGridCrossPlotCurveSet> CurveSetPtr;
typedef QPointer<RiuCvfOverlayItemWidget> LegendPtr;
typedef QPointer<RiuDraggableOverlayFrame> InfoBoxPtr;
InfoBoxPtr m_infoBox;
std::map<CurveSetPtr, LegendPtr> m_legendWidgets;
std::unique_ptr<RiuPlotAnnotationTool> m_annotationTool;
};

View File

@ -123,10 +123,7 @@ void RiuSummaryQwtPlot::addOrUpdateEnsembleCurveSetLegend(RimEnsembleCurveSet* c
auto it = m_ensembleLegendWidgets.find(curveSetToShowLegendFor);
if (it == m_ensembleLegendWidgets.end() || it->second == nullptr)
{
overlayWidget = new RiuCvfOverlayItemWidget(this);
new RiuWidgetDragger(overlayWidget);
overlayWidget = new RiuCvfOverlayItemWidget(this, canvas());
m_ensembleLegendWidgets[curveSetToShowLegendFor] = overlayWidget;
}
else

View File

@ -24,12 +24,22 @@
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuWidgetDragger::RiuWidgetDragger(QWidget* widgetToMove)
RiuWidgetDragger::RiuWidgetDragger(QWidget* widgetToMove, QWidget* widgetToSnapTo /*= nullptr*/, int snapMargins /*= 5*/)
: QObject(widgetToMove)
, m_widgetToMove(widgetToMove)
, m_widgetToSnapTo(widgetToSnapTo)
, m_snapMargins(snapMargins)
, m_startPos(0,0)
{
m_widgetToMove->installEventFilter(this);
addWidget(m_widgetToMove);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuWidgetDragger::addWidget(QWidget* widget)
{
widget->installEventFilter(this);
}
//--------------------------------------------------------------------------------------------------
@ -42,13 +52,75 @@ bool RiuWidgetDragger::eventFilter(QObject * watched, QEvent * event)
QMouseEvent* mMoveEv = static_cast<QMouseEvent*>(event);
if (mMoveEv->buttons() & Qt::LeftButton)
{
m_widgetToMove->move( m_widgetToMove->mapToParent(mMoveEv->pos() - m_startPos));
QPoint relativeMove = mMoveEv->pos() - m_startPos;
QRect newFrameRect = m_widgetToMove->frameGeometry().translated(relativeMove);
if (m_widgetToSnapTo)
{
QRect snapToRect = m_widgetToSnapTo->frameGeometry();
{
QPoint snapToTopLeft = snapToRect.topLeft();
QPoint widgetTopLeft = newFrameRect.topLeft();
QPoint diff = snapToTopLeft - widgetTopLeft;
if (std::abs(diff.x()) < 4 * m_snapMargins)
{
newFrameRect.moveLeft(snapToTopLeft.x() + m_snapMargins);
}
if (std::abs(diff.y()) < 4 * m_snapMargins)
{
newFrameRect.moveTop(snapToTopLeft.y() + m_snapMargins);
}
}
{
QPoint snapToBottomLeft = snapToRect.bottomLeft();
QPoint widgetBottomLeft = newFrameRect.bottomLeft();
QPoint diff = snapToBottomLeft - widgetBottomLeft;
if (std::abs(diff.x()) < 4 * m_snapMargins)
{
newFrameRect.moveLeft(snapToBottomLeft.x() + m_snapMargins);
}
if (std::abs(diff.y()) < 4 * m_snapMargins)
{
newFrameRect.moveBottom(snapToBottomLeft.y() - m_snapMargins);
}
}
{
QPoint snapToTopRight = snapToRect.topRight();
QPoint widgetTopRight = newFrameRect.topRight();
QPoint diff = snapToTopRight - widgetTopRight;
if (std::abs(diff.x()) < 4 * m_snapMargins)
{
newFrameRect.moveRight(snapToTopRight.x() - m_snapMargins);
}
if (std::abs(diff.y()) < 4 * m_snapMargins)
{
newFrameRect.moveTop(snapToTopRight.y() + m_snapMargins);
}
}
{
QPoint snapToBottomRight = snapToRect.bottomRight();
QPoint widgetBottomRight = newFrameRect.bottomRight();
QPoint diff = snapToBottomRight - widgetBottomRight;
if (std::abs(diff.x()) < 4 * m_snapMargins)
{
newFrameRect.moveRight(snapToBottomRight.x() - m_snapMargins);
}
if (std::abs(diff.y()) < 4 * m_snapMargins)
{
newFrameRect.moveBottom(snapToBottomRight.y() - m_snapMargins);
}
}
}
m_widgetToMove->move(newFrameRect.topLeft());
return true;
}
}
else if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mEv = static_cast<QMouseEvent*>(event);
m_startPos = mEv->pos();
return true;
}
return false;

View File

@ -28,13 +28,16 @@ class RiuWidgetDragger : public QObject
{
Q_OBJECT
public:
RiuWidgetDragger(QWidget* widgetToMove);
RiuWidgetDragger(QWidget* widgetToMove, QWidget* widgetToSnapTo = nullptr, int snapMargins = 5);
void addWidget(QWidget* widget);
bool eventFilter(QObject * watched, QEvent * event) override;
private:
QPointer<QWidget> m_widgetToMove;
QPoint m_startPos;
QPointer<QWidget> m_widgetToSnapTo;
int m_snapMargins;
QPoint m_startPos;
};