Skip to content
Snippets Groups Projects
qwt_plot_print.cpp 15.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • pixhawk's avatar
    pixhawk committed
    /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
     * Qwt Widget Library
     * Copyright (C) 1997   Josef Wilgen
     * Copyright (C) 2002   Uwe Rathmann
     *
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the Qwt License, Version 1.0
     *****************************************************************************/
    
    // vim: expandtab
    
    #include <qpainter.h>
    #if QT_VERSION < 0x040000
    #include <qpaintdevicemetrics.h>
    #else
    #include <qpaintengine.h>
    #endif
    #include "qwt_painter.h"
    #include "qwt_legend_item.h"
    #include "qwt_plot.h"
    #include "qwt_plot_canvas.h"
    #include "qwt_plot_layout.h"
    #include "qwt_legend.h"
    #include "qwt_dyngrid_layout.h"
    #include "qwt_scale_widget.h"
    #include "qwt_scale_engine.h"
    #include "qwt_text.h"
    #include "qwt_text_label.h"
    #include "qwt_math.h"
    
    /*!
      \brief Print the plot to a \c QPaintDevice (\c QPrinter)
      This function prints the contents of a QwtPlot instance to
      \c QPaintDevice object. The size is derived from its device
      metrics.
    
      \param paintDev device to paint on, often a printer
      \param pfilter print filter
      \sa QwtPlot::print
      \sa QwtPlotPrintFilter
    */
    
    void QwtPlot::print(QPaintDevice &paintDev,
    
                        const QwtPlotPrintFilter &pfilter) const
    
    pixhawk's avatar
    pixhawk committed
    {
    #if QT_VERSION < 0x040000
        QPaintDeviceMetrics mpr(&paintDev);
        int w = mpr.width();
        int h = mpr.height();
    #else
        int w = paintDev.width();
        int h = paintDev.height();
    #endif
    
        QRect rect(0, 0, w, h);
        double aspect = double(rect.width())/double(rect.height());
        if ((aspect < 1.0))
            rect.setHeight(int(aspect*rect.width()));
    
        QPainter p(&paintDev);
        print(&p, rect, pfilter);
    }
    
    /*!
      \brief Paint the plot into a given rectangle.
      Paint the contents of a QwtPlot instance into a given rectangle.
    
      \param painter Painter
      \param plotRect Bounding rectangle
      \param pfilter Print filter
      \sa QwtPlotPrintFilter
    */
    void QwtPlot::print(QPainter *painter, const QRect &plotRect,
    
                        const QwtPlotPrintFilter &pfilter) const
    
    pixhawk's avatar
    pixhawk committed
    {
        int axisId;
    
        if ( painter == 0 || !painter->isActive() ||
                !plotRect.isValid() || size().isNull() )
    
    pixhawk's avatar
    pixhawk committed
    
        painter->save();
    #if 1
        /*
          PDF: In Qt4 ( <= 4.3.2 ) the scales are painted in gray instead of
          black. See http://trolltech.com/developer/task-tracker/index_html?id=184671&method=entry
          The dummy lines below work around the problem.
         */
        const QPen pen = painter->pen();
        painter->setPen(QPen(Qt::black, 1));
        painter->setPen(pen);
    #endif
    
        // All paint operations need to be scaled according to
    
        // the paint device metrics.
    
    pixhawk's avatar
    pixhawk committed
    
        QwtPainter::setMetricsMap(this, painter->device());
        const QwtMetricsMap &metricsMap = QwtPainter::metricsMap();
    
        // It is almost impossible to integrate into the Qt layout
        // framework, when using different fonts for printing
        // and screen. To avoid writing different and Qt unconform
    
        // layout engines we change the widget attributes, print and
    
    pixhawk's avatar
    pixhawk committed
        // reset the widget attributes again. This way we produce a lot of
        // useless layout events ...
    
        pfilter.apply((QwtPlot *)this);
    
        int baseLineDists[QwtPlot::axisCnt];
    
        if ( pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales ) {
            for (axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) {
    
    pixhawk's avatar
    pixhawk committed
                QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(axisId);
    
                if ( scaleWidget ) {
    
    pixhawk's avatar
    pixhawk committed
                    baseLineDists[axisId] = scaleWidget->margin();
                    scaleWidget->setMargin(0);
                }
            }
        }
        // Calculate the layout for the print.
    
    
        int layoutOptions = QwtPlotLayout::IgnoreScrollbars
                            | QwtPlotLayout::IgnoreFrames;
    
    pixhawk's avatar
    pixhawk committed
        if ( !(pfilter.options() & QwtPlotPrintFilter::PrintMargin) )
            layoutOptions |= QwtPlotLayout::IgnoreMargin;
        if ( !(pfilter.options() & QwtPlotPrintFilter::PrintLegend) )
            layoutOptions |= QwtPlotLayout::IgnoreLegend;
    
    
        ((QwtPlot *)this)->plotLayout()->activate(this,
                QwtPainter::metricsMap().deviceToLayout(plotRect),
                layoutOptions);
    
    pixhawk's avatar
    pixhawk committed
    
        if ((pfilter.options() & QwtPlotPrintFilter::PrintTitle)
    
                && (!titleLabel()->text().isEmpty())) {
    
    pixhawk's avatar
    pixhawk committed
            printTitle(painter, plotLayout()->titleRect());
        }
    
        if ( (pfilter.options() & QwtPlotPrintFilter::PrintLegend)
    
                && legend() && !legend()->isEmpty() ) {
    
    pixhawk's avatar
    pixhawk committed
            printLegend(painter, plotLayout()->legendRect());
        }
    
    
        for ( axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) {
    
    pixhawk's avatar
    pixhawk committed
            QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(axisId);
    
            if (scaleWidget) {
    
    pixhawk's avatar
    pixhawk committed
                int baseDist = scaleWidget->margin();
    
                int startDist, endDist;
                scaleWidget->getBorderDistHint(startDist, endDist);
    
                printScale(painter, axisId, startDist, endDist,
    
                           baseDist, plotLayout()->scaleRect(axisId));
    
    pixhawk's avatar
    pixhawk committed
            }
        }
    
        QRect canvasRect = plotLayout()->canvasRect();
    
    
    pixhawk's avatar
    pixhawk committed
           The border of the bounding rect needs to ba scaled to
    
           layout coordinates, so that it is aligned to the axes
    
    pixhawk's avatar
    pixhawk committed
         */
        QRect boundingRect( canvasRect.left() - 1, canvasRect.top() - 1,
    
                            canvasRect.width() + 2, canvasRect.height() + 2);
    
    pixhawk's avatar
    pixhawk committed
        boundingRect = metricsMap.layoutToDevice(boundingRect);
        boundingRect.setWidth(boundingRect.width() - 1);
        boundingRect.setHeight(boundingRect.height() - 1);
    
        canvasRect = metricsMap.layoutToDevice(canvasRect);
    
    pixhawk's avatar
    pixhawk committed
        // When using QwtPainter all sizes where computed in pixel
        // coordinates and scaled by QwtPainter later. This limits
        // the precision to screen resolution. A better solution
        // is to scale the maps and print in unlimited resolution.
    
        QwtScaleMap map[axisCnt];
    
        for (axisId = 0; axisId < axisCnt; axisId++) {
    
    pixhawk's avatar
    pixhawk committed
            map[axisId].setTransformation(axisScaleEngine(axisId)->transformation());
    
            const QwtScaleDiv &scaleDiv = *axisScaleDiv(axisId);
            map[axisId].setScaleInterval(scaleDiv.lBound(), scaleDiv.hBound());
    
            double from, to;
    
            if ( axisEnabled(axisId) ) {
    
    pixhawk's avatar
    pixhawk committed
                const int sDist = axisWidget(axisId)->startBorderDist();
                const int eDist = axisWidget(axisId)->endBorderDist();
                const QRect &scaleRect = plotLayout()->scaleRect(axisId);
    
    
                if ( axisId == xTop || axisId == xBottom ) {
    
    pixhawk's avatar
    pixhawk committed
                    from = metricsMap.layoutToDeviceX(scaleRect.left() + sDist);
                    to = metricsMap.layoutToDeviceX(scaleRect.right() + 1 - eDist);
    
    pixhawk's avatar
    pixhawk committed
                    from = metricsMap.layoutToDeviceY(scaleRect.bottom() + 1 - eDist );
                    to = metricsMap.layoutToDeviceY(scaleRect.top() + sDist);
                }
    
    pixhawk's avatar
    pixhawk committed
                int margin = plotLayout()->canvasMargin(axisId);
    
                if ( axisId == yLeft || axisId == yRight ) {
    
    pixhawk's avatar
    pixhawk committed
                    margin = metricsMap.layoutToDeviceY(margin);
                    from = canvasRect.bottom() - margin;
                    to = canvasRect.top() + margin;
    
    pixhawk's avatar
    pixhawk committed
                    margin = metricsMap.layoutToDeviceX(margin);
                    from = canvasRect.left() + margin;
                    to = canvasRect.right() - margin;
                }
            }
            map[axisId].setPaintXInterval(from, to);
        }
    
    
        // The canvas maps are already scaled.
    
    pixhawk's avatar
    pixhawk committed
        QwtPainter::setMetricsMap(painter->device(), painter->device());
        printCanvas(painter, boundingRect, canvasRect, map, pfilter);
        QwtPainter::resetMetricsMap();
    
        ((QwtPlot *)this)->plotLayout()->invalidate();
    
        // reset all widgets with their original attributes.
    
        if ( pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales ) {
    
    pixhawk's avatar
    pixhawk committed
            // restore the previous base line dists
    
    
            for (axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) {
    
    pixhawk's avatar
    pixhawk committed
                QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(axisId);
                if ( scaleWidget  )
                    scaleWidget->setMargin(baseLineDists[axisId]);
            }
        }
    
        pfilter.reset((QwtPlot *)this);
    
        painter->restore();
    }
    
    /*!
      Print the title into a given rectangle.
    
      \param painter Painter
      \param rect Bounding rectangle
    */
    
    void QwtPlot::printTitle(QPainter *painter, const QRect &rect) const
    {
        painter->setFont(titleLabel()->font());
    
    
        const QColor color =
    
    pixhawk's avatar
    pixhawk committed
    #if QT_VERSION < 0x040000
            titleLabel()->palette().color(
                QPalette::Active, QColorGroup::Text);
    #else
            titleLabel()->palette().color(
                QPalette::Active, QPalette::Text);
    #endif
    
        painter->setPen(color);
        titleLabel()->text().draw(painter, rect);
    }
    
    /*!
      Print the legend into a given rectangle.
    
      \param painter Painter
      \param rect Bounding rectangle
    */
    
    void QwtPlot::printLegend(QPainter *painter, const QRect &rect) const
    {
        if ( !legend() || legend()->isEmpty() )
            return;
    
        QLayout *l = legend()->contentsWidget()->layout();
        if ( l == 0 || !l->inherits("QwtDynGridLayout") )
            return;
    
        QwtDynGridLayout *legendLayout = (QwtDynGridLayout *)l;
    
        uint numCols = legendLayout->columnsForWidth(rect.width());
    #if QT_VERSION < 0x040000
    
        QValueList<QRect> itemRects =
    
    pixhawk's avatar
    pixhawk committed
            legendLayout->layoutItems(rect, numCols);
    #else
    
        QList<QRect> itemRects =
    
    pixhawk's avatar
    pixhawk committed
            legendLayout->layoutItems(rect, numCols);
    #endif
    
        int index = 0;
    
    #if QT_VERSION < 0x040000
        QLayoutIterator layoutIterator = legendLayout->iterator();
    
        for ( QLayoutItem *item = layoutIterator.current();
                item != 0; item = ++layoutIterator) {
    
    pixhawk's avatar
    pixhawk committed
    #else
    
        for ( int i = 0; i < legendLayout->count(); i++ ) {
    
    pixhawk's avatar
    pixhawk committed
            QLayoutItem *item = legendLayout->itemAt(i);
    #endif
            QWidget *w = item->widget();
    
    pixhawk's avatar
    pixhawk committed
                painter->save();
                painter->setClipping(true);
                QwtPainter::setClipRect(painter, itemRects[index]);
    
                printLegendItem(painter, w, itemRects[index]);
    
                index++;
                painter->restore();
            }
        }
    }
    
    /*!
      Print the legend item into a given rectangle.
    
      \param painter Painter
      \param w Widget representing a legend item
      \param rect Bounding rectangle
    */
    
    
    void QwtPlot::printLegendItem(QPainter *painter,
                                  const QWidget *w, const QRect &rect) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
        if ( w->inherits("QwtLegendItem") ) {
    
    pixhawk's avatar
    pixhawk committed
            QwtLegendItem *item = (QwtLegendItem *)w;
    
            painter->setFont(item->font());
            item->drawItem(painter, rect);
        }
    }
    
    /*!
      \brief Paint a scale into a given rectangle.
      Paint the scale into a given rectangle.
    
      \param painter Painter
      \param axisId Axis
      \param startDist Start border distance
      \param endDist End border distance
      \param baseDist Base distance
      \param rect Bounding rectangle
    */
    
    void QwtPlot::printScale(QPainter *painter,
    
                             int axisId, int startDist, int endDist, int baseDist,
                             const QRect &rect) const
    
    pixhawk's avatar
    pixhawk committed
    {
        if (!axisEnabled(axisId))
            return;
    
        const QwtScaleWidget *scaleWidget = axisWidget(axisId);
    
        if ( scaleWidget->isColorBarEnabled()
                && scaleWidget->colorBarWidth() > 0) {
    
    pixhawk's avatar
    pixhawk committed
            const QwtMetricsMap map = QwtPainter::metricsMap();
    
            QRect r = map.layoutToScreen(rect);
            r.setWidth(r.width() - 1);
            r.setHeight(r.height() - 1);
    
            scaleWidget->drawColorBar(painter, scaleWidget->colorBarRect(r));
    
            const int off = scaleWidget->colorBarWidth() + scaleWidget->spacing();
            if ( scaleWidget->scaleDraw()->orientation() == Qt::Horizontal )
                baseDist += map.screenToLayoutY(off);
            else
                baseDist += map.screenToLayoutX(off);
        }
    
        QwtScaleDraw::Alignment align;
        int x, y, w;
    
    
        switch(axisId) {
        case yLeft: {
            x = rect.right() - baseDist;
            y = rect.y() + startDist;
            w = rect.height() - startDist - endDist;
            align = QwtScaleDraw::LeftScale;
            break;
        }
        case yRight: {
            x = rect.left() + baseDist;
            y = rect.y() + startDist;
            w = rect.height() - startDist - endDist;
            align = QwtScaleDraw::RightScale;
            break;
        }
        case xTop: {
            x = rect.left() + startDist;
            y = rect.bottom() - baseDist;
            w = rect.width() - startDist - endDist;
            align = QwtScaleDraw::TopScale;
            break;
        }
        case xBottom: {
            x = rect.left() + startDist;
            y = rect.top() + baseDist;
            w = rect.width() - startDist - endDist;
            align = QwtScaleDraw::BottomScale;
            break;
        }
        default:
            return;
    
    pixhawk's avatar
    pixhawk committed
        }
    
        scaleWidget->drawTitle(painter, align, rect);
    
        painter->save();
        painter->setFont(scaleWidget->font());
    
        QPen pen = painter->pen();
        pen.setWidth(scaleWidget->penWidth());
        painter->setPen(pen);
    
        QwtScaleDraw *sd = (QwtScaleDraw *)scaleWidget->scaleDraw();
        const QPoint sdPos = sd->pos();
        const int sdLength = sd->length();
    
        sd->move(x, y);
        sd->setLength(w);
    
    #if QT_VERSION < 0x040000
        sd->draw(painter, scaleWidget->palette().active());
    #else
        QPalette palette = scaleWidget->palette();
        palette.setCurrentColorGroup(QPalette::Active);
        sd->draw(painter, palette);
    #endif
        // reset previous values
    
        sd->move(sdPos);
        sd->setLength(sdLength);
    
    pixhawk's avatar
    pixhawk committed
    
        painter->restore();
    }
    
    /*!
      Print the canvas into a given rectangle.
    
      \param painter Painter
      \param map Maps mapping between plot and paint device coordinates
      \param boundingRect Bounding rectangle
      \param canvasRect Canvas rectangle
      \param pfilter Print filter
      \sa QwtPlotPrintFilter
    */
    
    
    void QwtPlot::printCanvas(QPainter *painter,
                              const QRect &boundingRect, const QRect &canvasRect,
                              const QwtScaleMap map[axisCnt], const QwtPlotPrintFilter &pfilter) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
        if ( pfilter.options() & QwtPlotPrintFilter::PrintBackground ) {
    
    pixhawk's avatar
    pixhawk committed
            QBrush bgBrush;
    #if QT_VERSION >= 0x040000
    
            bgBrush = canvas()->palette().brush(backgroundRole());
    
    pixhawk's avatar
    pixhawk committed
    #else
            QColorGroup::ColorRole role =
                QPalette::backgroundRoleFromMode( backgroundMode() );
            bgBrush = canvas()->colorGroup().brush( role );
    #endif
            QRect r = boundingRect;
    
            if ( !(pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales) ) {
    
    pixhawk's avatar
    pixhawk committed
                r = canvasRect;
    #if QT_VERSION >= 0x040000
                // Unfortunately the paint engines do no always the same
                const QPaintEngine *pe = painter->paintEngine();
    
                if ( pe ) {
                    switch(painter->paintEngine()->type() ) {
                    case QPaintEngine::Raster:
                    case QPaintEngine::X11:
                        break;
                    default:
                        r.setWidth(r.width() - 1);
                        r.setHeight(r.height() - 1);
                        break;
    
    pixhawk's avatar
    pixhawk committed
                    }
                }
    #else
    
                if ( painter->device()->isExtDev() ) {
    
    pixhawk's avatar
    pixhawk committed
                    r.setWidth(r.width() - 1);
    
                    r.setHeight(r.height() - 1);
    
    pixhawk's avatar
    pixhawk committed
                }
    #endif
            }
    
            QwtPainter::fillRect(painter, r, bgBrush);
        }
    
    
        if ( pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales ) {
    
    pixhawk's avatar
    pixhawk committed
            painter->save();
            painter->setPen(QPen(Qt::black));
            painter->setBrush(QBrush(Qt::NoBrush));
            QwtPainter::drawRect(painter, boundingRect);
            painter->restore();
        }
    
        painter->setClipping(true);
        QwtPainter::setClipRect(painter, canvasRect);
    
        drawItems(painter, canvasRect, map, pfilter);
    }