Recent

Author Topic: [solved] adding text notes to charts  (Read 1993 times)

Muso

  • Sr. Member
  • ****
  • Posts: 356
[solution] saving marks of charts
« Reply #15 on: July 14, 2022, 04:09:16 pm »
Since I succeeded to store and loading the marks, here is my solution in case someone needs the same:

setting marks and saving:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.DataPointClickToolPointClick(ATool: TChartTool;
  2.   APoint: TPoint);
  3. var
  4.  NoteText, NotesFile, OutputLine : string;
  5.  tool : TDataPointTool;
  6.  series : TChartSeries;
  7.  NotesFileStream : TFileStream;
  8.  MousePointer : TPoint;
  9. begin
  10.  MousePointer:= Mouse.CursorPos;
  11.  tool:= ATool as TDataPointTool;
  12.  if tool.PointIndex < 0 then
  13.   exit;
  14.  
  15.  series:= tool.Series as TChartSeries;
  16.  NoteText:= series.Source[tool.PointIndex]^.Text;
  17.  with NoteEditingF do
  18.  begin
  19.   NoteLabelL.Caption:= 'Note for ' + series.Title + ':';
  20.   NoteTextM.Lines.Text:= NoteText;
  21.   ShowModal;
  22.   series.Source[tool.PointIndex]^.Text:= NoteTextM.Lines.Text;
  23.  end;
  24.  // force a redraw of the chart (chart name is "SIXCH")
  25.  SIXCH.Invalidate;
  26.  
  27.  // write mark to the .notes file
  28.  // we write into the same folder than the program .exe
  29.  if InNameSensor.IsEmpty then
  30.   NotesFile:= 'unknown.notes'
  31.  else
  32.   NotesFile:= ExtractFileName(InNameSensor) + '.notes';
  33.  NotesFile:= ExtractFilePath(Application.ExeName) + NotesFile;
  34.  
  35.  try
  36.   try
  37.    if FileExists(NotesFile) then
  38.     NotesFileStream:= TFileStream.Create(NotesFile, fmOpenReadWrite or fmShareDenyNone)
  39.    else
  40.     NotesFileStream:= TFileStream.Create(NotesFile, fmCreate or fmShareDenyNone);
  41.   except
  42.    on EFOpenError do
  43.    begin
  44.     MessageDlgPos('Notes file is used by another program and cannot be opened.'
  45.                   + LineEnding + 'New notes cannot be saved.',
  46.                   mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  47.     exit;
  48.    end;
  49.   end;
  50.   // seek to end and append the mark
  51.   NotesFileStream.Seek(0, soFromEnd);
  52.   OutputLine:= series.Name + #9 + IntToStr(tool.PointIndex) + LineEnding
  53.                + NoteEditingF.NoteTextM.Lines.Text + LineEnding + LineEnding;
  54.   try
  55.    NotesFileStream.Write(OutputLine[1], Length(OutputLine));
  56.   except
  57.    NotesFileStream.Free;
  58.    MessageDlgPos('Notes could not be written to the .notes file.',
  59.                  mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  60.   end;
  61.  
  62.  finally
  63.   NotesFileStream.Free;
  64.  end;
  65.  
  66. end;


Reading existing marks and applying them:
Code: Pascal  [Select][+][-]
  1. procedure TMainForm.ReadNotes;
  2. // reads marks from .notes file ad adds them to the chart
  3. var
  4.  NotesFileStream : TFileStream;
  5.  LineReader : TStreamReader;
  6.  StringArray : TStringArray;
  7.  series : TChartSeries;
  8.  ReadLine, NotesFile, OutputLine : string;
  9.  MousePointer : TPoint;
  10.  rowCounter, PointIndex : integer;
  11.  skipNote : Boolean;
  12. begin
  13.  // initialize
  14.  rowCounter:= 0;
  15.  series:= nil;
  16.  MousePointer:= Mouse.CursorPos;
  17.  
  18.  NotesFile:= ExtractFileName(InNameSensor) + '.notes';
  19.  NotesFile:= ExtractFilePath(Application.ExeName) + NotesFile;
  20.  if not FileExists(NotesFile) then
  21.   // nothing to do
  22.   exit;
  23.  
  24. try
  25.  try
  26.   NotesFileStream:= TFileStream.Create(NotesFile, fmOpenRead or fmShareDenyNone)
  27.  except
  28.   on EFOpenError do
  29.   begin
  30.    MessageDlgPos('Notes file is used by another program and cannot be opened.'
  31.                  + LineEnding + 'New notes cannot be saved.',
  32.                  mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  33.    exit;
  34.   end;
  35.  end;
  36.  LineReader:= TStreamReader.Create(NotesFileStream);
  37.  
  38.  // parse the file to the end
  39.  while not LineReader.Eof do
  40.  begin
  41.   skipNote:= false;
  42.   inc(rowCounter);
  43.   LineReader.ReadLine(ReadLine);
  44.   StringArray:= ReadLine.Split(#9);
  45.   // the first part is the series name the second one the point index
  46.   series:= (FindComponent(StringArray[0]) as TChartSeries);
  47.   if series = nil then
  48.   begin
  49.    MessageDlgPos('In line ' + IntToStr(rowCounter) + ' "' + StringArray[0]
  50.                  + '" is no valid chart series name.'
  51.                  + LineEnding + 'This note could not be read.',
  52.                  mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  53.    skipNote:= true;
  54.   end;
  55.   if not skipNote then
  56.   begin
  57.    if not TryStrToInt(StringArray[1], PointIndex) then
  58.    begin
  59.     MessageDlgPos('Point index in line ' + IntToStr(rowCounter)
  60.                   + ' is no valid integer.' + LineEnding
  61.                   + 'This note could not be read.',
  62.                   mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  63.     skipNote:= true;
  64.    end;
  65.   if PointIndex < 0 then
  66.    begin
  67.    MessageDlgPos('Point index in line ' + IntToStr(rowCounter)
  68.                   + ' is negative.' + LineEnding
  69.                   + 'This note could not be read.',
  70.                  mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  71.     skipNote:= true;
  72.    end
  73.    else if PointIndex > series.Count then
  74.    begin
  75.     MessageDlgPos('Point index in line ' + IntToStr(rowCounter)
  76.                   + ' is "' + IntToStr(PointIndex)
  77.                   + '" and thus larger than "' + IntToStr(series.Count)
  78.                   + '", the number of points in the series.'
  79.                   + LineEnding + 'This note could not be read.',
  80.                   mtError, [mbOK], 0, MousePointer.X, MousePointer.Y);
  81.     skipNote:= true;
  82.    end;
  83.   end; // end if not skipNote
  84.  
  85.   // now read as many lines until there is an empty line (2 subsequent LineEndings)
  86.   OutputLine:= '';
  87.   repeat
  88.    inc(rowCounter);
  89.    LineReader.ReadLine(ReadLine);
  90.    OutputLine:= OutputLine + ReadLine + LineEnding;
  91.   // we can have an empty line for a deleted note, therefore until we
  92.   // have at least 2 LineEndings
  93.   until ReadLine.IsEmpty and (Length(OutputLine) >= 2 * Length(LineEnding));
  94.   // remove the two LineEndings at the end
  95.   OutputLine:= Copy(OutputLine, 0, Length(OutputLine) - 2 * Length(LineEnding));
  96.   // set the mark
  97.   if not skipNote then
  98.    series.Source[PointIndex]^.Text:= OutputLine;
  99.  end; // end of while not LineReader.Eof do
  100.  
  101. finally
  102.  LineReader.Free;
  103.  NotesFileStream.Free;
  104.  // force a redraw of the chart (chart name is "SIXCH")
  105.  SIXCH.Invalidate;
  106. end;
  107.  
  108. end;

I will open a new thread for the axis number formatting.
« Last Edit: July 27, 2022, 08:42:01 pm by Muso »

wp

  • Hero Member
  • *****
  • Posts: 11858
Re: adding text notes to charts
« Reply #16 on: July 15, 2022, 01:14:24 pm »
However, I have another thing I would like to improve and maybe there is already a method to do this:
- Since the measurements take weeks, I added a feature to change the live chart x-axis unit between minutes, hours and days. But now there happens something, one zooms in to see what is happening. To see a sensible x-value, one switches the unit to minutes but sees then then of course huge numbers, see attached.So maybe I could retranslate the x-axis unit somehow to the format day:hour:minute. I searched the forum and found this post from you:
https://forum.lazarus.freepascal.org/index.php/topic,33570.msg217785.html#msg217785
My application requires that the measurement stats at zero, so the data points x-values must be minutes, only the display should be different, if the user likes to.
Maybe I can just use https://www.freepascal.org/docs-html/rtl/sysutils/formatdatetime.html to reformat the x-axis labels without any internal chart recalculation? If so this would be perfect because my charts have quickly 100k data points.
In the attachment there is a demo project in which the time axis labels format changes depending on the time range displayed. Not 100% perfect, yet, but maybe you get the idea how this could be achieved. Basically it uses a TDateTimeIntervalChartSource (which requires the x values to be in units of days (TDateTime)), and it simply changes the DateTimeFormat property of the source according to the time range. Unfortunately, FPC's FormatDateTime() function does not allow days in interval mode, and this makes the display of day count more complicated: I remove the DateTimeIntervalchartSource in this case and implement an OnGetMarkText handler to format the day interval correctly (unfortunately I did not manage to get "nice" interval labels - but I am quite sure that it must be possible...).

 

TinyPortal © 2005-2018