///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RiuGridCrossQwtPlot.h" #include "RiaFontCache.h" #include "RiuCvfOverlayItemWidget.h" #include "RiuRimQwtPlotCurve.h" #include "RiuQwtCurvePointTracker.h" #include "RiuWidgetDragger.h" #include "RimGridCrossPlot.h" #include "RimGridCrossPlotDataSet.h" #include "RimGridCrossPlotCurve.h" #include "RimRegularLegendConfig.h" #include "cafCmdFeatureMenuBuilder.h" #include "cafFixedAtlasFont.h" #include "cafSelectionManager.h" #include "cafTitledOverlayFrame.h" #include "RimPlotAxisAnnotation.h" #include "RimPlotAxisProperties.h" #include "RiuPlotAnnotationTool.h" #include "qwt_text.h" #include "qwt_text_engine.h" #include #include #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuGridCrossQwtPlot::RiuGridCrossQwtPlot(RimViewWindow* ownerViewWindow, QWidget* parent /*= nullptr*/) : RiuQwtPlot(ownerViewWindow, parent) { m_annotationTool = std::unique_ptr(new RiuPlotAnnotationTool()); m_infoBox = new RiuDraggableOverlayFrame(this, canvas()); m_selectedPointMarker = new QwtPlotMarker; // QwtPlotMarker takes ownership of the symbol, it is deleted in destructor of QwtPlotMarker QwtSymbol* mySymbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(QColor(255, 255, 255, 50)), QPen(Qt::black, 2.0), QSize(10, 10)); m_selectedPointMarker->setSymbol(mySymbol); m_selectedPointMarker->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); m_selectedPointMarker->setSpacing(3); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuGridCrossQwtPlot::~RiuGridCrossQwtPlot() { if (m_selectedPointMarker->plot()) { m_selectedPointMarker->detach(); } delete m_selectedPointMarker; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::addOrUpdateDataSetLegend(RimGridCrossPlotDataSet* dataSet) { RiuCvfOverlayItemWidget* overlayWidget = nullptr; auto it = m_legendWidgets.find(dataSet); if (it == m_legendWidgets.end() || it->second == nullptr) { overlayWidget = new RiuCvfOverlayItemWidget(this, canvas()); m_legendWidgets[dataSet] = overlayWidget; } else { overlayWidget = it->second; } if (overlayWidget) { caf::TitledOverlayFrame* overlayItem = dataSet->legendConfig()->titledOverlayFrame(); applyFontSizeToOverlayItem(overlayItem); resizeOverlayItemToFitPlot(overlayItem); overlayWidget->updateFromOverlayItem(overlayItem); } this->updateLegendLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::removeDataSetLegend(RimGridCrossPlotDataSet* dataSetToShowLegendFor) { auto it = m_legendWidgets.find(dataSetToShowLegendFor); if (it != m_legendWidgets.end()) { if (it->second != nullptr) { it->second->hide(); it->second->deleteLater(); } m_legendWidgets.erase(it); } this->updateLegendLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::removeDanglingDataSetLegends() { for (auto it = m_legendWidgets.begin(); it != m_legendWidgets.end();) { if (it->first.isNull()) { if (it->second != nullptr) { it->second->hide(); it->second->deleteLater(); } m_legendWidgets.erase(it++); } else { ++it; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::updateLegendSizesToMatchPlot() { RimGridCrossPlot* crossPlot = dynamic_cast(ownerPlotDefinition()); if (!crossPlot) return; bool anyLegendResized = false; for (RimGridCrossPlotDataSet* dataSet : crossPlot->dataSets()) { if (!dataSet->isChecked() || !dataSet->legendConfig()->showLegend()) continue; auto pairIt = m_legendWidgets.find(dataSet); if (pairIt != m_legendWidgets.end()) { RiuCvfOverlayItemWidget* overlayWidget = pairIt->second; caf::TitledOverlayFrame* overlayItem = dataSet->legendConfig()->titledOverlayFrame(); applyFontSizeToOverlayItem(overlayItem); if (resizeOverlayItemToFitPlot(overlayItem)) { anyLegendResized = true; overlayWidget->updateFromOverlayItem(overlayItem); } } } if (anyLegendResized) { updateLegendLayout(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::updateAnnotationObjects(RimPlotAxisProperties* axisProperties) { m_annotationTool->detachAllAnnotations(); for (auto annotation : axisProperties->annotations()) { m_annotationTool->attachAnnotationLine(this, annotation->color(), annotation->name(), annotation->value()); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::updateLayout() { QwtPlot::updateLayout(); updateInfoBoxLayout(); updateLegendLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::updateInfoBoxLayout() { RimGridCrossPlot* crossPlot = dynamic_cast(ownerPlotDefinition()); if (!crossPlot) return; bool showInfo = false; if (crossPlot->showInfoBox()) { QStringList curveInfoTexts; for (auto dataSet : crossPlot->dataSets()) { QString curveInfoText = dataSet->infoText(); if (dataSet->isChecked() && !curveInfoText.isEmpty()) { curveInfoTexts += curveInfoText; } } QStringList infoText; if (curveInfoTexts.size() > 1) { infoText += QString("
    "); for (QString curveInfoText : curveInfoTexts) { infoText += QString("
  1. %1
  2. ").arg(curveInfoText); } infoText += QString("
"); } else if (curveInfoTexts.size() > 0) { infoText += curveInfoTexts.front(); } if (!infoText.empty()) { m_infoBox->label()->setText(infoText.join("\n")); QFont font = m_infoBox->font(); font.setPointSize(crossPlot->legendFontSize()); m_infoBox->setFont(font); 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(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::updateLegendLayout() { const int spacing = 5; int startMarginX = this->canvas()->pos().x() + spacing; int startMarginY = this->canvas()->pos().y() + spacing; int xpos = startMarginX; int ypos = startMarginY; int maxColumnWidth = 0; removeDanglingDataSetLegends(); RimGridCrossPlot* crossPlot = dynamic_cast(ownerPlotDefinition()); if (!crossPlot) return; std::set legendTypes; for (RimGridCrossPlotDataSet* dataSet : crossPlot->dataSets()) { if (dataSet->isChecked() && dataSet->groupingEnabled() && dataSet->legendConfig()->showLegend()) { auto pairIt = m_legendWidgets.find(dataSet); if (pairIt != m_legendWidgets.end()) { RiuCvfOverlayItemWidget* overlayWidget = pairIt->second; // Show only one copy of each legend type if (!legendTypes.count(dataSet->groupParameter())) { if (ypos + overlayWidget->height() + spacing > this->canvas()->height()) { xpos += spacing + maxColumnWidth; ypos = startMarginY; maxColumnWidth = 0; } overlayWidget->show(); overlayWidget->move(xpos, ypos); ypos += pairIt->second->height() + spacing; maxColumnWidth = std::max(maxColumnWidth, pairIt->second->width()); legendTypes.insert(dataSet->groupParameter()); } } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::resizeEvent(QResizeEvent* e) { QwtPlot::resizeEvent(e); updateLegendSizesToMatchPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuGridCrossQwtPlot::resizeOverlayItemToFitPlot(caf::TitledOverlayFrame* overlayItem) { QSize plotSize = this->canvas()->contentsRect().size(); cvf::Vec2ui existingRenderSize = overlayItem->renderSize(); cvf::Vec2ui legendSize = overlayItem->preferredSize(); bool sizeAltered = false; if (plotSize.width() > 0 && (double)legendSize.x() > 0.9 * plotSize.width()) { legendSize.x() = (plotSize.width() * 9) / 10; sizeAltered = true; } if (plotSize.height() > 0 && (double)legendSize.y() > 0.9 * plotSize.height()) { legendSize.y() = (plotSize.height() * 9) / 10; sizeAltered = true; } overlayItem->setRenderSize(legendSize); if (legendSize.x() != existingRenderSize.x() || legendSize.y() != existingRenderSize.y()) { sizeAltered = true; } return sizeAltered; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::contextMenuEvent(QContextMenuEvent* event) { QMenu menu; caf::CmdFeatureMenuBuilder menuBuilder; caf::SelectionManager::instance()->setSelectedItem(ownerViewWindow()); menuBuilder << "RicSwapGridCrossPlotDataSetAxesFeature"; menuBuilder << "Separator"; menuBuilder << "RicShowPlotDataFeature"; menuBuilder.appendToMenu(&menu); if (menu.actions().size() > 0) { menu.exec(event->globalPos()); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::selectSample(QwtPlotCurve* curve, int sampleNumber) { QPointF sample = curve->sample(sampleNumber); m_selectedPointMarker->setValue(sample); m_selectedPointMarker->setAxes(QwtPlot::xBottom, QwtPlot::yLeft); m_selectedPointMarker->attach(this); QString curveName, xAxisName, yAxisName; if (curveText(curve, &curveName, &xAxisName, &yAxisName)) { QString labelFormat("
%1:
%2 = %3, %4 = %5
"); QString labelString = labelFormat.arg(curveName).arg(xAxisName).arg(sample.x()).arg(yAxisName).arg(sample.y()); QwtText curveLabel(labelString, QwtText::RichText); curveLabel.setBackgroundBrush(QBrush(QColor(250, 250, 250, 220))); curveLabel.setPaintAttribute(QwtText::PaintBackground); curveLabel.setBorderPen(QPen(Qt::black, 1.0)); curveLabel.setBorderRadius(2.0); m_selectedPointMarker->setLabel(curveLabel); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::clearSampleSelection() { m_selectedPointMarker->detach(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RiuGridCrossQwtPlot::curveText(const QwtPlotCurve* curve, QString* curveTitle, QString* xParamName, QString* yParamName) const { CVF_ASSERT(curveTitle && xParamName && yParamName); auto riuCurve = dynamic_cast(curve); if (riuCurve) { auto crossPlotCurve = dynamic_cast(riuCurve->ownerRimCurve()); if (crossPlotCurve) { *curveTitle = crossPlotCurve->curveName(); RimGridCrossPlotDataSet* dataSet = nullptr; crossPlotCurve->firstAncestorOrThisOfType(dataSet); if (dataSet) { *xParamName = dataSet->xAxisName(); *yParamName = dataSet->yAxisName(); return true; } } } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::applyFontSizeToOverlayItem(caf::TitledOverlayFrame* overlayItem) { RimGridCrossPlot* crossPlot = static_cast(ownerViewWindow()); int fontSize = crossPlot->legendFontSize(); cvf::ref cafFont = RiaFontCache::getFont(RiaFontCache::fontSizeEnumFromPointSize(fontSize)); overlayItem->setFont(cafFont.p()); }