dbchart.pas

来自「Delphi TeeChartPro.6.01的源代码」· PAS 代码 · 共 1,041 行 · 第 1/2 页

PAS
1,041
字号
{******************************************}
{ TDBChart Component                       }
{ Copyright (c) 1995-2003 by David Berneda }
{ All rights Reserved                      }
{******************************************}
unit DBChart;
{$I TeeDefs.inc}

interface

uses
  {$IFNDEF LINUX}
  Windows, Messages,
  {$ENDIF}
  SysUtils, Classes,
  {$IFDEF CLX}
  QGraphics, QControls, QMenus, QForms, QDialogs, QExtCtrls, QStdCtrls,
  {$ELSE}
  Graphics, Controls, Menus, Forms, Dialogs, ExtCtrls, StdCtrls,
  {$ENDIF}
  Chart, DB, TeeProcs, TeCanvas, TeEngine;

type
  DBChartException=class(Exception);

  TTeeDBGroup=(dgHour,dgDay,dgWeek,dgWeekDay,dgMonth,dgQuarter,dgYear,dgNone);

  TListOfDataSources=class(TList)
  private
    procedure Put(Index:Integer; Value:TDataSource);
    function Get(Index:Integer):TDataSource;
  public
    Procedure Clear; override;
    property DataSource[Index:Integer]:TDataSource read Get write Put; default;
  end;

  TCustomDBChart=class;

  TProcessRecordEvent=Procedure(Sender:TCustomDBChart; DataSet:TDataSet) of object;

  TCustomDBChart = class(TCustomChart)
  private
    FAutoRefresh     : Boolean;
    FRefreshInterval : Integer;
    FShowGlassCursor : Boolean;
    FOnProcessRecord : TProcessRecordEvent;
    { internal }
    IUpdating        : Boolean;
    ITimer           : TTimer;
    IDataSources     : TListOfDataSources;
    Procedure DataSourceCheckDataSet(ADataSet:TDataSet);
    Procedure DataSourceCloseDataSet(ADataSet:TDataSet);
    Procedure CheckDataSet(ADataSet:TDataSet; ASeries:TChartSeries=nil);
    Procedure CheckNewDataSource(ADataSet:TDataSet; SingleRow:Boolean);
    Procedure SetRefreshInterval(Value:Integer);
    Procedure CheckTimer;
    Procedure OnRefreshTimer(Sender:TObject);
  protected
    procedure RemovedDataSource( ASeries: TChartSeries;
                                 AComponent: TComponent ); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    Procedure Assign(Source:TPersistent); override;
    Procedure CheckDatasource(ASeries:TChartSeries); override;
    Function IsValidDataSource(ASeries:TChartSeries; AComponent:TComponent):Boolean; override;

    Procedure FillValueSourceItems( AValueList:TChartValueList; Proc:TGetStrProc); override;
    Procedure FillSeriesSourceItems( ASeries:TChartSeries; Proc:TGetStrProc); override;

    Procedure RefreshDataSet(ADataSet:TDataSet; ASeries:TChartSeries);
    Procedure RefreshData;
    { properties }
    property AutoRefresh:Boolean read FAutoRefresh write FAutoRefresh default True;
    property RefreshInterval:Integer read FRefreshInterval write SetRefreshInterval default 0;
    property ShowGlassCursor:Boolean read FShowGlassCursor write FShowGlassCursor default True;
    { events }
    property OnProcessRecord:TProcessRecordEvent read FOnProcessRecord
                                                 write FOnProcessRecord;
  published
  end;

  TDBChart=class(TCustomDBChart)
  published
    { TCustomDBChart properties }
    property AutoRefresh;
    property RefreshInterval;
    property ShowGlassCursor;
    { TCustomDBChart events }
    property OnProcessRecord;

    { TCustomChart Properties }
    property AllowPanning;
    property BackImage;
    property BackImageInside;
    property BackImageMode;
    property BackImageTransp;
    property BackWall;
    property Border;
    property BorderRound;
    property BottomWall;
    property Foot;
    property Gradient;
    property LeftWall;
    property MarginBottom;
    property MarginLeft;
    property MarginRight;
    property MarginTop;
    property MarginUnits;
    property PrintProportional;
    property RightWall;
    property SubFoot;
    property SubTitle;
    property Title;

    { TCustomChart Events }
    property OnAllowScroll;
    property OnClickAxis;
    property OnClickBackground;
    property OnClickLegend;
    property OnClickSeries;
    property OnClickTitle;
    property OnGetLegendPos;
    property OnGetLegendRect;
    property OnScroll;
    property OnUndoZoom;
    property OnZoom;

    { TCustomAxisPanel properties }
    property AxisBehind;
    property AxisVisible;
    property BottomAxis;
    property Chart3DPercent;
    property ClipPoints;
    property CustomAxes;
    property DepthAxis;
    property Frame;
    property LeftAxis;
    property Legend;
    property MaxPointsPerPage;
    property Monochrome;
    property Page;
    property RightAxis;
    property ScaleLastPage;
    property SeriesList;
    property Shadow;
    property TopAxis;
    property View3D;
    property View3DOptions;
    property View3DWalls;
    property Zoom;

    { TCustomAxisPanel events }
    property OnAfterDraw;
    property OnBeforeDrawAxes;
    property OnBeforeDrawChart;
    property OnBeforeDrawSeries;
    property OnGetAxisLabel;
    property OnGetLegendText;
    property OnGetNextAxisLabel;
    property OnPageChange;

    { TPanel properties }
    property Align;
    property BevelInner;
    property BevelOuter;
    property BevelWidth;

    {$IFDEF CLX}
    property Bitmap;
    {$ENDIF}

    property BorderWidth;
    property Color;
    {$IFNDEF CLX}
    property DragCursor;
    {$ENDIF}
    property DragMode;
    property Enabled;
    property ParentColor;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property Anchors;
    {$IFNDEF CLX}
    property AutoSize;
    {$ENDIF}
    property Constraints;
    {$IFNDEF CLX}
    property DragKind;
    property Locked;
    {$ENDIF}

    { TPanel events }
    property OnClick;
    {$IFDEF D5}
    property OnContextPopup;
    {$ENDIF}
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnStartDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnResize;
    {$IFNDEF CLX}
    property OnCanResize;
    {$ENDIF}
    property OnConstrainedResize;
    {$IFNDEF CLX}
    property OnDockDrop;
    property OnDockOver;
    property OnEndDock;
    property OnGetSiteInfo;
    property OnStartDock;
    property OnUnDock;
    {$ENDIF}

    {$IFDEF K3}
    property OnMouseEnter;
    property OnMouseLeave;
    {$ENDIF}
  end;

  TTeeFieldType=(tftNumber,tftDateTime,tftText,tftNone);

{ given a VCL "database field type", this function returns
  a more simplified "type": (number, date, text) }
Function TeeFieldType(AType:TFieldType):TTeeFieldType;

{ Returns the ISO- Week number (from 1 to 52) of a given "ADate" parameter,
  and decrements "Year" if it's the first week of the year. }
Function DateToWeek(ADate:TDateTime; Var Year:Word):Integer;

{ same as DateToWeek, using a different algorithm
  (compatible with TeeChart version 4 }
Function DateToWeekOld(Const ADate:TDateTime; Var Year:Word):Integer;

{ internal, used by DBChart Summary feature }
Function TeeGetDBPart(Num:Integer; St:String):String;
Function StrToDBGroup(St:String):TTeeDBGroup;
Function StrToDBOrder(St:String):TChartListOrder;

type
  TCheckDataSetEvent=procedure(DataSet:TDataSet) of object;

  TDBChartDataSource=class(TDataSource)
  private
    FDBChart   : TCustomDBChart;
    FWasActive : Boolean;
    Procedure DataSourceRowChange(Sender:TObject; Field:TField);
    Procedure DataSourceStateChange(Sender:TObject);
    Procedure DataSourceUpdateData(Sender:TObject);
  protected
    OnCheckDataSet : TCheckDataSetEvent;
    OnCloseDataSet : TCheckDataSetEvent;
    Procedure SetDataSet(Value:TDataSet; SingleRow:Boolean=False);
  end;

Procedure FillDataSetFields(DataSet:TDataSet; Proc:TGetStrProc);

implementation

Uses TeeConst, TypInfo;


{ TDBChartDataSource }
Procedure TDBChartDataSource.SetDataSet(Value:TDataSet; SingleRow:Boolean=False);
Begin
  DataSet:=Value;
  FWasActive:=DataSet.Active;

  OnStateChange:=DataSourceStateChange;
  OnUpdateData:=DataSourceUpdateData;
  if SingleRow then OnDataChange:=DataSourceRowChange;
end;

Procedure TDBChartDataSource.DataSourceRowChange(Sender:TObject; Field:TField);
begin
  if Assigned(FDBChart) then
     if not FDBChart.IUpdating then
        With TDBChartDataSource(Sender) do OnCheckDataSet(DataSet);
end;

Procedure TDBChartDataSource.DataSourceUpdateData(Sender:TObject);
Begin
  With TDBChartDataSource(Sender) do
  if State=dsBrowse then OnCheckDataSet(DataSet)
                    else FWasActive:=False;
End;

Procedure TDBChartDataSource.DataSourceStateChange(Sender:TObject);
Begin
  With TDBChartDataSource(Sender) do
  if State=dsInactive then
  Begin
    FWasActive:=False;
    if Assigned(OnCloseDataSet) then OnCloseDataSet(DataSet);
  end
  else
  if (State=dsBrowse) and (not FWasActive) then
  Begin
    OnCheckDataSet(DataSet);
    FWasActive:=True;
  end;
end;

{ TListOfDataSources }
Procedure TListOfDataSources.Clear;
var t : Integer;
begin
  for t:=0 to Count-1 do DataSource[t].Free;
  inherited;
end;

procedure TListOfDataSources.Put(Index:Integer; Value:TDataSource);
begin
  inherited Items[Index]:=Value;
end;

function TListOfDataSources.Get(Index:Integer):TDataSource;
begin
  result:=TDataSource(inherited Items[Index]);
end;

{ TDBChart }
Constructor TCustomDBChart.Create(AOwner: TComponent);
begin
  inherited;
  IDataSources:=TListOfDataSources.Create;
  FAutoRefresh:=True;
  FShowGlassCursor:=True;
  ITimer:=nil;
  IUpdating:=False;
end;

Destructor TCustomDBChart.Destroy;
begin
  ITimer.Free;
  IDataSources.Free;
  inherited;
end;

procedure TCustomDBChart.RemovedDataSource( ASeries: TChartSeries;
                                            AComponent: TComponent );
var t   : Integer;
    tmp : TDataSet;
begin
  inherited;
  if AComponent is TDataSet then
  for t:=0 to IDataSources.Count-1 do
  begin
    tmp:=IDataSources[t].DataSet;
    if (not Assigned(tmp)) or (tmp=AComponent) then
    begin
      IDataSources[t].Free;
      IDataSources.Delete(t);
      break;
    end;
  end;
end;

Procedure TCustomDBChart.CheckTimer;
Begin
  if Assigned(ITimer) then ITimer.Enabled:=False;
  if (FRefreshInterval>0) and (not (csDesigning in ComponentState) ) then
  Begin
    if not Assigned(ITimer) then
    Begin
      ITimer:=TTimer.Create(Self);
      ITimer.Enabled:=False;
      ITimer.OnTimer:=OnRefreshTimer;
    end;
    ITimer.Interval:=FRefreshInterval*1000;
    ITimer.Enabled:=True;
  end;
End;

Procedure TCustomDBChart.OnRefreshTimer(Sender:TObject);
var t : Integer;
Begin
  ITimer.Enabled:=False;  { no try..finally here ! }
  for t:=0 to IDataSources.Count-1 do
  With IDataSources[t] do
  if DataSet.Active then
  Begin
    DataSet.Refresh;
    CheckDataSet(DataSet);
  end;
  ITimer.Enabled:=True;
end;

Procedure TCustomDBChart.SetRefreshInterval(Value:Integer);
Begin
  if (Value<0) or (Value>60) then
     Raise DBChartException.Create(TeeMsg_RefreshInterval);
  FRefreshInterval:=Value;
  CheckTimer;
End;

Function TCustomDBChart.IsValidDataSource(ASeries:TChartSeries; AComponent:TComponent):Boolean;
Begin
  result:=inherited IsValidDataSource(ASeries,AComponent);
  if not Result then result:=(AComponent is TDataSet) or (AComponent is TDataSource);
end;

Procedure TCustomDBChart.CheckNewDataSource(ADataSet:TDataSet; SingleRow:Boolean);
Var tmpDataSource : TDBChartDataSource;
Begin
  if IDataSources.IndexOf(ADataSet)=-1 then
  begin
    tmpDataSource:=TDBChartDataSource.Create(nil); { 5.02 }
    With tmpDataSource do
    begin
      SetDataSet(ADataSet,SingleRow);
      OnCheckDataSet:=DataSourceCheckDataSet;
      OnCloseDataSet:=DataSourceCloseDataSet;
    end;
    IDataSources.Add(tmpDataSource);
  end;
end;

Procedure TCustomDBChart.CheckDatasource(ASeries:TChartSeries);
Begin
  if Assigned(ASeries) then
  With ASeries do
    if ParentChart=Self then
    Begin
      if Assigned(DataSource) then
      Begin
        ASeries.Clear;
        if DataSource is TDataSet then
        Begin
          CheckNewDataSource(TDataSet(DataSource),False);
          CheckDataSet(TDataSet(DataSource),ASeries);
        end
        else
        if (DataSource is TDataSource) and Assigned(TDataSource(DataSource).DataSet) then
        begin
          CheckNewDataSource(TDataSource(DataSource).DataSet,True);
          CheckDataSet(TDataSource(DataSource).DataSet,ASeries);
        end
        else inherited;
      end
      else inherited;
    end
    else Raise ChartException.Create(TeeMsg_SeriesParentNoSelf);
end;

Procedure TCustomDBChart.CheckDataSet(ADataSet:TDataSet; ASeries:TChartSeries=nil);
Begin
  if FAutoRefresh then RefreshDataSet(ADataSet,ASeries);
end;

Procedure TCustomDBChart.DataSourceCheckDataSet(ADataSet:TDataSet);
begin
  CheckDataSet(ADataSet);
end;

Procedure TCustomDBChart.DataSourceCloseDataSet(ADataSet:TDataSet);
var t : Integer;
begin
  if FAutoRefresh then
     for t:=0 to SeriesCount-1 do
         if Series[t].DataSource=ADataSet then Series[t].Clear;
end;

type TValueListAccess=class(TChartValueList);

     TDBChartAgg=(dcaNone, dcaSum, dcaCount, dcaHigh, dcaLow, dcaAverage);
     TDBChartSeries=packed record
        ASeries     : TChartSeries;
        YManda      : Boolean;
        MandaList   : TChartValueList;
        LabelSort   : TChartListOrder;
        LabelField  : TField;
        ColorField  : TField;
        MandaField  : TField;
        NumFields   : Integer;
        GroupPrefix : TTeeDBGroup;
        AggPrefix   : TDBChartAgg;
     end;

     TDBChartSeriesList=Array of TDBChartSeries;

Procedure TCustomDBChart.RefreshDataSet(ADataSet:TDataSet; ASeries:TChartSeries);
Var HasAnyDataSet : Boolean;

  Procedure ProcessRecord(const tmpSeries:TDBChartSeries);
  var tmpxLabel : String;
      tmpColor  : TColor;
      tmpNotMand: Double;
      tmpMand   : Double;

    Procedure AddToSeries(const DestSeries:TDBChartSeries);
    Var t        : Integer;
        tmpIndex : Integer;
    begin
      With DestSeries do
      if AggPrefix<>dcaNone then
      begin
        tmpIndex:=ASeries.Labels.IndexOfLabel(tmpXLabel);

        if tmpIndex=-1 then { new point }
        begin
          if AggPrefix=dcaCount then tmpMand:=1
          else
          if AggPrefix=dcaAverage then tmpColor:=1;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?