📄 jvqchart.pas
字号:
// 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 }
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;
implementation
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
SysUtils, Math, QForms, QDialogs, QPrinters, QClipbrd,
JvQJVCLUtils, JvQConsts, JvQResources;
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.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
FDataAlloc := (ValueIndex * 2) // Double in size
else
FDataAlloc := ValueIndex + 64000;
oldLength := Length(FData);
SetLength(FData, FDataAlloc);
// new: If we set FClearToValue to NaN, special handling in growing arrays:
if IsNaN(FClearToValue) then
for I := oldLength to FDataAlloc - 1 do
for J := 0 to Length(FData[I]) - 1 do
FData[I][J] := FClearToValue; // XXX Debug me!
end;
if Pen >= Length(FData[ValueIndex]) then
begin
oldLength := Length(FData[ValueIndex]);
SetLength(FData[ValueIndex], Pen + 1);
if IsNaN(FClearToValue) then
begin
for I := oldLength to FDataAlloc - 1 do
begin
Assert(Length(FData) > ValueIndex);
if (Length(FData[ValueIndex]) < FDataAlloc) then
SetLength(FData[ValueIndex], FDataAlloc); // Safety code!
FData[ValueIndex][I] := FClearToValue; // XXX Debug me!
end;
end;
end;
end;
function TJvChartData.DebugStr(ValueIndex: Integer): string; // dump all pens for particular valueindex, as string.
var
S: string;
I, IMax: Integer;
begin
if (ValueIndex < 0) or (ValueIndex >= FDataAlloc) then
Exit;
IMax := Length(FData[ValueIndex]) - 1;
if Timestamp[ValueIndex] > 0.0 then
S := FormatDateTime('hh:nn:ss ', Timestamp[ValueIndex]);
for I := 0 to IMax do
begin
if IsNaN(FData[ValueIndex, I]) then
S := S + '-'
else
S := S + Format('%5.2f', [FData[ValueIndex, I]]);
if I < IMax then
S := S + ', '
end;
Result := S;
end;
procedure TJvChartData.Clear; // Resets FValuesCount/FPenCount to zero. Zeroes everything too, just for good luck.
var
I, J: Integer;
begin
for I := 0 to FDataAlloc - 1 do
for J := 0 to Length(FData[I]) - 1 do
FData[I, J] := FClearToValue;
FValueCount := 0;
end;
procedure TJvChartData.ClearPenValues; // Clears all pen values to NaN but does not reset pen definitions etc.
var
I, J: Integer;
begin
for I := 0 to FDataAlloc - 1 do
for J := 0 to Length(FData[I]) - 1 do
FData[I, J] := 0.0;
end;
//=== { TJvChartYAxisOptions } ===============================================
constructor TJvChartYAxisOptions.Create(Owner: TJvChartOptions);
begin
inherited Create;
FOwner := Owner;
FMarkerValueDecimals := -1; // -1 = default (automatic decimals)
FYLegends := TStringList.Create;
FMaxYDivisions := 20;
FMinYDivisions := 5;
FYDivisions := 10;
FDefaultYLegends := JvDefaultYLegends;
end;
destructor TJvChartYAxisOptions.Destroy;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -