qt_demoe/qwtdemo/qwt/qwt_painter.cpp

1299 lines
35 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/* -*- 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
*****************************************************************************/
#include "qwt_painter.h"
#include "qwt_math.h"
#include "qwt_clipper.h"
#include "qwt_color_map.h"
#include "qwt_scale_map.h"
#include <qwindowdefs.h>
#include <qwidget.h>
#include <qframe.h>
#include <qrect.h>
#include <qpainter.h>
#include <qpalette.h>
#include <qpaintdevice.h>
#include <qpixmap.h>
#include <qstyle.h>
#include <qtextdocument.h>
#include <qabstracttextdocumentlayout.h>
#include <qstyleoption.h>
#include <qpaintengine.h>
#include <qapplication.h>
#include <qdesktopwidget.h>
#if QT_VERSION >= 0x050000
#include <qwindow.h>
#endif
#if QT_VERSION < 0x050000
#ifdef Q_WS_X11
#include <qx11info_x11.h>
#endif
#endif
bool QwtPainter::d_polylineSplitting = true;
bool QwtPainter::d_roundingAlignment = true;
static inline bool qwtIsClippingNeeded(
const QPainter *painter, QRectF &clipRect )
{
bool doClipping = false;
const QPaintEngine *pe = painter->paintEngine();
if ( pe && pe->type() == QPaintEngine::SVG )
{
// The SVG paint engine ignores any clipping,
if ( painter->hasClipping() )
{
doClipping = true;
clipRect = painter->clipRegion().boundingRect();
}
}
return doClipping;
}
template <class T>
static inline void qwtDrawPolyline( QPainter *painter,
const T *points, int pointCount, bool polylineSplitting )
{
bool doSplit = false;
if ( polylineSplitting )
{
const QPaintEngine *pe = painter->paintEngine();
if ( pe && pe->type() == QPaintEngine::Raster )
{
/*
The raster paint engine seems to use some algo with O(n*n).
( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
To work around this problem, we have to split the polygon into
smaller pieces.
*/
doSplit = true;
}
}
if ( doSplit )
{
const int splitSize = 6;
for ( int i = 0; i < pointCount; i += splitSize )
{
const int n = qMin( splitSize + 1, pointCount - i );
painter->drawPolyline( points + i, n );
}
}
else
{
painter->drawPolyline( points, pointCount );
}
}
static inline QSize qwtScreenResolution()
{
static QSize screenResolution;
if ( !screenResolution.isValid() )
{
QDesktopWidget *desktop = QApplication::desktop();
if ( desktop )
{
screenResolution.setWidth( desktop->logicalDpiX() );
screenResolution.setHeight( desktop->logicalDpiY() );
}
}
return screenResolution;
}
static inline void qwtUnscaleFont( QPainter *painter )
{
if ( painter->font().pixelSize() >= 0 )
return;
const QSize screenResolution = qwtScreenResolution();
const QPaintDevice *pd = painter->device();
if ( pd->logicalDpiX() != screenResolution.width() ||
pd->logicalDpiY() != screenResolution.height() )
{
QFont pixelFont( painter->font(), QApplication::desktop() );
pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
painter->setFont( pixelFont );
}
}
/*!
Check is the application is running with the X11 graphics system
that has some special capabilities that can be used for incremental
painting to a widget.
\return True, when the graphics system is X11
*/
bool QwtPainter::isX11GraphicsSystem()
{
static int onX11 = -1;
if ( onX11 < 0 )
{
QPixmap pm( 1, 1 );
QPainter painter( &pm );
onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0;
}
return onX11 == 1;
}
/*!
Check if the painter is using a paint engine, that aligns
coordinates to integers. Today these are all paint engines
beside QPaintEngine::Pdf and QPaintEngine::SVG.
If we have an integer based paint engine it is also
checked if the painter has a transformation matrix,
that rotates or scales.
\param painter Painter
\return true, when the painter is aligning
\sa setRoundingAlignment()
*/
bool QwtPainter::isAligning( QPainter *painter )
{
if ( painter && painter->isActive() )
{
switch ( painter->paintEngine()->type() )
{
case QPaintEngine::Pdf:
case QPaintEngine::SVG:
return false;
default:;
}
const QTransform tr = painter->transform();
if ( tr.isRotating() || tr.isScaling() )
{
// we might have to check translations too
return false;
}
}
return true;
}
/*!
Enable whether coordinates should be rounded, before they are painted
to a paint engine that floors to integer values. For other paint engines
( PDF, SVG ) this flag has no effect.
QwtPainter stores this flag only, the rounding itself is done in
the painting code ( f.e the plot items ).
The default setting is true.
\sa roundingAlignment(), isAligning()
*/
void QwtPainter::setRoundingAlignment( bool enable )
{
d_roundingAlignment = enable;
}
/*!
\brief En/Disable line splitting for the raster paint engine
In some Qt versions the raster paint engine paints polylines of many points
much faster when they are split in smaller chunks: f.e all supported Qt versions
>= Qt 5.0 when drawing an antialiased polyline with a pen width >=2.
The default setting is true.
\sa polylineSplitting()
*/
void QwtPainter::setPolylineSplitting( bool enable )
{
d_polylineSplitting = enable;
}
//! Wrapper for QPainter::drawPath()
void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
{
painter->drawPath( path );
}
//! Wrapper for QPainter::drawRect()
void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
{
drawRect( painter, QRectF( x, y, w, h ) );
}
//! Wrapper for QPainter::drawRect()
void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
{
const QRectF r = rect;
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping )
{
if ( !clipRect.intersects( r ) )
return;
if ( !clipRect.contains( r ) )
{
fillRect( painter, r & clipRect, painter->brush() );
painter->save();
painter->setBrush( Qt::NoBrush );
drawPolyline( painter, QPolygonF( r ) );
painter->restore();
return;
}
}
painter->drawRect( r );
}
//! Wrapper for QPainter::fillRect()
void QwtPainter::fillRect( QPainter *painter,
const QRectF &rect, const QBrush &brush )
{
if ( !rect.isValid() )
return;
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
/*
Performance of Qt4 is horrible for a non trivial brush. Without
clipping expect minutes or hours for repainting large rectangles
(might result from zooming)
*/
if ( deviceClipping )
clipRect &= painter->window();
else
clipRect = painter->window();
if ( painter->hasClipping() )
clipRect &= painter->clipRegion().boundingRect();
QRectF r = rect;
if ( deviceClipping )
r = r.intersected( clipRect );
if ( r.isValid() )
painter->fillRect( r, brush );
}
//! Wrapper for QPainter::drawPie()
void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
int a, int alen )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping && !clipRect.contains( rect ) )
return;
painter->drawPie( rect, a, alen );
}
//! Wrapper for QPainter::drawEllipse()
void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping && !clipRect.contains( rect ) )
return;
painter->drawEllipse( rect );
}
//! Wrapper for QPainter::drawText()
void QwtPainter::drawText( QPainter *painter, double x, double y,
const QString &text )
{
drawText( painter, QPointF( x, y ), text );
}
//! Wrapper for QPainter::drawText()
void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
const QString &text )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping && !clipRect.contains( pos ) )
return;
painter->save();
qwtUnscaleFont( painter );
painter->drawText( pos, text );
painter->restore();
}
//! Wrapper for QPainter::drawText()
void QwtPainter::drawText( QPainter *painter,
double x, double y, double w, double h,
int flags, const QString &text )
{
drawText( painter, QRectF( x, y, w, h ), flags, text );
}
//! Wrapper for QPainter::drawText()
void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
int flags, const QString &text )
{
painter->save();
qwtUnscaleFont( painter );
painter->drawText( rect, flags, text );
painter->restore();
}
#ifndef QT_NO_RICHTEXT
/*!
Draw a text document into a rectangle
\param painter Painter
\param rect Traget rectangle
\param flags Alignments/Text flags, see QPainter::drawText()
\param text Text document
*/
void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
int flags, const QTextDocument &text )
{
QTextDocument *txt = text.clone();
painter->save();
QRectF unscaledRect = rect;
if ( painter->font().pixelSize() < 0 )
{
const QSize res = qwtScreenResolution();
const QPaintDevice *pd = painter->device();
if ( pd->logicalDpiX() != res.width() ||
pd->logicalDpiY() != res.height() )
{
QTransform transform;
transform.scale( res.width() / double( pd->logicalDpiX() ),
res.height() / double( pd->logicalDpiY() ));
painter->setWorldTransform( transform, true );
unscaledRect = transform.inverted().mapRect(rect);
}
}
txt->setDefaultFont( painter->font() );
txt->setPageSize( QSizeF( unscaledRect.width(), QWIDGETSIZE_MAX ) );
QAbstractTextDocumentLayout* layout = txt->documentLayout();
const double height = layout->documentSize().height();
double y = unscaledRect.y();
if ( flags & Qt::AlignBottom )
y += ( unscaledRect.height() - height );
else if ( flags & Qt::AlignVCenter )
y += ( unscaledRect.height() - height ) / 2;
QAbstractTextDocumentLayout::PaintContext context;
context.palette.setColor( QPalette::Text, painter->pen().color() );
painter->translate( unscaledRect.x(), y );
layout->draw( painter, context );
painter->restore();
delete txt;
}
#endif // !QT_NO_RICHTEXT
//! Wrapper for QPainter::drawLine()
void QwtPainter::drawLine( QPainter *painter,
const QPointF &p1, const QPointF &p2 )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping &&
!( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
{
QPolygonF polygon;
polygon += p1;
polygon += p2;
drawPolyline( painter, polygon );
return;
}
painter->drawLine( p1, p2 );
}
//! Wrapper for QPainter::drawPolygon()
void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
QPolygonF cpa = polygon;
if ( deviceClipping )
cpa = QwtClipper::clipPolygonF( clipRect, polygon );
painter->drawPolygon( cpa );
}
//! Wrapper for QPainter::drawPolyline()
void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
QPolygonF cpa = polygon;
if ( deviceClipping )
cpa = QwtClipper::clipPolygonF( clipRect, cpa );
qwtDrawPolyline<QPointF>( painter,
cpa.constData(), cpa.size(), d_polylineSplitting );
}
//! Wrapper for QPainter::drawPolyline()
void QwtPainter::drawPolyline( QPainter *painter,
const QPointF *points, int pointCount )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping )
{
QPolygonF polygon( pointCount );
::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) );
polygon = QwtClipper::clipPolygonF( clipRect, polygon );
qwtDrawPolyline<QPointF>( painter,
polygon.constData(), polygon.size(), d_polylineSplitting );
}
else
{
qwtDrawPolyline<QPointF>( painter, points, pointCount, d_polylineSplitting );
}
}
//! Wrapper for QPainter::drawPolygon()
void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
QPolygon cpa = polygon;
if ( deviceClipping )
cpa = QwtClipper::clipPolygon( clipRect, polygon );
painter->drawPolygon( cpa );
}
//! Wrapper for QPainter::drawPolyline()
void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
QPolygon cpa = polygon;
if ( deviceClipping )
cpa = QwtClipper::clipPolygon( clipRect, cpa );
qwtDrawPolyline<QPoint>( painter,
cpa.constData(), cpa.size(), d_polylineSplitting );
}
//! Wrapper for QPainter::drawPolyline()
void QwtPainter::drawPolyline( QPainter *painter,
const QPoint *points, int pointCount )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping )
{
QPolygon polygon( pointCount );
::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) );
polygon = QwtClipper::clipPolygon( clipRect, polygon );
qwtDrawPolyline<QPoint>( painter,
polygon.constData(), polygon.size(), d_polylineSplitting );
}
else
qwtDrawPolyline<QPoint>( painter, points, pointCount, d_polylineSplitting );
}
//! Wrapper for QPainter::drawPoint()
void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping && !clipRect.contains( pos ) )
return;
painter->drawPoint( pos );
}
//! Wrapper for QPainter::drawPoint()
void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping )
{
const int minX = qCeil( clipRect.left() );
const int maxX = qFloor( clipRect.right() );
const int minY = qCeil( clipRect.top() );
const int maxY = qFloor( clipRect.bottom() );
if ( pos.x() < minX || pos.x() > maxX
|| pos.y() < minY || pos.y() > maxY )
{
return;
}
}
painter->drawPoint( pos );
}
//! Wrapper for QPainter::drawPoints()
void QwtPainter::drawPoints( QPainter *painter,
const QPoint *points, int pointCount )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping )
{
const int minX = qCeil( clipRect.left() );
const int maxX = qFloor( clipRect.right() );
const int minY = qCeil( clipRect.top() );
const int maxY = qFloor( clipRect.bottom() );
const QRect r( minX, minY, maxX - minX, maxY - minY );
QPolygon clippedPolygon( pointCount );
QPoint *clippedData = clippedPolygon.data();
int numClippedPoints = 0;
for ( int i = 0; i < pointCount; i++ )
{
if ( r.contains( points[i] ) )
clippedData[ numClippedPoints++ ] = points[i];
}
painter->drawPoints( clippedData, numClippedPoints );
}
else
{
painter->drawPoints( points, pointCount );
}
}
//! Wrapper for QPainter::drawPoints()
void QwtPainter::drawPoints( QPainter *painter,
const QPointF *points, int pointCount )
{
QRectF clipRect;
const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
if ( deviceClipping )
{
QPolygonF clippedPolygon( pointCount );
QPointF *clippedData = clippedPolygon.data();
int numClippedPoints = 0;
for ( int i = 0; i < pointCount; i++ )
{
if ( clipRect.contains( points[i] ) )
clippedData[ numClippedPoints++ ] = points[i];
}
painter->drawPoints( clippedData, numClippedPoints );
}
else
{
painter->drawPoints( points, pointCount );
}
}
//! Wrapper for QPainter::drawImage()
void QwtPainter::drawImage( QPainter *painter,
const QRectF &rect, const QImage &image )
{
const QRect alignedRect = rect.toAlignedRect();
if ( alignedRect != rect )
{
const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
painter->save();
painter->setClipRect( clipRect, Qt::IntersectClip );
painter->drawImage( alignedRect, image );
painter->restore();
}
else
{
painter->drawImage( alignedRect, image );
}
}
//! Wrapper for QPainter::drawPixmap()
void QwtPainter::drawPixmap( QPainter *painter,
const QRectF &rect, const QPixmap &pixmap )
{
const QRect alignedRect = rect.toAlignedRect();
if ( alignedRect != rect )
{
const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
painter->save();
painter->setClipRect( clipRect, Qt::IntersectClip );
painter->drawPixmap( alignedRect, pixmap );
painter->restore();
}
else
{
painter->drawPixmap( alignedRect, pixmap );
}
}
//! Draw a focus rectangle on a widget using its style.
void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget )
{
drawFocusRect( painter, widget, widget->rect() );
}
//! Draw a focus rectangle on a widget using its style.
void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget,
const QRect &rect )
{
QStyleOptionFocusRect opt;
opt.init( widget );
opt.rect = rect;
opt.state |= QStyle::State_HasFocus;
widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
&opt, painter, widget );
}
/*!
Draw a round frame
\param painter Painter
\param rect Frame rectangle
\param palette QPalette::WindowText is used for plain borders
QPalette::Dark and QPalette::Light for raised
or sunken borders
\param lineWidth Line width
\param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
*/
void QwtPainter::drawRoundFrame( QPainter *painter,
const QRectF &rect, const QPalette &palette,
int lineWidth, int frameStyle )
{
enum Style
{
Plain,
Sunken,
Raised
};
Style style = Plain;
if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
style = Sunken;
else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
style = Raised;
const double lw2 = 0.5 * lineWidth;
QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
QBrush brush;
if ( style != Plain )
{
QColor c1 = palette.color( QPalette::Light );
QColor c2 = palette.color( QPalette::Dark );
if ( style == Sunken )
qSwap( c1, c2 );
QLinearGradient gradient( r.topLeft(), r.bottomRight() );
gradient.setColorAt( 0.0, c1 );
#if 0
gradient.setColorAt( 0.3, c1 );
gradient.setColorAt( 0.7, c2 );
#endif
gradient.setColorAt( 1.0, c2 );
brush = QBrush( gradient );
}
else // Plain
{
brush = palette.brush( QPalette::WindowText );
}
painter->save();
painter->setPen( QPen( brush, lineWidth ) );
painter->setBrush( Qt::NoBrush );
painter->drawEllipse( r );
painter->restore();
}
/*!
Draw a rectangular frame
\param painter Painter
\param rect Frame rectangle
\param palette Palette
\param foregroundRole Foreground role used for QFrame::Plain
\param frameWidth Frame width
\param midLineWidth Used for QFrame::Box
\param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
*/
void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect,
const QPalette &palette, QPalette::ColorRole foregroundRole,
int frameWidth, int midLineWidth, int frameStyle )
{
if ( frameWidth <= 0 || rect.isEmpty() )
return;
const int shadow = frameStyle & QFrame::Shadow_Mask;
painter->save();
if ( shadow == QFrame::Plain )
{
const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
const QRectF innerRect = outerRect.adjusted(
frameWidth, frameWidth, -frameWidth, -frameWidth );
QPainterPath path;
path.addRect( outerRect );
path.addRect( innerRect );
painter->setPen( Qt::NoPen );
painter->setBrush( palette.color( foregroundRole ) );
painter->drawPath( path );
}
else
{
const int shape = frameStyle & QFrame::Shape_Mask;
if ( shape == QFrame::Box )
{
const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
const QRectF midRect1 = outerRect.adjusted(
frameWidth, frameWidth, -frameWidth, -frameWidth );
const QRectF midRect2 = midRect1.adjusted(
midLineWidth, midLineWidth, -midLineWidth, -midLineWidth );
const QRectF innerRect = midRect2.adjusted(
frameWidth, frameWidth, -frameWidth, -frameWidth );
QPainterPath path1;
path1.moveTo( outerRect.bottomLeft() );
path1.lineTo( outerRect.topLeft() );
path1.lineTo( outerRect.topRight() );
path1.lineTo( midRect1.topRight() );
path1.lineTo( midRect1.topLeft() );
path1.lineTo( midRect1.bottomLeft() );
QPainterPath path2;
path2.moveTo( outerRect.bottomLeft() );
path2.lineTo( outerRect.bottomRight() );
path2.lineTo( outerRect.topRight() );
path2.lineTo( midRect1.topRight() );
path2.lineTo( midRect1.bottomRight() );
path2.lineTo( midRect1.bottomLeft() );
QPainterPath path3;
path3.moveTo( midRect2.bottomLeft() );
path3.lineTo( midRect2.topLeft() );
path3.lineTo( midRect2.topRight() );
path3.lineTo( innerRect.topRight() );
path3.lineTo( innerRect.topLeft() );
path3.lineTo( innerRect.bottomLeft() );
QPainterPath path4;
path4.moveTo( midRect2.bottomLeft() );
path4.lineTo( midRect2.bottomRight() );
path4.lineTo( midRect2.topRight() );
path4.lineTo( innerRect.topRight() );
path4.lineTo( innerRect.bottomRight() );
path4.lineTo( innerRect.bottomLeft() );
QPainterPath path5;
path5.addRect( midRect1 );
path5.addRect( midRect2 );
painter->setPen( Qt::NoPen );
QBrush brush1 = palette.dark().color();
QBrush brush2 = palette.light().color();
if ( shadow == QFrame::Raised )
qSwap( brush1, brush2 );
painter->setBrush( brush1 );
painter->drawPath( path1 );
painter->drawPath( path4 );
painter->setBrush( brush2 );
painter->drawPath( path2 );
painter->drawPath( path3 );
painter->setBrush( palette.mid() );
painter->drawPath( path5 );
}
#if 0
// qDrawWinPanel doesn't result in something nice
// on a scalable document like PDF. Better draw a
// Panel.
else if ( shape == QFrame::WinPanel )
{
painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true );
qDrawWinPanel ( painter, rect.toRect(), palette,
frameStyle & QFrame::Sunken );
}
else if ( shape == QFrame::StyledPanel )
{
}
#endif
else
{
const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
const QRectF innerRect = outerRect.adjusted(
frameWidth - 1.0, frameWidth - 1.0,
-( frameWidth - 1.0 ), -( frameWidth - 1.0 ) );
QPainterPath path1;
path1.moveTo( outerRect.bottomLeft() );
path1.lineTo( outerRect.topLeft() );
path1.lineTo( outerRect.topRight() );
path1.lineTo( innerRect.topRight() );
path1.lineTo( innerRect.topLeft() );
path1.lineTo( innerRect.bottomLeft() );
QPainterPath path2;
path2.moveTo( outerRect.bottomLeft() );
path2.lineTo( outerRect.bottomRight() );
path2.lineTo( outerRect.topRight() );
path2.lineTo( innerRect.topRight() );
path2.lineTo( innerRect.bottomRight() );
path2.lineTo( innerRect.bottomLeft() );
painter->setPen( Qt::NoPen );
QBrush brush1 = palette.dark().color();
QBrush brush2 = palette.light().color();
if ( shadow == QFrame::Raised )
qSwap( brush1, brush2 );
painter->setBrush( brush1 );
painter->drawPath( path1 );
painter->setBrush( brush2 );
painter->drawPath( path2 );
}
}
painter->restore();
}
/*!
Draw a rectangular frame with rounded borders
\param painter Painter
\param rect Frame rectangle
\param xRadius x-radius of the ellipses defining the corners
\param yRadius y-radius of the ellipses defining the corners
\param palette QPalette::WindowText is used for plain borders
QPalette::Dark and QPalette::Light for raised
or sunken borders
\param lineWidth Line width
\param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
*/
void QwtPainter::drawRoundedFrame( QPainter *painter,
const QRectF &rect, double xRadius, double yRadius,
const QPalette &palette, int lineWidth, int frameStyle )
{
painter->save();
painter->setRenderHint( QPainter::Antialiasing, true );
painter->setBrush( Qt::NoBrush );
double lw2 = lineWidth * 0.5;
QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
QPainterPath path;
path.addRoundedRect( r, xRadius, yRadius );
enum Style
{
Plain,
Sunken,
Raised
};
Style style = Plain;
if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
style = Sunken;
else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
style = Raised;
if ( style != Plain && path.elementCount() == 17 )
{
// move + 4 * ( cubicTo + lineTo )
QPainterPath pathList[8];
for ( int i = 0; i < 4; i++ )
{
const int j = i * 4 + 1;
pathList[ 2 * i ].moveTo(
path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
);
pathList[ 2 * i ].cubicTo(
path.elementAt(j + 0).x, path.elementAt(j + 0).y,
path.elementAt(j + 1).x, path.elementAt(j + 1).y,
path.elementAt(j + 2).x, path.elementAt(j + 2).y );
pathList[ 2 * i + 1 ].moveTo(
path.elementAt(j + 2).x, path.elementAt(j + 2).y
);
pathList[ 2 * i + 1 ].lineTo(
path.elementAt(j + 3).x, path.elementAt(j + 3).y
);
}
QColor c1( palette.color( QPalette::Dark ) );
QColor c2( palette.color( QPalette::Light ) );
if ( style == Raised )
qSwap( c1, c2 );
for ( int i = 0; i < 4; i++ )
{
QRectF r = pathList[2 * i].controlPointRect();
QPen arcPen;
arcPen.setCapStyle( Qt::FlatCap );
arcPen.setWidth( lineWidth );
QPen linePen;
linePen.setCapStyle( Qt::FlatCap );
linePen.setWidth( lineWidth );
switch( i )
{
case 0:
{
arcPen.setColor( c1 );
linePen.setColor( c1 );
break;
}
case 1:
{
QLinearGradient gradient;
gradient.setStart( r.topLeft() );
gradient.setFinalStop( r.bottomRight() );
gradient.setColorAt( 0.0, c1 );
gradient.setColorAt( 1.0, c2 );
arcPen.setBrush( gradient );
linePen.setColor( c2 );
break;
}
case 2:
{
arcPen.setColor( c2 );
linePen.setColor( c2 );
break;
}
case 3:
{
QLinearGradient gradient;
gradient.setStart( r.bottomRight() );
gradient.setFinalStop( r.topLeft() );
gradient.setColorAt( 0.0, c2 );
gradient.setColorAt( 1.0, c1 );
arcPen.setBrush( gradient );
linePen.setColor( c1 );
break;
}
}
painter->setPen( arcPen );
painter->drawPath( pathList[ 2 * i] );
painter->setPen( linePen );
painter->drawPath( pathList[ 2 * i + 1] );
}
}
else
{
QPen pen( palette.color( QPalette::WindowText ), lineWidth );
painter->setPen( pen );
painter->drawPath( path );
}
painter->restore();
}
/*!
Draw a color bar into a rectangle
\param painter Painter
\param colorMap Color map
\param interval Value range
\param scaleMap Scale map
\param orientation Orientation
\param rect Traget rectangle
*/
void QwtPainter::drawColorBar( QPainter *painter,
const QwtColorMap &colorMap, const QwtInterval &interval,
const QwtScaleMap &scaleMap, Qt::Orientation orientation,
const QRectF &rect )
{
QVector<QRgb> colorTable;
if ( colorMap.format() == QwtColorMap::Indexed )
colorTable = colorMap.colorTable( interval );
QColor c;
const QRect devRect = rect.toAlignedRect();
/*
We paint to a pixmap first to have something scalable for printing
( f.e. in a Pdf document )
*/
QPixmap pixmap( devRect.size() );
pixmap.fill( Qt::transparent );
QPainter pmPainter( &pixmap );
pmPainter.translate( -devRect.x(), -devRect.y() );
if ( orientation == Qt::Horizontal )
{
QwtScaleMap sMap = scaleMap;
sMap.setPaintInterval( rect.left(), rect.right() );
for ( int x = devRect.left(); x <= devRect.right(); x++ )
{
const double value = sMap.invTransform( x );
if ( colorMap.format() == QwtColorMap::RGB )
c.setRgba( colorMap.rgb( interval, value ) );
else
c = colorTable[colorMap.colorIndex( interval, value )];
pmPainter.setPen( c );
pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
}
}
else // Vertical
{
QwtScaleMap sMap = scaleMap;
sMap.setPaintInterval( rect.bottom(), rect.top() );
for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
{
const double value = sMap.invTransform( y );
if ( colorMap.format() == QwtColorMap::RGB )
c.setRgba( colorMap.rgb( interval, value ) );
else
c = colorTable[colorMap.colorIndex( interval, value )];
pmPainter.setPen( c );
pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
}
}
pmPainter.end();
drawPixmap( painter, rect, pixmap );
}
static inline void qwtFillRect( const QWidget *widget, QPainter *painter,
const QRect &rect, const QBrush &brush)
{
if ( brush.style() == Qt::TexturePattern )
{
painter->save();
painter->setClipRect( rect );
painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
painter->restore();
}
else if ( brush.gradient() )
{
painter->save();
painter->setClipRect( rect );
painter->fillRect(0, 0, widget->width(),
widget->height(), brush);
painter->restore();
}
else
{
painter->fillRect(rect, brush);
}
}
/*!
Fill a pixmap with the content of a widget
In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy
for backgrounds with gradients. Thus fillPixmap() offers
an alternative implementation.
\param widget Widget
\param pixmap Pixmap to be filled
\param offset Offset
\sa QPixmap::fill()
*/
void QwtPainter::fillPixmap( const QWidget *widget,
QPixmap &pixmap, const QPoint &offset )
{
const QRect rect( offset, pixmap.size() );
QPainter painter( &pixmap );
painter.translate( -offset );
const QBrush autoFillBrush =
widget->palette().brush( widget->backgroundRole() );
if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) )
{
const QBrush bg = widget->palette().brush( QPalette::Window );
qwtFillRect( widget, &painter, rect, bg);
}
if ( widget->autoFillBackground() )
qwtFillRect( widget, &painter, rect, autoFillBrush);
if ( widget->testAttribute(Qt::WA_StyledBackground) )
{
painter.setClipRegion( rect );
QStyleOption opt;
opt.initFrom( widget );
widget->style()->drawPrimitive( QStyle::PE_Widget,
&opt, &painter, widget );
}
}
/*!
Fill rect with the background of a widget
\param painter Painter
\param rect Rectangle to be filled
\param widget Widget
\sa QStyle::PE_Widget, QWidget::backgroundRole()
*/
void QwtPainter::drawBackgound( QPainter *painter,
const QRectF &rect, const QWidget *widget )
{
if ( widget->testAttribute( Qt::WA_StyledBackground ) )
{
QStyleOption opt;
opt.initFrom( widget );
opt.rect = rect.toAlignedRect();
widget->style()->drawPrimitive(
QStyle::PE_Widget, &opt, painter, widget);
}
else
{
const QBrush brush =
widget->palette().brush( widget->backgroundRole() );
painter->fillRect( rect, brush );
}
}
/*!
\return A pixmap that can be used as backing store
\param widget Widget, for which the backinstore is intended
\param size Size of the pixmap
*/
QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size )
{
QPixmap pm;
#define QWT_HIGH_DPI 1
#if QT_VERSION >= 0x050000 && QWT_HIGH_DPI
qreal pixelRatio = 1.0;
if ( widget && widget->windowHandle() )
{
#if QT_VERSION < 0x050100
pixelRatio = widget->windowHandle()->devicePixelRatio();
#else
pixelRatio = widget->devicePixelRatio();
#endif
}
else
{
if ( qApp )
pixelRatio = qApp->devicePixelRatio();
}
pm = QPixmap( size * pixelRatio );
pm.setDevicePixelRatio( pixelRatio );
#else
Q_UNUSED( widget )
pm = QPixmap( size );
#endif
#if QT_VERSION < 0x050000
#ifdef Q_WS_X11
if ( widget && isX11GraphicsSystem() )
{
if ( pm.x11Info().screen() != widget->x11Info().screen() )
pm.x11SetScreen( widget->x11Info().screen() );
}
#endif
#endif
return pm;
}