TeeChart:快速显示当前鼠标位置的系列值

时间:2016-06-07 05:52:28

标签: delphi annotations teechart delphi-xe3 onmousemove

如果光标位于图表上,我想显示当前鼠标位置的所有系列的值。正如它在这个图中显示的那样:

Target Display

为了完成此行为,我使用了TAnnotationToolOnMouseMove事件。另外,我使用TCursorTool Style := cssVerticalFollowMouse := True在当前的mouve位置绘制一条垂直线。不幸的是,这个解决方案很慢。如果系列计数大于10,则用户已经可以观察到注释在鼠标之后运行,延迟大约为500ms。在我对此问题的调查过程中,我发现MouseMoveEvent的这一部分是瓶颈:

chtMain  : TChart; 
FValAnno : TAnnotationTool;
...
TfrmMain.chtMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer)
var
  HasData : Boolean;
  AnnoLst : TStrings;
begin
  ...  
  if HasData then
    Self.FValAnno.Text := AnnoLst.Text
  else
    Self.FValAnno.Text := 'No data';
  //
  if (X < Self.chtMain.Width - Self.FValAnno.Width - 5) then
    Self.FValAnno.Shape.Left := X + 10
  else
    Self.FValAnno.Shape.Left := X - Self.FValAnno.Width - 15;
  //
  if (Y < Self.chtMain.Height - Self.FValAnno.Height - 5) then
    Self.FValAnno.Shape.Top := Y + 10
  else
    Self.FValAnno.Shape.Top := Y - Self.FValAnno.Height - 15;
  //
  if (FX >= Self.chtMain.BottomAxis.IStartPos) and
    (FX <= Self.chtMain.BottomAxis.IEndPos) and
    (FY >= Self.chtMain.LeftAxis.IStartPos) and
    (FY <= Self.chtMain.LeftAxis.IEndPos) then
    Self.FValAnno.Active := True
  else
    Self.FValAnno.Active := False;
  ...
end;

如果我使用垂直线上方的代码,并且注释在光标之后运行大约500ms,系列计数为100.滞后增加,系列计数越高。另一方面,如果我不使用注释代码,则垂直线仅运行约100ms的延迟。

使用TChart组件是否还有其他工具可以更快地完成此操作?或者,我可以使用哪些属性来加快速度?

提前感谢您的支持!

编辑:重现此问题的示例代码

  1. 创建新的VCL项目
  2. 删除表格上的TChart组件和复选框
  3. 为表单创建FormCreate,为图表创建MouseMoveEvent
  4. 切换到代码视图,插入以下代码:
  5. 代码:

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus,
      VCLTee.TeEngine, Vcl.ExtCtrls, VCLTee.TeeProcs, VCLTee.Chart, VCLTee.TeeTools,
      Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        chtMain: TChart;
        chkAnno: TCheckBox;
        procedure FormCreate(Sender: TObject);
        procedure chtMainMouseMove(Sender: TObject; Shift: TShiftState; X,
          Y: Integer);
      private
        FCursor : TCursorTool;
        FAnno   : TAnnotationTool;
      public
        { Public-Deklarationen }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    uses
      VCLTee.Series,
      System.DateUtils;
    
    const
      ARR_MAXS : array[0..3] of Double = (12.5, 25.8, 2.8, 56.7);
    
    procedure TForm1.chtMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    
      function GetXValueIndex(const ASerie: TChartSeries; const AX: Double): Integer;
      var
        index: Integer;
      begin
        for index := 0 to ASerie.XValues.Count - 1 do
        begin
          if ASerie.XValue[index] >= AX then
            Break;
        end;
        //
        Result := index - 1;
      end;
    
    var
      Idx, I    : Integer;
      CursorX,
      CursorY,
      Value     : Double;
      Serie     : TChartSeries;
      LegendTxt : string;
      AnnoLst   : TStrings;
      HasData   : Boolean;
      ShownDate : TDateTime;
    begin
      //
      if not Self.chkAnno.Checked then
      begin
        //
        FAnno.Text := Format('Position:'#13#10'  X: %d'#13#10'  Y: %d', [X, Y]);
      end
      else
      begin
        //
        if (Self.chtMain.SeriesCount < 1) then
        begin
          //
          if Assigned(Self.FAnno) then
            Self.FAnno.Active := False;
          Exit;
        end;
        //
        Self.chtMain.Series[0].GetCursorValues(CursorX, CursorY);
        //
        AnnoLst := TStringList.Create;
        try
          //
          ShownDate := 0;
          HasData   := False;
          for I := 0 to Self.chtMain.SeriesCount - 1 do
          begin
            //
            Serie := Self.chtMain.Series[I];
            //
            Idx := GetXValueIndex(Serie, CursorX);
    
            if Serie.XValue[Idx] > ShownDate then
            begin
              //
              LegendTxt := DateTimeToStr(Serie.XValue[Idx]);
              if (AnnoLst.Count > 0) and
                (ShownDate > 0) then
                AnnoLst[0] := LegendTxt
              else if AnnoLst.Count > 0 then
                AnnoLst.Insert(0, LegendTxt)
              else
                AnnoLst.Add(LegendTxt);
              HasData   := True;
              ShownDate := Serie.XValue[Idx];
            end;
            //
            LegendTxt := Format('Serie: %d', [I]);
            if Length(LegendTxt) <= 25 then
              LegendTxt := Format('%-25s', [LegendTxt])
            else
              LegendTxt := Format('%s...', [LegendTxt.Substring(0, 22)]);
            //
            Value     := Serie.YValue[Idx] * Abs(ARR_MAXS[I]);
            LegendTxt := Format('%s: %3.3f %s', [LegendTxt, Value, 'none']);
            AnnoLst.Add(LegendTxt);
          end;
    
          FAnno.Text := AnnoLst.Text;
        finally
          FreeAndNil(AnnoLst);
        end;
      end;
    
      if (X < Self.chtMain.Width - Self.FAnno.Width - 5) then
        Self.FAnno.Shape.Left := X + 10
      else
        Self.FAnno.Shape.Left := X - Self.FAnno.Width - 15;
      //
      if (Y < Self.chtMain.Height - Self.FAnno.Height - 5) then
        Self.FAnno.Shape.Top := Y + 10
      else
        Self.FAnno.Shape.Top := Y - Self.FAnno.Height - 15;
      //
      if (X >= Self.chtMain.BottomAxis.IStartPos) and
        (X <= Self.chtMain.BottomAxis.IEndPos) and
        (Y >= Self.chtMain.LeftAxis.IStartPos) and
        (Y <= Self.chtMain.LeftAxis.IEndPos) then
        Self.FAnno.Active := True
      else
        Self.FAnno.Active := False;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      Idx, J : Integer;
      Serie  : TFastLineSeries;
      Start  : TDateTime;
      Value  : Double;
    begin
      //
      Self.chtMain.View3D                    := False;
      Self.chtMain.Align                     := alClient;
      Self.chtMain.BackColor                 := clWhite;
      Self.chtMain.Color                     := clWhite;
      Self.chtMain.Gradient.Visible          := False;
      Self.chtMain.Legend.LegendStyle        := lsSeries;
      Self.chtMain.Zoom.Allow                := False; 
      Self.chtMain.AllowPanning              := pmNone;  
      Self.chtMain.BackWall.Color            := clWhite;
      Self.chtMain.BackWall.Gradient.Visible := False;
    
      Self.chtMain.LeftAxis.Automatic        := False;
      Self.chtMain.LeftAxis.Minimum          := 0;
      Self.chtMain.LeftAxis.Maximum          := 2;
      Self.chtMain.LeftAxis.Increment        := 0.1;
      Self.chtMain.LeftAxis.Visible          := True;
      Self.chtMain.LeftAxis.AxisValuesFormat := '#,##0.## LV';
      //
      Self.chtMain.BottomAxis.DateTimeFormat   := 'dd.mm.yyyy hh:mm:ss';
      Self.chtMain.BottomAxis.Increment        := 1 / 6; 
      Self.chtMain.BottomAxis.Automatic        := True;
      Self.chtMain.BottomAxis.LabelsSize       := 32;
      Self.chtMain.BottomAxis.LabelsMultiLine  := True;
      Self.chtMain.MarginBottom                := 6;
      Self.chtMain.BottomAxis.Title.Caption    := 'Date';
      Self.chtMain.BottomAxis.Visible          := False;
    
    
      FAnno := Self.chtMain.Tools.Add(TAnnotationTool) as TAnnotationTool;
      FAnno.Active := False;
      FAnno.Shape.CustomPosition := True;
    
      FCursor := Self.chtMain.Tools.Add(TCursorTool) as TCursorTool;
      FCursor.FollowMouse := True;
      FCursor.Style := cssVertical;
    
      Randomize;
      Start := Now;
      for Idx := 0 to 3 do
      begin
        //
        Serie := Self.chtMain.AddSeries(TFastLineSeries) as TFastLineSeries;
        Serie.FastPen := True;
        Serie.ShowInLegend := False;
        Serie.XValues.DateTime := True;
        Serie.VertAxis := aLeftAxis;
        Serie.ParentChart := Self.chtMain;
    
        for J := 1 to 1000 do
        begin
          //
          Value := Random * ARR_MAXS[Idx] * 1.8;
          Serie.AddXY(IncSecond(Start, J), Value / ARR_MAXS[Idx]);
        end;
      end;
    end;
    
    end.
    
    1. 按[F9]
    2. 我没有发现任何差异,无论你使用位置注释代码还是另一个。

1 个答案:

答案 0 :(得分:1)

TCursorTool具有FullRepaint属性(默认情况下为false)以使用XOR绘制它,因此每次更新其位置时都不需要重新绘制完整的图表。这很快。

但是,TAnnotationTool并不包含这种可能性,因此,当您更新FAnnot文本或位置时,您正在强制重新绘制图表,并且拥有多个点会使进程变慢。

您可以使用TLabel组件,而不是使用TAnnotationTool来绘制文字。