📄 jvchart.pas
字号:
procedure PrimaryYAxisLabels; // Put contents into Options.PrimaryYAxis.YLegends
procedure NotifyOptionsChange; {NEW}
procedure _PlotGraph; { internal version of _PlotGraph that doesn't call Invalidate. }
{ internal drawing properties, valid during Paint method invocations only }
property XOrigin: Double read FXOrigin; {was in TJvChart.PlotGraph}
property YOrigin: Double read FYOrigin; {was in TJvChart.PlotGraph}
//property YOldOrigin: Integer read FYOldOrigin; {was in TJvChart.PlotGraph}
//property YTempOrigin: Integer read FYTempOrigin; {was in TJvChart.PlotGraph}
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// Get the X and Y Value for a particular Mouse location:
function MouseToXValue(X: Integer): Integer;
// convert X pixel mouse position to data index, ie Data.Values[..,<INDEX>].
function MouseToYValue(Y: Integer): Double;
// convert Y pixel mouse position to value in range Options.PrimaryYAxis.Min to Options.PrimaryYAxis.mAx
{General procedures for the graph...}
procedure ResetGraphModule; {Call this before totally new values and Pen}
//procedure AutoFormatGraph; {XXX BAD CODE. TO BE DELETED. MAY BE REPLACED LATER BY NEW AutoRange FUNCTION!}
procedure PlotGraph; {Update screen / draw graph to screen. calls Invalidate. Don't call from inside Paint code!}
procedure PrintGraph; {Send picture to printer; all printing done by component}
procedure AddGraphToOpenPrintCanvas(XStartPos, YStartPos, GraphWidth, GraphHeight: Longint);
{adds the graph to the "OPEN" printer canvas}
{printing control=outside this component; add other text etc}
procedure GraphToClipboard; {Puts picture on clipboard}
procedure ResizeChartCanvas; {Call this after screen resize and after start up}
procedure PivotData; { Pivot Table. Switches the x values with Pen! Resets AverageLine}
procedure AutoHint; // Make the automatic hint message showing all pens and their values.
procedure SetCursorPosition(Pos: Integer);
// FLOATING MARKERS: NEW JAN 2005. -WP
function AddFloatingMarker: TJvChartFloatingMarker; // NEW Jan 2005!
procedure DeleteFloatingMarker(Index: Integer); // NEW Jan 2005!
procedure ClearFloatingMarkers;
property FloatingMarker[Index: Integer]: TJvChartFloatingMarker read GetFloatingMarker; // NEW Jan 2005!
property Data: TJvChartData read FData;
property AverageData: TJvChartData read FAverageData;
public
{runtime only helper properties}
{ TImage-like stuff }
property Picture: TPicture read FPicture; // write SetPicture;
// NEW: Ability to highlight a particular sample by setting the Cursor position!
property CursorPosition: Integer read FCursorPosition write SetCursorPosition;
// procedure DataTests; // TESTING. WAP.
published
{ Standard TControl Stuff}
//property Color default clWindow;
property Font;
property Align;
property Anchors;
property Constraints;
property OnDblClick; { TNotifyEvent from TControl }
{$IFDEF VCL}
property AutoSize;
property DragCursor;
property DragKind;
//property OnKeyDown; // Tried to add this, but it was too hard. -WP APril 2004.
{$ENDIF VCL}
property DragMode;
property Enabled;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Visible;
{ chart options}
property Options: TJvChartOptions read FOptions write FOptions;
{ chart events}
property OnChartClick: TJvChartClickEvent read FOnChartClick write FOnChartClick;
property OnChartPaint: TJvChartPaintEvent read FOnChartPaint write FOnChartPaint;
// After chart bitmap is painted onto control surface we can "decorate" it with owner-drawn extras.
{ Drag and Drop of Floating Marker Events - NEW Jan 2005 -WP}
property OnBeginFloatingMarkerDrag: TJvChartFloatingMarkerDragEvent read FOnBeginFloatingMarkerDrag write
FOnBeginFloatingMarkerDrag; // Drag/drop of floating markers beginning.
property OnEndFloatingMarkerDrag: TJvChartFloatingMarkerDragEvent read FOnEndFloatingMarkerDrag write
FOnEndFloatingMarkerDrag; // Drag/drop of floating markers ending.
{
Chart Margin Click Events - you can click on the four
'margin' areas (left,right,top,bottom) around the main chart
area. The left and top margins have default behaviours
which you can disable by turning off Options.MouseEdit.
The other 2 margin areas are entirely up to the user to define.
Clicking bottom or right margins does nothing by default.
}
property OnYAxisClick: TJvChartEvent read FOnYAxisClick write FOnYAxisClick;
// When user clicks on Y axis, they can enter a new Y Scale value.
property OnXAxisClick: TJvChartEvent read FOnXAxisClick write FOnXAxisClick;
// Also allow user to define some optional action for clicking on the X axis.
property OnAltYAxisClick: TJvChartEvent read FOnAltYAxisClick write FOnAltYAxisClick;
// Right margin click (Secondary Y Axis labels)
property OnTitleClick: TJvChartEvent read FOnTitleClick write FOnTitleClick; // Top margin area (Title area) click.
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$RCSfile: JvChart.pas,v $';
Revision: '$Revision: 1.82 $';
Date: '$Date: 2005/03/07 16:33:16 $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
SysUtils, Math, Forms, Dialogs, Printers, Clipbrd,
{$IFDEF COMPILER5}
JclMath, // function IsNaN for Delphi 5 (ahuser)
JvJCLUtils, // StrToFloatDef
{$ENDIF COMPILER5}
JvJVCLUtils, JvConsts, JvResources;
const
CHART_SANITY_LIMIT = 60000;
// Any attempt to have more than CHART_SANITY_LIMIT elements in this
// graph will be treated as an internal failure on our part. This prevents
// ugly situations where we thrash because of excessive memory usage.
// Better to set this than to have the system pig out when we
// don't want it to. Set this very small when debugging,
// large when releasing component, and don't remove it unless
// you're absolutely sure. Increase it whenever necessary.
// Remember, it's a debugging tool, here on purpose to help keep you
// out of thrashing-virtual-memory-hell. You probably have a screen
// to view the chart that is a maximum of 1600x1200, so more than 1600
// samples will mean the data should be reduced before charting.
MAX_VALUES = 20000;
// Any attempt to exceed this values count will cause array size and performance problems, thus we limit it.
MAX_PEN = 100;
// Any attempt to exceed this pen count will cause array size and performance problems, thus we hardcode the pen limit to 100 pens.
DEFAULT_PEN_COUNT = 16; // By Default TJvChartData's internal data structures have room for up to 16 pens
MAX_X_LEGENDS = 50;
MAX_GRAPH_LEGEND_LEN = 9;
REALPREC = 7;
DEFAULT_MARKER_SIZE = 3;
DEFAULT_VALUE_COUNT = 100;
// By Default TJvChartData holds 100 values per pen. Grows autofragellisticexpialidociously. :-)
//=== {TJvChartFloatingMarker} ===============================================
constructor TJvChartFloatingMarker.Create(Owner: TJvChart);
begin
FOwner := Owner;
FVisible := False; // NOT visible by default.
FIndex := -1; // not yet set.
FLineToMarker := -1; // Don't draw a line to connect to another marker.
//FYPositionToPen := -1; // Don't copy FYPosition from the pen values.
FMarkerColor := clRed;
FMarker := pmkDiamond; // default is diamond marker.
FLineStyle := psDot;
FLineColor := clBlue;
//FCaptionBorderStyle := psClear;
FXDragMin := -1; // no limit.
FXDragMax := -1; // no limit.
FRawXPosition := -1;
FRawYPosition := -1;
FLineWidth := 1;
//FXPosition := 0;
//FYPosition := 0.0;
end;
procedure TJvChartFloatingMarker.SetCaption(aCaption: string);
begin
if aCaption <> FCaption then
begin
FCaption := aCaption;
if Assigned(FOwner) and FVisible then
if not FOwner.FUpdating then
FOwner.Invalidate;
end;
end;
procedure TJvChartFloatingMarker.SetXPosition(xPos: Integer); // should invalidate the chart (FOwner) if changed.
begin
if xPos <> FXPosition then
begin
FXPosition := xPos;
if Assigned(FOwner) and FVisible then
if not FOwner.FUpdating then
FOwner.Invalidate;
end
end;
procedure TJvChartFloatingMarker.SetYPosition(yPos: Double); // should invalidate the chart (FOwner) if changed.
begin
if yPos <> FYPosition then
begin
FYPosition := yPos;
if Assigned(FOwner) and FVisible then
if not FOwner.FUpdating then
FOwner.Invalidate;
end
end;
procedure TJvChartFloatingMarker.SetVisible(isVisible: Boolean);
begin
if isVisible <> FVisible then
begin
FVisible := isVisible;
if Assigned(FOwner) then
if not FOwner.FUpdating then
FOwner.Invalidate;
end
end;
//=== { TJvChartData } =======================================================
constructor TJvChartData.Create;
var
I: Integer;
begin
inherited Create;
for I := 0 to DEFAULT_PEN_COUNT do
Grow(I, DEFAULT_VALUE_COUNT);
end;
destructor TJvChartData.Destroy;
var
I: Integer;
begin
for I := 0 to FDataAlloc - 1 do
Finalize(FData[I]);
Finalize(FData); // Free array.
inherited Destroy;
end;
function TJvChartData.GetValue(Pen, ValueIndex: Integer): Double;
begin
// Grow base array
Assert(ValueIndex >= 0);
Grow(Pen, ValueIndex);
Result := FData[ValueIndex, Pen]; // This will raise EInvalidOP for NaN values.
end;
procedure TJvChartData.SetValue(Pen, ValueIndex: Integer; NewValue: Double);
begin
// Grow base array
Grow(Pen, ValueIndex);
FData[ValueIndex, Pen] := NewValue;
if ValueIndex >= FValueCount then
begin
Grow(Pen, ValueIndex + 1);
FData[ValueIndex + 1, Pen] := NewValue; // Workaround for a graphical bug. Sorry.
FValueCount := ValueIndex + 1;
end;
end;
function TJvChartData.GetTimestamp(ValueIndex: Integer): TDateTime;
begin
if (ValueIndex < 0) or (ValueIndex >= Length(FTimeStamp)) then
Result := 0.0 // null datetime
else
Result := FTimeStamp[ValueIndex];
end;
procedure TJvChartData.SetTimestamp(ValueIndex: Integer; AValue: TDateTime);
begin
if ValueIndex < 0 then
Exit;
if ValueIndex >= Length(FTimeStamp) then
SetLength(FTimeStamp, ValueIndex + 1);
FTimeStamp[ValueIndex] := AValue;
end;
procedure TJvChartData.Scroll;
var
I, J: Integer;
begin
if FValueCount < 2 then
begin
Clear;
Exit;
end;
{ ULTRA SLOW BUT NON-CRASHING Version }
for I := 0 to FValueCount - 1 do
begin
for J := 0 to Length(FData[I]) - 1 do
FData[I, J] := FData[I + 1, J];
SetTimestamp(I, GetTimestamp(I + 1));
end;
FTimeStamp[FValueCount - 1] := 0;
// Check we didn't break the heap:
end;
procedure TJvChartData.PreGrow(Pen, ValueIndex: Integer);
var
t:Integer;
begin
if Length(FData)<ValueIndex then
SetLength(FData,ValueIndex);
for t := 0 to ValueIndex-1 do begin
SetLength(FData[t],Pen);
end;
FDataAlloc := ValueIndex;
end;
procedure TJvChartData.Grow(Pen, ValueIndex: Integer);
var
I, J, oldLength: Integer;
begin
if (Pen < 0) or (ValueIndex < 0) then
begin
raise ERangeError.CreateRes(@RsEDataIndexCannotBeNegative);
end;
if (Pen > CHART_SANITY_LIMIT) or (ValueIndex > CHART_SANITY_LIMIT) then
begin
raise ERangeError.CreateRes(@RsEDataIndexTooLargeProbablyAnInternal);
end;
if ValueIndex >= FDataAlloc then
begin
//--------------------------------------------------------
// Performance tweak: Uses more memory but makes JvChart
// much faster!
// We Double our allocation unit size
// until we start to get Really Huge, then grow in chunks!
//--------------------------------------------------------
if ValueIndex < 640000 then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -