Development/Tutorials/KPlotWidget

From KDE TechBase
Revision as of 20:52, 29 June 2011 by Neverendingo (talk | contribs) (Text replace - "</code>" to "</syntaxhighlight>")

Introduction and Scope

KPlotWidget is used for easily creating 2-D plots of data sets, complete with plot axes that include labeled tickmarks and optional axis labels. Data sets can be represented as any combination of: discrete points (using a variety of point shapes), connected line segments (using any QPen), or bar-graph rectangles (using any QPen/QBrush). In addition, any data point can have a label string attached to it.

Here is a simple example showing sine and cosine curves:

This example demonstrates most of the commonly-used features of KPlotWidget. It is included in KDE, under kdelibs/kdeui/tests. The first part of this tutorial will explain how to produce this plot using KPlotWidget.

Basic Usage

The data to be plotted in KPlotWidget are stored as a list of KPlotObjects. Each KPlotObject itself consists of a list of KPlotPoints (which store the coordinates of each data point, as well as an optional text label), and methods to modify the parameters which control how they are drawn in the plot. KPlotWidget also contains four KPlotAxis objects, which encapsulate the plot axes, tickmarks, tickmark labels, and axis labels along the four edges bounding the plot.

KPlotWidget takes care of much of the plot rendering automatically, so simple data plots can be done with just a few lines of code:

  • The area shown in the plot is determined from the range of the data in the KPlotObjects
  • Tickmark positions are optimally computed, and synced between the top/bottom and left/right axes
  • Tickmark labels are placed automatically along the left and bottom axes
  • The padding area outside the axes is adjusted according to whether tickmark labels and/or axis labels are being drawn
  • Data points are automatically transformed from their natural units to screen-pixel coordinates. In most cases, you will not have to care about pixel coordinates at all.

Basic usage of KPlotWidget is illustrated with the following example:

// Create a new KPlotWidget, provide a minimum size, 
// and activate antialiased drawing:
KPlotWidget *plot = new KPlotWidget( parent );
plot->setMinimumSize( 400, 400 );
plot->setAntialiasing( true );

// Set the boundaries of the plot to display the full extent of the sine curve:
plot->setLimits( -0.1, 6.38, -1.1, 1.1 );

// Create a KPlotObject which will display data 
// points by connecting them with red line segments
// with a width of 2 pixels:
KPlotObject *sineobj = new KPlotObject( Qt::red, KPlotObject::Lines, 2 );

// Add data points to the KPlotObject for a 
// sine curve (these are stored internally 
// as KPlotPoints):
for ( float t=0.0; t<=6.28; t+=0.04 ) 
    sineobj->addPoint( t, sin(t) );

// Add the KPlotObject to the plot:
plot->addPlotObject( sineobj );

// Add a text label to the bottom axis:
plot->axis( KPlotWidget::BottomAxis )->setLabel( i18n("Angle in radians") );

plot->update();

Rendering Options for KPlotObjects

The data points in a KPlotObject can be rendered in a number of ways: as discrete points, connected line segments, or bar-graph style rectangles. In fact, the same KPlotObject can be rendered in any combination of these styles; here is an example of a KPlotObject which will be rendered with all three styles:

po = new KPlotObject( Qt::white, KPlotObject::Points, 10, KPlotObject::Pentagon );
po->setShowLines( true );
po->setShowBars( true );

Note that when constructing a KPlotObject which will be rendered as Points, the third argument is the size of the points in pixels, and the fourth argument describes the shape to use when rendering each point. The options are:

  • Circle
  • Triangle
  • Square
  • Pentagon
  • Hexagon
  • Asterisk
  • Star

By default, the same QPen and QBrush will be used to render the Points, Lines, and Bars. The color of the default pen and brush are set in the constructor. However, it is easy to provide custom pens and brushes for the different plot styles:

// Modify the pen used to draw Lines connecting 
// data points in this KPlotObject:
po->setLinePen( QPen( Qt::red, 3.0, Qt::DashDotLine ) );

// Modify the brush used to fill Bars in this 
// KPlotObject:
po->setBarBrush( QBrush( Qt::blue, Qt::BDiagPattern ) );

Traversing All Data Points

Both KPlotWidget and KPlotObject provide functions that return the list of KPlotObjects and KPlotPoints that they contain, so if one needed to access all of the data points in the plot, it can be done like this:

// "plot" is a KPlotWidget containing several KPlotObject data sets,
// each of which consists of several  KPlotPoints
foreach( KPlotObject *po, plot->plotObjects() ) {
    foreach( KPlotPoint *p, po->points() ) {
        // Do stuff to each KPlotPoint
    }
}

Labeling Data Points

Text labels can be attached to any point in a KPlotObject. The easiest way to accomplish this is to add the label text as the second argument to KPlotObject::addPoint( const QPointF&, const QString& ). There is also a KPlotPoint::setLabel( const QString& ) function, if you need to add a text label after the point has already been created.

Note that KPlotWidget will automatically try to place the labels intelligently, so that each label is close to its point without overlapping other labels or plot elements. If a label needs to be placed at some distance from its point, then the label and point will be connected with a line segment.

Customizing the plot

The basic usage outlined above will usually suffice for simple data plots. However, the time may come when you need more flexibility. Many aspects of the plot rendering which are normally done automatically can be manually overidden.

Custom Colors for the Plot Area

Besides setting the colors of KPlotObjects (using KPlotObject::setPen(), setLinePen(), setBrush(), etc), you can also customize three color elements for the plot as a whole: the background, foreground and grid colors. The functions for this are:

KPlotWidget::setBackgroundColor( const QColor& );
KPlotWidget::setForegroundColor( const QColor& );
KPlotWidget::setGridColor( const QColor& );

The background color is self-explanatory; its default is black.
The foreground color is used for drawing the axes and their labels; its default is white.
The grid color is used for drawing the optional grid on the plot (see the next section in this tutorial); its default is grey.

Showing a grid

If you would like your plot to include a grid overlay, simply add:

plot->setShowGrid(true)

The grid lines are placed at the positions of major tickmarks along the Bottom and Left axes.

The Padding Area

By default, KPlotWidget will automatically compute how much padding is needed outside of each Axis, to provide room for tickmark labels and/or the axis label, if they are present. If you need to override these calculations, the padding on each axis can be set manually with the functions setTopPadding(int), setBottomPadding(int), setLeftPadding(int), or setRightPadding(int), where the argument is the size of the padding in pixels.

If you need to restore the automatic calculation of the axis padding, you can set the padding of any axis to -1, or use setDefaultPaddings() to restore all four axes at once.

Providing Secondary Data Limits

By default, the top and right axes show the same tickmarks as the bottom and left axes. However, it is possible to define secondary data limits for your data set which will be displayed on the top and right axes.

Why would one want to do this? One possibility is shown in the example image at the beginning of this tutorial: you may want to express the data using two different units. In the case of the example, we are plotting angles on the x-axis; the angles are shown in both degrees (top axis) and in radians (bottom axis).

In any case, bear in mind that the secondary data limits are not used for anything other than rendering the top and right axes; all internal calculations use the primary data limits only. Also, no check is made to make sure that the secondary limits are actually mapped correctly to the primary limits.

The secondary data limits are stored internally as a QRectF, just like the primary limits. To set the secondary limits, so something like:

plot->setSecondaryLimits( -5.73, 365.55, -1.1, 1.1 );
plot->axis(KPlotWidget::TopAxis)->setTickLabelsShown( true );
plot->axis(KPlotWidget::TopAxis)->setLabel("Angle [degrees]")

The first command tells the top axis to cover values -5.73 to 365.55, and the right axis to cover values -1.1 to 1.1. The second command tells the plot to draw tick labels along the top axis, and the third command adds an axis label to the top axis.