Recent

Author Topic: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row  (Read 6084 times)

Relativity

  • Jr. Member
  • **
  • Posts: 96
I have a simple TStringGrid with 5 columns and a fixed row on the top containing the titles.
Using LoadFromCSVFile, if I set the WithHeader parameter to false then the file's first record is not loaded or, at least, I can not see it: I see the rows in the grid only from the second on.
If I set the WithHeader parameter to true then the first file's record is loaded in my fixed row and replaces the titles, which are no more visible.

Can anybody help me get also the file's first record loaded in the grid and nevertheless preserve the titles in the fixed row ?

Thank you in advance.
"How'm I gonna get through?"
  -- Pet Shop Boys

wp

  • Hero Member
  • *****
  • Posts: 6662
The behavior you describe sounds reasonable. I think you have to import with WithHeader=false to load all lines and keep the FixedRow unchanged. To get rid of the file's title line, just delete the first data row of the grid (StringGrid1.Rows.Delete(1); 1 is the row index after the FixedRow ) after calling LoadFromCSVfile.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Relativity

  • Jr. Member
  • **
  • Posts: 96
My file has no title line.
And that's the problem.
At this point, I guess the best could be to add a title line to my file on purpose and then apply your method.

"How'm I gonna get through?"
  -- Pet Shop Boys

wp

  • Hero Member
  • *****
  • Posts: 6662
Then I misunderstood you. Having WithHeader=false should import all lines into the non-fixed rows of the grid. Your first post say that it does not. This is a bug. Please prepare a little demo project along with a sample file and write a bug report (or post them here, and I'll do it for you).

In the demo project, pack the lpi, lpr, pas and lfm and data files into a single zip file which you can upload. No exe, ppu etc please.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

wp

  • Hero Member
  • *****
  • Posts: 6662
I did some experiments on my own and found: No is not a bug, it is a misunderstanding. The parameter "WithHeaders" refers to the datafile, not to the grid; it means "there is a header line in the file which is to be included in the grid, or not". Since you want to load all data lines "WithHeaders" must definitely be true.

As you notice you are overwriting your prepared headers this way. So after you imported the file you only have to insert a row before the very first grid row to become the new fixedrow. The grid has a handly method for this: InsertRowWithValues

Code: [Select]
  StringGrid1.LoadFromCSVFile('data.txt', ';', true);
  StringGrid1.InsertRowWithValues(0, ['', 'Title 1', Title 2']);

And of course: don't prepare the fixedRow before the import since it will be overwritten.
« Last Edit: July 06, 2015, 12:21:03 pm by wp »
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Relativity

  • Jr. Member
  • **
  • Posts: 96
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #5 on: August 27, 2015, 02:39:30 pm »
wp, using your method, when the TStringGrid is shown I don't see the row with the titles I have inserted following your suggestion, but I see twice the first row of my file.
There could be an error of mine somewhere and I must investigate, but I have to say that in my program each column has a title, defined through the object inspector, that disappear when I use your method.

Should I remove these column-titles, seen that I insert the same captions through your InsertRowWithValues-statement ?
However, the big question is: are the column-titles and the fixed row the same thing or not ? Or are they someway tangled ?

"How'm I gonna get through?"
  -- Pet Shop Boys

wp

  • Hero Member
  • *****
  • Posts: 6662
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #6 on: August 27, 2015, 03:58:59 pm »
Please create a little demo which shows the issue. Pack the pas, lfm, lpi and lpr files along with a sample data file into a zip and upload it here. Important: the project must be compilable. Otherwise there will be only wild guesses.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Relativity

  • Jr. Member
  • **
  • Posts: 96
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #7 on: August 28, 2015, 01:21:20 pm »
wp, I have made a demo.
In the .txt file there are 5 records related to 5 citizens.
The TStringGrid has one title row.
My wish: that all 5 records be shown under the titles and the titles must not disappear.

The flag WithHeader can be true or false (hence 2 possibilities).
One can use your InsertRowWithValues-statement or not (hence additional 2 possibilities): therefore one has 2x2 = 4 possible combinations.
But in no one I get what I wish.

Thank you for your time and for everything.
"How'm I gonna get through?"
  -- Pet Shop Boys

derek.john.evans

  • Guest
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #8 on: August 28, 2015, 01:47:33 pm »
Try
Code: [Select]
  StringGrid1.FixedRows := 0;
  StringGrid1.LoadFromCSVFile('People.txt', ';', True);
  StringGrid1.InsertRowWithValues(0, ['Family name', 'Name', 'State', 'Tel. number']);
  StringGrid1.FixedRows := 1;   

wp

  • Hero Member
  • *****
  • Posts: 6662
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #9 on: August 28, 2015, 05:43:33 pm »
I am sure this is a bug. The LoadFromCSV* methods require more parameters to specify in which line data start and in which line the header is found. I am trying to write a patch...
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

wp

  • Hero Member
  • *****
  • Posts: 6662
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #10 on: August 28, 2015, 10:56:26 pm »
Patch submitted: http://bugs.freepascal.org/view.php?id=28582

If you do not want to wait until it is included in Lazarus you can patch your version of "grids.pas" by replacing the code of "TCustomStringGrid.LoadFromCSVStream" and ".LoadFromCSVFiel" by this:

Code: [Select]
procedure TCustomStringGrid.LoadFromCSVStream(AStream: TStream;
  ADelimiter: Char=','; WithHeader: boolean=true; SkipLines: Integer=0);
var
  MaxCols: Integer = 0;
  MaxRows: Integer = 0;
  LineCounter: Integer = -1;

  function RowOffset: Integer;
  begin
    // return row offset of current CSV record (MaxRows) which is 1 based
    if withHeader then
      result := Max(0, FixedRows-1)  + Max(MaxRows-1, 0)
    else
      result := FixedRows + Max(MaxRows-1, 0);
  end;

  procedure NewRecord(Fields:TStringlist);
  var
    i, aRow: Integer;
  begin
    inc(LineCounter);
    if (LineCounter < SkipLines) then
      exit;

    if Fields.Count=0 then
      exit;

    // make sure we have enough columns
    if MaxCols<Fields.Count then
      MaxCols := Fields.Count;
    if Columns.Enabled then begin
      while Columns.VisibleCount<MaxCols do
        Columns.Add;
    end
    else begin
      if ColCount<MaxCols then
        ColCount := MaxCols;
    end;

    // setup columns captions of custom columns if they are enabled
    if (MaxRows = 0) then
      if WithHeader then
      begin
        if Columns.Enabled then
          for i:=0 to Fields.Count-1 do Columns[i].Title.Caption:=Fields[i]
        else
          for i:=0 to Fields.Count-1 do Cells[i, 0] := Fields[i];
        inc(MaxRows);
        exit;
      end;

    // Make sure we have enough rows
    Inc(MaxRows);
    aRow := RowOffset;
    if aRow>RowCount-1 then
      RowCount := aRow + 20;

    // Copy line data to cells
    for i:=0 to Fields.Count-1 do
      Cells[i, aRow] := Fields[i];
  end;

begin
  BeginUpdate;
  try
    LCSVUtils.LoadFromCSVStream(AStream, @NewRecord, ADelimiter);

    // last row offset + 1 (offset is 0 based)
    RowCount := RowOffset + 1;

    if not Columns.Enabled then
      ColCount := MaxCols
    else
      while Columns.Count > MaxCols do
        Columns.Delete(Columns.Count-1);

  finally
    EndUpdate;
  end;
end;

procedure TCustomStringGrid.LoadFromCSVFile(AFilename: string;
  ADelimiter: Char=','; WithHeader: boolean=true; SkipLines: Integer=0);
var
  TheStream: TFileStreamUtf8;
begin
  TheStream:=TFileStreamUtf8.Create(AFileName,fmOpenRead or fmShareDenyWrite);
  try
    LoadFromCSVStream(TheStream, ADelimiter, WithHeader, SkipLines);
  finally
    TheStream.Free;
  end;
end;

I think the IDE will automatically recompile the LCL after this replacement. But if not do it manually: "Tools" / "Build Lazarus with profile..." - this may take some time, then Lazarus restarts using the new grids unit.

P.S.
Make a backup copy of grids.pas before changing anything - just in case...
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

derek.john.evans

  • Guest
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #11 on: August 29, 2015, 04:40:49 am »
Might be easier to have a standalone reader.

Code: Pascal  [Select]
  1. procedure StringGridLoadFromCSV(const AStringGrid: TStringGrid; const AFileName: TFileName; const ADelimiter: Char);
  2. var
  3.   LIndex, LRow, LCol: Integer;
  4.   LLine: TStrings;
  5. begin
  6.   with TStringList.Create do begin
  7.     try
  8.       LoadFromFile(AFileName);
  9.       for LIndex := Count - 1 downto 0 do begin
  10.         if Strings[LIndex] = EmptyStr then begin
  11.           Delete(LIndex);
  12.         end;
  13.       end;
  14.       AStringGrid.RowCount := AStringGrid.FixedRows + Count;
  15.       LLine := TStringList.Create;
  16.       try
  17.         LLine.Delimiter := ADelimiter;
  18.         LLine.StrictDelimiter := True;
  19.         for LRow := AStringGrid.FixedRows to AStringGrid.RowCount - 1 do begin
  20.           LLine.DelimitedText := Strings[LRow - AStringGrid.FixedRows];
  21.           for LCol := AStringGrid.FixedCols to AStringGrid.ColCount - 1 do begin
  22.             LIndex := LCol - AStringGrid.FixedCols;
  23.             if LIndex < LLine.Count then begin
  24.               AStringGrid.Cells[LCol, LRow] := LLine[LIndex];
  25.             end;
  26.           end;
  27.         end;
  28.       finally
  29.         FreeAndNil(LLine);
  30.       end;
  31.     finally
  32.       Free;
  33.     end;
  34.   end;
  35. end;
  36.  
« Last Edit: October 02, 2015, 03:21:10 am by Geepster »

Relativity

  • Jr. Member
  • **
  • Posts: 96
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #12 on: September 01, 2015, 08:29:08 am »
Thanks to wp and derek.john.evans.
I prefer not to touch Lazarus's code, so I adopted the solution suggested by derek.john.evans in his reply #8.
When Lazarus patched version is released I will change my code. It's ok.
"How'm I gonna get through?"
  -- Pet Shop Boys

wp

  • Hero Member
  • *****
  • Posts: 6662
Re: Filling TStringGrid using LoadFromCSVFile: Problems with the fixed row
« Reply #13 on: September 01, 2015, 10:23:47 pm »
The patch in my bugreport has been applied now. If you work with Lazarus trunk you should update your working copy to get the fixed grids unit right away. If you prefer to use the release version of Lazarus you'll have to wait until version 1.6, though.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10