From de355291298607f07dd92ea39e3936af4d037e9f Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 20 Jun 2021 18:17:32 +0200 Subject: [PATCH] Statistics graph painting --- .../objectstatisticsdialog.cpp | 2 +- .../objectstatisticsdialog.ui | 2 +- .../statisticsgraphwidget.cpp | 187 +++++++++++++++++- .../statisticsgraphwidget.h | 18 +- 4 files changed, 204 insertions(+), 5 deletions(-) diff --git a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.cpp b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.cpp index c838197..889d89d 100644 --- a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.cpp +++ b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.cpp @@ -22,7 +22,7 @@ namespace pdfplugin { ObjectStatisticsDialog::ObjectStatisticsDialog(const pdf::PDFDocument* document, QWidget *parent) : - QDialog(parent), + QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint), ui(new Ui::ObjectStatisticsDialog), m_document(document) { diff --git a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.ui b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.ui index d35747f..08d3f81 100644 --- a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.ui +++ b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/objectstatisticsdialog.ui @@ -11,7 +11,7 @@ - Dialog + Object Statistics diff --git a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.cpp b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.cpp index 34a3673..4167426 100644 --- a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.cpp +++ b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.cpp @@ -19,6 +19,9 @@ #include "ui_statisticsgraphwidget.h" #include "pdfwidgetutils.h" +#include "pdfpainterutils.h" + +#include namespace pdfplugin { @@ -51,11 +54,16 @@ StatisticsGraphWidget::GeometryHint StatisticsGraphWidget::getGeometryHint() con int fivePixelsX = pdf::PDFWidgetUtils::scaleDPI_x(this, 5); int fivePixelsY = pdf::PDFWidgetUtils::scaleDPI_y(this, 5); + int textMargin = fivePixelsX; hint.margins = QMargins(fivePixelsX, fivePixelsY, fivePixelsX, fivePixelsY); - hint.colorRectangleWidth = pdf::PDFWidgetUtils::scaleDPI_x(this, 40); + hint.colorRectangleWidth = pdf::PDFWidgetUtils::scaleDPI_x(this, 80); hint.linesWidthLeft = pdf::PDFWidgetUtils::scaleDPI_x(this, 15); hint.linesWidthRight = pdf::PDFWidgetUtils::scaleDPI_x(this, 5); + hint.markerSize = fivePixelsX; + hint.normalLineWidth = pdf::PDFWidgetUtils::scaleDPI_y(this, 2); + hint.selectedLineWidth = pdf::PDFWidgetUtils::scaleDPI_y(this, 3); + hint.colorRectangeLeftMargin = pdf::PDFWidgetUtils::scaleDPI_x(this, 40); QFontMetrics fontMetricsTitle(getTitleFont()); hint.titleWidth = fontMetricsTitle.horizontalAdvance(m_statistics.title); @@ -67,12 +75,176 @@ StatisticsGraphWidget::GeometryHint StatisticsGraphWidget::getGeometryHint() con QFontMetrics fontMetricsText(getTextFont()); hint.textHeight += fontMetricsText.lineSpacing() * int(m_statistics.items.size()); + hint.textMargin = textMargin; + hint.textLineHeight = fontMetricsText.lineSpacing(); + // Determine text header width - s + hint.textWidths.resize(m_statistics.headers.size(), 0); + + for (int i = 0; i < m_statistics.headers.size(); ++i) + { + hint.textWidths[i] = fontMetricsHeader.horizontalAdvance(m_statistics.headers[i]); + } + + for (const auto& item : m_statistics.items) + { + Q_ASSERT(item.texts.size() == hint.textWidths.size()); + + for (int i = 0; i < item.texts.size(); ++i) + { + hint.textWidths[i] = qMax(hint.textWidths[i], fontMetricsHeader.horizontalAdvance(m_statistics.headers[i])); + } + } + + int totalTextWidth = textMargin; + for (int& textWidth : hint.textWidths) + { + textWidth += 2 * textMargin; + totalTextWidth += textWidth; + } + + const int widthHint = hint.margins.left() + hint.colorRectangeLeftMargin + hint.colorRectangleWidth + hint.linesWidthLeft + hint.linesWidthRight + qMax(totalTextWidth, hint.titleWidth) + hint.margins.right(); + const int heightHint = hint.margins.top() + hint.titleHeight + qMax(pdf::PDFWidgetUtils::scaleDPI_y(this, 100), hint.textHeight) + hint.margins.bottom(); + + hint.minimalWidgetSize = QSize(widthHint, heightHint).expandedTo(pdf::PDFWidgetUtils::scaleDPI(this, QSize(400, 300))); return hint; } +void StatisticsGraphWidget::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + + QPainter painter(this); + QRect rect = this->rect(); + + painter.fillRect(rect, Qt::white); + + GeometryHint geometryHint = getGeometryHint(); + rect = rect.marginsRemoved(geometryHint.margins); + + if (!m_statistics.title.isEmpty()) + { + pdf::PDFPainterStateGuard guard(&painter); + + QRect titleRect = rect; + titleRect.setHeight(geometryHint.titleHeight); + + painter.setFont(getHeaderFont()); + painter.drawText(titleRect, Qt::AlignCenter, m_statistics.title); + + rect.setTop(titleRect.bottom()); + } + + rect.setLeft(rect.left() + geometryHint.colorRectangeLeftMargin); + + // Color boxes + m_colorBoxes.clear(); + const int height = rect.height(); + + QColor selectedColor = Qt::darkYellow; + + int top = rect.top(); + for (size_t i = 0; i < m_statistics.items.size(); ++i) + { + const StatisticsItem& item = m_statistics.items[i]; + QRect currentRect(rect.left(), top, geometryHint.colorRectangleWidth, height * item.portion); + top = currentRect.bottom(); + + QColor color = (m_selectedColorBox == i) ? selectedColor: item.color; + painter.fillRect(currentRect, color); + m_colorBoxes.push_back(currentRect); + } + + QPoint minLinePoint; + QPoint maxLinePoint; + + // Marked lines + { + pdf::PDFPainterStateGuard guard(&painter); + for (size_t i = 0; i < m_statistics.items.size(); ++i) + { + QPen pen = painter.pen(); + pen.setColor((i == m_selectedColorBox) ? selectedColor : Qt::black); + pen.setWidth(geometryHint.markerSize); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + + QRect colorBox = m_colorBoxes[i]; + QPoint marker = colorBox.center(); + marker.setX(colorBox.right()); + + painter.drawPoint(marker); + + pen.setWidth((i == m_selectedColorBox) ? geometryHint.selectedLineWidth : geometryHint.normalLineWidth); + painter.setPen(pen); + + QPoint lineEnd = marker + QPoint(geometryHint.linesWidthLeft, 0); + painter.drawLine(marker, lineEnd); + + if (i == 0) + { + minLinePoint = lineEnd; + } + maxLinePoint = lineEnd; + } + } + + // Texts + { + pdf::PDFPainterStateGuard guard(&painter); + + QFont headerFont = getHeaderFont(); + QFont textFont = getTextFont(); + + QRect textRect = rect; + textRect.setLeft(maxLinePoint.x() + geometryHint.textMargin); + textRect.setHeight(geometryHint.textLineHeight); + + auto drawTexts = [&](const QStringList& texts, bool isHeader) + { + pdf::PDFPainterStateGuard guard(&painter); + painter.setFont(isHeader ? headerFont : textFont); + + const int y = textRect.center().y(); + minLinePoint.ry() = qMin(minLinePoint.ry(), y); + maxLinePoint.ry() = qMax(maxLinePoint.ry(), y); + + QRect cellRect = textRect; + for (int i = 0; i < texts.size(); ++i) + { + cellRect.setWidth(geometryHint.textWidths[i]); + QRect finalCellRect = cellRect.marginsRemoved(QMargins(geometryHint.textMargin, 0, geometryHint.textMargin, 0)); + QString text = texts[i]; + Qt::Alignment alignment = (i == 0) ? Qt::Alignment(Qt::AlignLeft | Qt::AlignVCenter) : Qt::Alignment(Qt::AlignRight | Qt::AlignVCenter); + painter.drawText(finalCellRect, alignment, text); + cellRect.translate(cellRect.width(), 0); + } + + textRect.translate(0, textRect.height()); + }; + + if (!m_statistics.headers.isEmpty()) + { + drawTexts(m_statistics.headers, true); + } + + for (size_t i = 0; i < m_statistics.items.size(); ++i) + { + const StatisticsItem& item = m_statistics.items[i]; + drawTexts(item.texts, i == m_selectedColorBox); + } + + QPen pen = painter.pen(); + pen.setColor(Qt::black); + pen.setWidth(geometryHint.normalLineWidth); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + + painter.drawLine(minLinePoint, maxLinePoint); + } +} + QFont StatisticsGraphWidget::getTitleFont() const { QFont font = this->font(); @@ -93,4 +265,15 @@ QFont StatisticsGraphWidget::getTextFont() const return this->font(); } +QSize StatisticsGraphWidget::sizeHint() const +{ + return getGeometryHint().minimalWidgetSize + pdf::PDFWidgetUtils::scaleDPI(this, QSize(100, 100)); +} + +QSize StatisticsGraphWidget::minimumSizeHint() const +{ + return getGeometryHint().minimalWidgetSize; +} + } // namespace pdfplugin + diff --git a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.h b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.h index 154389f..16ccd2a 100644 --- a/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.h +++ b/Pdf4QtViewerPlugins/ObjectInspectorPlugin/statisticsgraphwidget.h @@ -20,6 +20,8 @@ #include +#include + namespace Ui { class StatisticsGraphWidget; @@ -55,8 +57,14 @@ public: explicit StatisticsGraphWidget(QWidget* parent); virtual ~StatisticsGraphWidget() override; + virtual QSize sizeHint() const override; + virtual QSize minimumSizeHint() const override; + void setStatistics(Statistics statistics); +protected: + virtual void paintEvent(QPaintEvent* event) override; + private: Ui::StatisticsGraphWidget* ui; @@ -66,14 +74,20 @@ private: int colorRectangleWidth = 0; int linesWidthLeft = 0; int linesWidthRight = 0; + int markerSize = 0; + int normalLineWidth = 0; + int selectedLineWidth = 0; + int colorRectangeLeftMargin = 0; int titleWidth = 0; int titleHeight = 0; int textHeight = 0; + int textMargin = 0; + int textLineHeight = 0; std::vector textWidths; - QSize minimalWidgetSize = 0; + QSize minimalWidgetSize; }; GeometryHint getGeometryHint() const; @@ -83,6 +97,8 @@ private: QFont getTextFont() const; Statistics m_statistics; + std::vector m_colorBoxes; + size_t m_selectedColorBox = -1; }; } // namespace pdfplugin