22.01.2011

für CorePlot 0.2.2, Xcode 3.2

Tutorial: CorePlot Balkendiagramm

Im Folgenden wird erklärt, wie man mit dem Framework CorePlot ein einfaches Balkendiagramm auf dem Mac erstellt.

Als erstes muß ein neues Projekt in Xcode angelegt werden. Als Typ wird dabei eine Quartz Composer Application gewählt. Der Grund für die Wahl dieses Typs ist, daß auf diese Weise bereits eine XIB-Datei angelegt wird und alle benötigten Frameworks im Projekt enthalten sind. Im einzelnen sind das: Quartz.framework, Cocoa.framework, AppKit.framework, Foundation.framework und CoreData.framework. Wobei das CoreData.framework in dem Beispiel nicht verwendet wird.

Als Name wird bei dem Beispiel CorePlotBalkenDiagramm gewählt. Das neue Projekt sollte dann ungefähr so aussehen:

Als erstes sollte das CorePlot.Framework hinzugefügt werden. Dafür muß unter Groups&Files der Ordner Frameworks selektiert werden. Nach einem Rechtsklick, kann im Popup-Menü Add->Existing Framework ausgewählt werden. In dem Auswahldialog wird das CorePlot.framework mit Add hinzufügt.

Wenn es dort nicht erscheint, muß das Binary des Frameworks an die richtige Stelle kopiert werden. Das ist hier unter Punkt 2 beschrieben.

In der Datei AppController.h müssen die folgenden zwei Zeilen entfernt werden:

IBOutlet QCView* qcView;

- (IBAction) changeColorToBlue:(id)sender;
  

Die QCView und die Methode wird durch diesen Quellcode ersetzt:

#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
#import <CorePlot/CorePlot.h>

@interface AppController : NSObject <CPPlotDataSource>
{
    IBOutlet CPLayerHostingView *view;
    CPXYGraph *graph;
    NSArray *data;
}
@end

Zuerst wird die Importanweisung des benötigten Headers von CorePlot hinzugefügt. Außerdem wird ein IBOutlet vom Typ CPLayerHostingView definiert, in dem das Balkendiagramm später angezeigt wird. Der Graph mit den Balken ist vom Typ CPXYGraph. Das NSArray wird dazu verwendet, um die Daten zu speichern, die angezeigt werden sollen. Außerdem muß noch das Protokoll CPPlotDataSource für die DataSource angegeben werden.

In der Datei AppController.m müssen auch noch einige Zeilen gelöscht werden. Nach dem Löschen, sollte die Datei folgendermaßen aussehen:

#import "AppController.h"

@implementation AppController

- (void) awakeFromNib
{

}

- (void)windowWillClose:(NSNotification *)notification
{
	[NSApp terminate:self];
}

@end

Als erstes wird die dealloc-Methode ergänzt.

-(void)dealloc
{
    [data release];
    [graph release];
    [super dealloc];
}

Als nächstes kommt die Implementierung der DataSource-Methoden:

#pragma mark -
#pragma mark Plot Data Source Methods

-(NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot
{
    return data.count;
}

-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{

}

-(CPFill *) barFillForBarPlot:(CPBarPlot *)barPlot recordIndex:(NSNumber *)index;
{
	return nil;
}

Die Methode numberOfRecordsForPlot liefert die Anzahl der Datensätze, d.h. die Anzahl der Balken, zurück. Dieser Wert kann direkt durch eine Abfrage der Elementanzahl in data bestimmt werden. Weitere Informationen zur Methode numberForPlot:field:recordIndex werden weiter unten gegeben.

Die Methode barFillForBarPlot:recordIndex wird in dem Beispiel nicht weiter verwendet und liefert deshalb nil zurück.

Die meiste Logik steckt in der Methode awakeFromNib. Hier werden die Daten angelegt und das Aussehen des Balkendiagramms wird definiert.

In der Methode awakeFromNib wird zuerst der Code ergänzt, der das Array mit den anzuzeigenden Daten enthält.

data = [[NSArray alloc]initWithObjects:
                    [NSDecimalNumber numberWithInt:100],
                    [NSDecimalNumber numberWithInt:130],
                    [NSDecimalNumber numberWithInt:30],
                    [NSDecimalNumber numberWithInt:40],
                    [NSDecimalNumber numberWithInt:60],
                    [NSDecimalNumber numberWithInt:80],
                    [NSDecimalNumber numberWithInt:100],
                    [NSDecimalNumber numberWithInt:120],
                    [NSDecimalNumber numberWithInt:10],
                    [NSDecimalNumber numberWithInt:15],
                    [NSDecimalNumber numberWithInt:20],
                    [NSDecimalNumber numberWithInt:100],
                    nil ];

Als nächstes kommt der Code, der den Graph erzeugt:

// Create graph and set a theme
graph = [[CPXYGraph alloc] initWithFrame:CGRectZero];
CPTheme *theme = [CPTheme themeNamed:kCPDarkGradientTheme];
[graph applyTheme:theme];
view.hostedLayer = graph;

Es wird ein Graph initialisiert und das CPTheme festgelegt. Der Graph wird abschließend dem HostedLayer (hostedLayer) der view zugewiesen.

Als nächste wird eine Zeichenfläche definiert.

// Define the space for the bars. (12 Bars with a max height of 150)
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0f) length:CPDecimalFromFloat(150.0f)];
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0f) length:CPDecimalFromFloat(12.0f)];

Hier wird zuerst die Höhe y mit 150 angegeben. Für die Breite x wird der Wert 12 gewählt, weil 12 Balken angezeigt werden sollen.

//  Bar plot
CPBarPlot *barPlot = [CPBarPlot tubularBarPlotWithColor:[CPColor yellowColor] horizontalBars:NO];
barPlot.dataSource = self;
barPlot.baseValue = CPDecimalFromString(@"0");
barPlot.barOffset = 1.0f;
barPlot.barWidth = 20.0f;
barPlot.identifier = @"BlueBarPlot";
[graph addPlot:barPlot toPlotSpace:plotSpace];

Abschließend wird ein CPBarPlot mit der Farbe gelb definiert, bei dem die Balken vertikal angezeigt werden.

Die DataSource für den BarPlot wird auf self gesetzt, so daß die oben angelegten DataSource-Methoden aufgerufen werden, wenn der BarPlot nach den anzuzeigenden Daten fragt.

Als Basevalue wird 0 gewählt. Der Offset sollte 1 sein, damit alle Balken angezeigt werden und der erste Balken nicht links aus dem Bild verschwindet. Als Breite für einen Balken wird 20 gewählt. Der Identifier wird angegeben damit ein Balken eindeutig identifiziert werden kann.

Zum Schluß wird der BarPlot und der PlotSpace dem Graphen zugewiesen.

In der Methode numberForPlot:field:recordIndex werden die Werte aus dem Array ausgelesen, wenn die Ansicht (view) die Werte von ihrer DataSource abfragt. Dies funktioniert folgendermaßen:

-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
    switch ( fieldEnum ) {
        case CPBarPlotFieldBarLocation:
            return (NSDecimalNumber *)[NSDecimalNumber numberWithUnsignedInteger:index];
        case CPBarPlotFieldBarLength:
            return [data objectAtIndex:index];
    }
    return nil;
}

Wenn nach der Position des Balkens gefragt wird, dann liefert die Methode einfach den Index zurück. Falls die Länge eines Balkens bestimmt werden soll, wird der entsprechende Wert aus den Array ausgelesen und zurück geliefert.

Die Implementierung ist damit abgeschlossen. Jetzt muß noch die Ansicht (view) eingerichtet werden. Dazu wird die Datei MainMenu.xib im Resources-Folder ausgewählt. Nach einem Doppelklick auf die Datei öffnet sich der InterfaceBuilder. Da als Vorlage eine Quartz Composer Application ausgewählt wurde, ist die Ansicht (View) schon mit allerlei Komponenten gefüllt, die gelöscht werden können. Also einfach alles in dem Fenster (Window) markieren und löschen.

Dann die Library mit cmd-shift-L öffnen, falls sie nicht schon offen ist. Dort Classes anklicken und unten im Suchfeld CPLayerHostingView eintippen.

Diese Ansicht (View) wird selektiert und mit gedrückter Mousetaste in das Fenster (Window) gezogen und auf die gewünschte Größe angepaßt.

Was jetzt noch fehlt, ist die Verbindung zwischen View und Controller. Diese wird durch ein crtl-mousedrag vom App Controller auf die CPLayerHostingView hinzugefügt. Jetzt ist auch das Outlet im Controller mit der Ansicht (View) verbunden.

Dadurch, daß am Anfang eine Quartz Composer Application gewählt wurde, sind alle anderen Bindings schon vorhanden.

Nach dem Kompilieren und Starten, sollte folgendes Balkendiagramm zu sehen sein: