2012-03-10 15 views
6

Używam FastReport i potrzebuję podglądu/drukowania siatek z więcej niż 1000 rzędów i mam pewne problemy z wydajnością. Zazwyczaj używam TfrxCrossObject do przygotowania siatki, ponieważ użytkownik końcowy może zmienić wygląd siatki (używane kolumny, nazwa kolumny, rozmiar), więc muszę mieć wydruk dynamiczny. Przetestowałem prostą siatkę (16 kol. X2000 wierszy) i potrzeba więcej niż 10 sekund, aby zaprezentować pierwszą stronę podglądu. Czy masz pomysł na poprawę wyników?Występy z FastReport TFrxCrossObject i dużymi siatkami (> 1000 wierszy)

EDIT: Jak powiedział w niektórych odpowiedziach, problem: jak stworzyć „dynamicznie” siatkę (z same kolumny nazw i rozmiarów, które mam na ekranie) w FastReport bez użycia TFrxCrossObject która wydaje się nie mieć bardzo wydajny. Mogę przyznać się do wszystkich rozwiązań, takich jak użycie DataSet lub ulepszenie TfrxCrossObject.

Kod testu:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
    frxClass, StdCtrls, Grids, frxCross; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    StringGrid1: TStringGrid; 
    frxCrossObject1: TfrxCrossObject; 
    frxReport1: TfrxReport; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure frxReport1BeforePrint(c: TfrxReportComponent); 
    end; 

var 
    Form1: TForm1; 

implementation 
{$R *.DFM} 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    i, j: Integer; 
begin 
    for i := 1 to 16 do 
    for j := 1 to 2000 do 
     StringGrid1.Cells[i - 1, j - 1] := IntToStr(i * j); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    frxReport1.ShowReport; 
end; 

procedure TForm1.frxReport1BeforePrint(c: TfrxReportComponent); 
var 
    Cross: TfrxCrossView; 
    i, j: Integer; 
begin 
    if c is TfrxCrossView then 
    begin 
    Cross := TfrxCrossView(c); 
    for i := 1 to 16 do 
     for j := 1 to 2000 do 
     Cross.AddValue([i], [j], [StringGrid1.Cells[i - 1, j - 1]]); 
    end; 
end; 
end. 
+1

Polecam opublikowanie tego pytania na forach FastReports. Jeśli używasz AQTime, możesz go użyć do profilowania aplikacji i przynajmniej zobaczyć, GDZIE przeważa większość tego czasu. AQtime jest zawarty w niektórych wersjach Delphi XE i XE2, więc możesz już go mieć. –

+3

spróbuj użyć ClientDataSet zamiast "crossview". – teran

+1

Najprostszym sposobem sprawdzenia wydajności kodu jest użycie 'GetTickCount' przed i po częściach kodu i porównanie wartości. Ale najpierw spróbuj użyć 'BeginUpdate' /' EndUpdate' podczas wypełniania StringGrid. – LightBulb

Odpowiedz

4

tabeli krzyżowej mają wiele napowietrznych. Oto wersja UserDataSet:

  1. Wystarczy wrzucić 1 stringgrid, przycisk 1, 1, 1 frxUserDataSet frxReport w formularzu.

  2. Ustaw zdarzenia frxUserDataSet, Formularz OnCreate i Buttom OnClick jak poniżej kod.

  3. Nie ma potrzeby projektowania raportu ani ustawiania żadnych właściwości, wszystkie zostaną ustawione lub wygenerowane w czasie wykonywania.

Wygląda na to, że jest szybszy niż wersja z krzyżową zakładką, ale potrzebujesz więcej kodowania i utraty funkcjonalności CrossObject.

Edytuj: Dodaj komentarze i popraw błędy obliczeń PaperWidth.

Edycja2: Dodaj wersję do wydruku, która dzieli dane na strony.

wersja

Zobacz Friendly pokaz w 1 jednej stronie jako stringgrid:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, frxClass, Grids, StdCtrls; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    StringGrid1: TStringGrid; 
    frxReport1: TfrxReport; 
    frxUserDataSet1: TfrxUserDataSet; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure frxUserDataSet1Next(Sender: TObject); 
    procedure frxUserDataSet1GetValue(const VarName: string; var Value: Variant); 
    procedure frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean); 
    procedure frxUserDataSet1First(Sender: TObject); 
    private 
    X, Y, TCol, TRow : Integer; 
    IsEof : Boolean; 
    CW, CH, PF : Double; 
    Page : TfrxReportPage; 
    MDB : TfrxMasterData; 
    Memo : TfrxMemoView; 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 


procedure TForm1.Button1Click(Sender: TObject); 
var 
    BW : Double; 
begin 
    IsEof := False; 
    Page.PaperWidth := CW * TCol + 20; // EndlessWidth seems not work with band column 
    MDB.SetBounds(0,0, CW * PF * TCol, CH * PF); 
    MDB.Columns := TCol; 
    MDB.ColumnWidth := CW * PF; 
    frxReport1.ShowReport; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    i, j : Integer; 
begin 
    CW := 12; // Cell Width in mm 
    CH := 5; // Cell Height in mm 
    PF := 3.7794; // Pixie Factor i.e. the conversion of mm to FR component measurement 
    TCol := 2000; // Total Column 
    TRow := 16; // Total Row 

    for i := 1 to TRow do 
    for j := 1 to TCol do 
     StringGrid1.Cells[i - 1, j - 1] := IntToStr(i * j); 

    frxUserDataSet1.Fields.Text := 'Data'; 
    frxReport1.Clear; 
    frxReport1.DataSets.Add(frxUserDataSet1); 
    Page := TfrxReportPage.Create(frxReport1); 
    Page.CreateUniqueName; 
    Page.TopMargin := 10; 
    Page.BottomMargin := 10; 
    Page.LeftMargin := 10; 
    Page.RightMargin := 10; 
    Page.EndlessHeight := True; 
    Page.EndlessWidth := True; 
    MDB := TfrxMasterData.Create(Page); 
    MDB.DataSet := frxUserDataSet1; 
    Memo := TfrxMemoView.Create(MDB); 
    Memo.SetBounds(0,0,CW * PF,CH * PF); 
    Memo.Memo.Text := '[frxUserDataSet1."Data"]'; 
    Memo.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom]; 
end; 

procedure TForm1.frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean); 
begin 
    Eof := IsEof; 
end; 

procedure TForm1.frxUserDataSet1First(Sender: TObject); 
begin 
    X := 0; 
    Y := 0; 
end; 

procedure TForm1.frxUserDataSet1GetValue(const VarName: string; var Value: Variant); 
begin 
    Value := StringGrid1.Cells[X,Y]; 
end; 

procedure TForm1.frxUserDataSet1Next(Sender: TObject); 
begin 
    If Y = TCol - 1 then 
    begin 
    if X = TRow - 1 then 
     IsEof := True; 
    Inc(X); 
    Y := 0; 
    end 
    else 
    Inc(Y); 
end; 

end. 

do wydruku wersji, która jest nieco bardziej skomplikowane i jednostkowe dane z różnych stron do druku:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, frxClass, Grids, StdCtrls; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    StringGrid1: TStringGrid; 
    frxReport1: TfrxReport; 
    frxUserDataSet1: TfrxUserDataSet; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure frxUserDataSet1Next(Sender: TObject); 
    procedure frxUserDataSet1GetValue(const VarName: string; var Value: Variant); 
    procedure frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean); 
    procedure frxUserDataSet1First(Sender: TObject); 
    private 
    X, Y, TCol, TRow, RPP, ColBreak : Integer; 
    IsEof : Boolean; 
    CW, CH, PF : Double; 
    Page : TfrxReportPage; 
    MDB : TfrxMasterData; 
    Memo : TfrxMemoView; 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

uses Math; 

{$R *.dfm} 


procedure TForm1.Button1Click(Sender: TObject); 
var 
    BW : Double; 
begin 
    IsEof := False; 
    RPP := Ceil((Page.PaperHeight - Page.TopMargin - Page.BottomMargin)/CH) - 1; // Row per page 
    ColBreak := RPP; // break to next column 

    frxReport1.ShowReport; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    i, j : Integer; 
begin 
    CW := 12; // Cell Width in mm 
    CH := 5; // Cell Height in mm 
    PF := 3.7794; // Pixil Factor i.e. the conversion of mm to FR component measurement 
    TCol := 2000; // Total Column 
    TRow := 16; // Total Row 

    for i := 1 to TRow do 
    for j := 1 to TCol do 
     StringGrid1.Cells[i - 1, j - 1] := IntToStr(i * j); 

    frxUserDataSet1.Fields.Text := 'Data'; 
    frxReport1.Clear; 
    frxReport1.DataSets.Add(frxUserDataSet1); 
    Page := TfrxReportPage.Create(frxReport1); 
    Page.CreateUniqueName; 
    Page.TopMargin := 10; 
    Page.BottomMargin := 10; 
    Page.LeftMargin := 10; 
    Page.RightMargin := 10; 
    Page.Columns := Ceil(Page.PaperWidth/CW); 
    MDB := TfrxMasterData.Create(Page); 
    MDB.DataSet := frxUserDataSet1; 
    MDB.SetBounds(0,0, CW * PF, CH * PF); 
    Memo := TfrxMemoView.Create(MDB); 
    Memo.Align := baClient; 
    Memo.Memo.Text := '[frxUserDataSet1."Data"]'; 
    Memo.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom]; 
end; 

procedure TForm1.frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean); 
begin 
    Eof := IsEof; 
end; 

procedure TForm1.frxUserDataSet1First(Sender: TObject); 
begin 
    X := 0; 
    Y := 0; 
end; 

procedure TForm1.frxUserDataSet1GetValue(const VarName: string; var Value: Variant); 
begin 
    Value := StringGrid1.Cells[X,Y]; 
end; 

procedure TForm1.frxUserDataSet1Next(Sender: TObject); 
begin 
    If X = TRow - 1 then 
    begin 
    if Y = TCol - 1 then 
     IsEof := True 
    else 
    begin 
     frxReport1.Engine.NewColumn; 
     Inc(Y); 
     X := ColBreak - RPP; 
    end; 
    end 
    else if (X = ColBreak - 1) then 
    begin 
    if Y = TCol - 1 then 
    begin 
     frxReport1.Engine.NewPage; 
     ColBreak := ColBreak + RPP; 
     Y := 0; 
    end 
    else 
     Inc(Y); 
    frxReport1.Engine.NewColumn; 
    X := ColBreak - RPP; 
    end 
    else 
    Inc(X); 
end; 

end. 
+0

W moim notebooku (i5 2.4GHz, 4GRam), oryginalna wersja pokazuje pierwszą stronę na 2,5 sekundy i jest gotowa (cała strona załadowana) na 9 sekund. Moja wersja Display (1 duże strony) gotowa na 2,5 sekundy. Moja wersja do druku natychmiast i natychmiast wyświetla pierwszą stronę (112 stron) w 1,5 sekundy. 2000 Kolumna X 200 wiersza natychmiast i natychmiast wyświetla pierwszą stronę (448 stron) w 10,5 sekundy. 2000 Kol. X 2000 Wiersz natychmiast wyświetla pierwszą stronę i gotowy (4144 stron) po 104 sekundach. – Justmade

+0

Wydaje się być właściwą drogą. Sprawdzę to dzisiaj. – philnext

+0

@philnext Jeśli używasz DBGrid/DataSet, Y może zmienić na indeks pola i Inc (X) zmienić na DataSet.Next i możesz potrzebować Bookmark do dzielenia kolumn. Tak więc w zdarzeniu getvalue można podać DataSet.Fields [Y] .Value. – Justmade

2

Twój fragment kodu odpowiada nieco zmienionemu demo wersji FastReport z PrintStringGrid (RowCount = 2000 zamiast 16).

Używanie TStringGrid jako kontenera nie jest dobrym pomysłem, jeśli masz do czynienia z dużymi danymi: To powinno być używane tylko w celu prezentacji.

Użyj zbioru danych w pamięci (np. ClientDataset jako teran zasugerowanego w wątku komentarza do pytania), nadal możesz prezentować swoje duże dane do TStringGrid, jeśli jest to obowiązkowe, ale właściwsze jest TDBGrid.

Iterowanie dużego TDataset przebiega szybko, niż robi to samo z samym TStringGrid.

PrintTable demo FastReport może służyć jako punkt wyjścia, dostosowując to zostanie wam jako ćwiczenie wiedząc, że wykorzystuje te same składniki jak PrintStringGrid Demo:

  • TfrxReport i
  • TfrxCrossObject gdzie następuje iteracja.
+1

czy mogę zapytać, czy Clientdataset (grid) ma obecnie 100 wierszy * 100 col z danymi, a użytkownik chce dodać jeszcze 1 kolumnę, jak dodać kolumnę do Clientdataset? A także, jak dodać tę kolumnę do raportu? Jeśli korzystam z DBCrossTab, jestem pewien, że nie będzie poprawy wydajności z powodu kosztów obliczeń i sortowania CrossTab. – Justmade

+0

Tak, IRL, używam TDBGrid, ale TStringGrid był tutaj łatwiejszy do wyjaśnienia mojego problemu i do odpytywania w celu przetestowania go. Muszę do tego samego perf. problemy z TDBGrid. – philnext

Powiązane problemy