Rave Reports - raportowanie za pomocą kodu

Artur

W tym artykule postaram się przybliżyć ideę raportowania w Delphi za pomocą Rave Reports. Wielu uzytkowników przyzwyczajonych do Quick Reports z poprzednich wersji Delphi ma spore trudności w tworzeniu raportów za pomocą Delphi 7, bowiem w tej wersji zrezygnowano z Quick Reports i wprowadzono nową ideę raportowania za pomocą RAve Reports (Sposób działania Rave jest zupełnie inny niż Quick Reports). W tym artykule zaprezentuję sposoby tworzenia raportów za pomocą kodu (drugim sposobem raportowania za pomocą Rave jest wykorzystanie narzędzia Visual Designer - o tym moze będzie kolejny artykuł:) )

A więc zaczynamy

Spróbujmy stworzyć najprostszy raport, który wyświetli nam jakiś tekst, powiedzmy "To jest pierwszy raport stworzony za pomocą Rave Reports"

Otwieramy Delphi 7 Enterprise (bo chyba w pozostałych wersjach nie ma Rave - trzeba wtedy pobrać ze strony http://www.borland.com)
przechodzimy na zakładkę Rave i umieszczamy na formie komponent RvSystem1, oraz przycisk Button1 z zakładki Standard (możemy zmianić właściwosć Caption Buttona na "Raport")
Ok, to interfejs już mamy:)

Teraz zajmijmy się kodowaniem
Stwórzmy procedurę, która będzie odpowiedzialna za wygląda raportu
W sekcji private Formy umieścmy nagłówek procedury

procedure DrukujRaport(Report: TBaseReport);

i pod słowem implementation napiszmy kod procedury

procedure TForm1.DrukujRaport(Report: TBaseReport);
begin
  with Report do
  begin
    SetFont('Times New Roman', 20);   //ustawiamy czcionkę na times New Roman o rozmiarze 20
    GotoXY(1,1);		      //przechodzimy do pola 1, 1 na raporcie
    Print('To jest pierwszy raport stworzony za pomocą Rave Reports'); //wyświetlamy na raporcie tekst
  end;
end;

Parametr Sender wskazuje na raport, który właśnie tworzymy; może tu użyć obiekt klasy TBaseReport. Zawiera on wszystkie metody, których będziemy potrzebować potrzebować, by przesłać informacje do tego konkretnego raportu.
SetFont określa typ i krój czcionki. Instrukcja gotoxy ustawia kursor w punkcie o współrzędnych (1,1). Współrzędne te wyrażone są w jednostkach określonych we właściwościach SystemPrinter.Units komponentu RvSystem, zaś domyślną jednostką są cale. Możemy to zmienić na bardziej polską jednostkę - na centymetry. W tym celu należy ustawić właściwość SystemPrinter.Units komponentu RvSystem na unCM i możemy wybrać wielkość tej jednostki we właściwosci SystemPrinter.UnitsFector. Ustawmy tę wielkość na 2. Dla przykładu, gdy wartość UnitsFactor jest ustawiona na 2, wówczas 1 jednostka odpowiada 2 centymetrom.

Teraz musimy wywołać napisana przez nas procedurę w zdarzeniu onPrint komponentu RvSystem1. Robimy to w następujacy sposób

procedure TForm1.RvSystem1Print(Sender: TObject);
begin
    DrukujRaport(Sender as TBaseReport);
end;

I na koniec musimy napisać kod zdarzenia dla naciśnięcia przycisku - po naciśnięciu Button1 ma być generowany raport. Robimy to w następujacy sposób

procedure TForm1.Button1Click(Sender: TObject);
begin
    RvSystem1.Execute
end;

I już możemy testować nasz program do generowania raportów. Poniżej przedstawiam cały kod programu

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, RpDefine, RpBase, RpSystem;

type
  TForm1 = class(TForm)
    RvSystem1: TRvSystem;
    Button1: TButton;
    procedure RvSystem1Print(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure DrukujRaport(Report: TBaseReport);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DrukujRaport(Report: TBaseReport);
begin
  with Report do
  begin
    SetFont('Times New Roman', 20);
    GotoXY(1,1);
    Print('To jest pierwszy raport stworzony za pomocą Rave Reports');
  end;
end;

procedure TForm1.RvSystem1Print(Sender: TObject);
begin
    DrukujRaport(Sender as TBaseReport);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    RvSystem1.Execute
end;

end.

No to nadszedł czas na bardziej rozbudowane raporty.
ZAłóżmy, że mamy plik tekstowy zawierajacy informacje o naszych kolegach/koleżankach w takiej postaci:

imie
nazwisko
e-mail
wiek

np. ja utworzyłem sobie plik tekstowy koledzy.txt i wpisałem do niego:

Asia
Abacka
[email protected]
21
Basia
Babacka
[email protected]
20
Szymon
Cabacki
[email protected]
19
Wiola
Dabacka
[email protected]
18

Chcę teraz wyświetlić raport, który wypisze mi dane moich znajomych w postaci kolumn. A wiec zaczynamy tworzenie interfejsu. Tak jak poprzednio umieszczamy na Formie RvSystem1 i Button1. No i już mamy interfejs:) Teraz pora napisać kod naszej aplikacji. W sekcji private Formy umieszczamy deklarację procedury, która będzie nam generować raport.

procedure Raportkolegow(Report : TBaseReport);

Następnie orpogramowujemy zdarzenia onPrint dla komponentu RvSystem1 i onClick dla Buttona. Są one identyczne jak w poprzednim przykładzie, więc nie będę ich omawiał, umieszczam tylko ich kod.

procedure TFormMain.RvSystemPrint(Sender: TObject);
begin
    Raportkolegow(Sender as TBaseReport);
end;

procedure TFormMain.Button1Click(Sender: TObject);
begin
    RvSystem.Execute;
end;

Zostało nam jeszcze oprogramowanie procedury Raportkolegow. Robimy to w następujacy sposób:

procedure TFormMain.Raportkolegow(Report: TBaseReport);
var
  f:TextFile;   //zmienna plikowa
  imie,nazwisko,e_mail,wiek:string; //zmienne łańcuchowe, które będziemy czytać z pliku
begin
    Report.SetFont('Arial', 15);//ustawiamy czcionkę na arial o wielkości 15
    Report.NewLine;             //przechodzimy do nowej linii
    Report.PrintCenter('Dane moich znajomych', 4);//tytuł raportu, 4 oznacza że tytuł będzie 4 cale od lewego brzegu raportu
    Report.NewLine;  //przechodzimy do nowej linii
    Report.NewLine;  //jeszcze raz przechodzimy do nowej linii
    Report.ClearTabs; //będziemy tworzyć nowy raport tabularyczny, więc ta instrukcja jest konieczna

    //ustawiamy właściwości kolumn, jednostkami są cale
    Report.SetTab(0.2, pjLeft, 1.7, 0, 0, 0);{kulumna zaczyna się 0,2 cala od lewego brzegu raportu, będzie wyrównana do lewej i długość jej będzie wynosić 1,7 cala, pierwsze zero określa, że tekst w kolumnach nie będzie miał marginesów, drugie zero oznacza, że nie będzie żadnego obramowania kolumny, wpisując rózne liczby uzyskujemy rózne obramowanie, ostatnie zero określa, że tło kolumny będzie białe, jeśli wpiszemy wartośc 25 uzyskamy szare tło, jeśli 100, tło będzie czarne}
    Report.SetTab(1.9, pjLeft, 2.1, 0, 0, 0);
    Report.SetTab(4.0, pjLeft, 2.5, 0, 0, 0);
    Report.SetTab(6.5, pjRight, 1.5, 0, 0, 0);
    Report.SetFont('Arial', 10); //ustawiamy wielkość czcionki dla nagłówków kolumn
    Report.Bold := True; //nagłówki będą pisane czcionką pogrubioną

    //tworzymy odpowiednie nagłówki
    Report.PrintTab('Imię'); //nagłówek pierwszej kolumny
    Report.PrintTab('Nazwisko'); //nagłowek drugiej kolumny
    Report.PrintTab('Adres e-mail'); //nagłówek trzeciej kolumny
    Report.PrintTab('Wiek'); //nagłowek czwartej kolumny
    Report.Bold := False; //reszta będzie pisana czcionką "niepogrubioną" czyli normalną
    Report.NewLine; //przechodzimy do nowej linii

    //teraz będziemy odczytywać z pliku dane i umieszczać je w raporcie
    assignfile(f,'koledzy.txt');//kojarzymy zmienną plikową z plikiem koledzy.txt
    reset(f); //otwieramy plik
    while not eof(f) do //dopóki są dane w pliku
    begin
        readln(f,imie); //czytaj z pliku imię
        Report.PrintTab(imie); //umieść w 1 kolumnie raportu imię
        readln(f,nazwisko); //czytaj z pliku nazwisko
        Report.PrintTab(nazwisko); //umieść nazwisko w 2 kolumnie
        readln(f,e_mail); //czytaj z pliku e-mail
        Report.PrintTab(e_mail); //umieść e-mail w 3 kolumnie
        readln(f,wiek); //czytaj z pliku wiek
        Report.PrintTab(wiek); //umieść wiek w 4 kolumnie
        Report.NewLine; //przejdź do nowej linii
    end;
    closefile(f); //zamykamy plik
end;

I już możemy generować raporty dotyczące naszych znajomych.
Zauważ, że zastosowaliśmy inne podejście: zamiast specyfikować koordynaty każdego elementu wydruku, posłużyłem się liniami i kolumnami jako wielkościami odniesienia. Wysokość linii zależy od wielkości użytej czcionki: każda jednostka to 1/72 cala(bo w tym projekcie używaliśmy cali), a więc linia drukowana czcionką o rozmiarze 10 będzie miała wysokość około 0,138 cala. Przeskok do kolejnej linii następuje po komendzie NewLine. Kolumny są zdefiniowane przez odwołania do metody SetTabs, a metoda PrintTab powoduje drukowanie tekstu w bieżącej kolumnie z przeskokiem do następnej kolumny.
Poniżej umieszczam cały kod programu

unit MainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, RpDefine, RpBase, RpSystem, StdCtrls, ExtCtrls;

type
  TFormMain = class(TForm)
    RvSystem: TRvSystem;
    Button1: TButton;
    procedure RvSystemPrint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    procedure Raportkolegow(Report : TBaseReport);
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

procedure TFormMain.RvSystemPrint(Sender: TObject);
begin
    Raportkolegow(Sender as TBaseReport);
end;

procedure TFormMain.Button1Click(Sender: TObject);
begin
    RvSystem.Execute;
end;

procedure TFormMain.Raportkolegow(Report: TBaseReport);
var
  f:TextFile;
  imie,nazwisko,e_mail,wiek:string;
begin
    Report.SetFont('Arial', 15);
    Report.NewLine;           
    Report.PrintCenter('Dane moich znajomych', 4);
    Report.NewLine;  
    Report.NewLine;  
    Report.ClearTabs;
    Report.SetTab(0.2, pjLeft, 1.7, 0, 0, 0);
    Report.SetTab(1.9, pjLeft, 2.1, 0, 0, 0);
    Report.SetTab(4.0, pjLeft, 2.5, 0, 0, 0);
    Report.SetTab(6.5, pjRight, 1.5, 0, 0, 0);
    Report.SetFont('Arial', 10);
    Report.Bold := True;
    Report.PrintTab('Imię');
    Report.PrintTab('Nazwisko');
    Report.PrintTab('Adres e-mail');
    Report.PrintTab('Wiek');
    Report.Bold := False;
    Report.NewLine;
    assignfile(f,'koledzy.txt');
    reset(f);
    while not eof(f) do
    begin
        readln(f,imie);
        Report.PrintTab(imie);
        readln(f,nazwisko);
        Report.PrintTab(nazwisko);
        readln(f,e_mail);
        Report.PrintTab(e_mail);
        readln(f,wiek);
        Report.PrintTab(wiek);
        Report.NewLine;
    end;
    closefile(f);
end;

end.

Za pomocą Reve Reports można także w raportach umieszczać obrazki. Zróbmy teraz raport naszych znajomych ze zdjeciami. Załóżmy, że mamy plik tekstowy Koledzy.txt o następującej strukturze

imię
nazwisko
e-mail
wiek
fotka (nazwa pliku *.bmp)

Ja na przykład mam taki plik

Asia
Abacka
[email protected]
21
asia.bmp
Basia
Babacka
[email protected]
20
basia.bmp
Zosia
Cabacka
[email protected]
19
zosia.bmp
Wiola
Dabacka
[email protected]
18
wiola.bmp

Przygotowałem sobie również bitmapy znajomych (asia.bmp, basia.bmp, zosia.bmp, wiola.bmp), które znajdują się w katalogu bieżącym aplikacji.

No to zabieramy sie za tworzenie interfejsu aplikacji - będzie on taki sam jak poprzednio - na Formie umieść RvSystem1 i Button1

Teraz nadszedł czas oprogramować zdarzenia.
W sekcji private Formy wpisz

procedure Raportkolegowzfotkami(Report : TBaseReport);

Następnie oprogramuj zdarzenia onPrint RvSystem1 i onClick Button1

procedure TFormMain.RvSystemPrint(Sender: TObject);
begin
    Raportkolegowzfotkami(Sender as TBaseReport);
end;

procedure TFormMain.Button1Click(Sender: TObject);
begin
    RvSystem.Execute;
end;

Tu nic się nie zmieniło. I nadszedł czas napisać procedurę Raportkolegowzfotkami

procedure TFormMain.Raportkolegowzfotkami(Report: TBaseReport);
var
  f:TextFile;//zmienna plikowa
  imie,nazwisko,e_mail,wiek,fotka:string;//zmienne tekstowe - bedziemy czytać z pliku
  Bitmap:TBitmap;//zmienna przechowywać będzie bitmapę
  i:double;//zmienna liczbowa
begin
    Report.SetFont('Arial', 15);//ustawiamy czcionkę na arial o wielkości 15
    Report.NewLine;             //przechodzimy do nowej linii
    Report.PrintCenter('Dane moich znajomych', 3.5);//tytuł raportu, 4 oznacza że tytuł będzie 4 cale od lewego brzegu raportu
    Report.NewLine;  //przechodzimy do nowej linii
    Report.NewLine;  //jeszcze raz przechodzimy do nowej linii
    Report.SetFont('Arial', 10);//ustawiamy wielkosć czcionki kolumn
    Report.Bold := True;//nagłówek pogrubiamy
    //tworzymy nagłowki kolumn - tym razem za pomocą PrintXY
    Report.PrintXY(0.2,0.7,'Imię');//na pozycji x=0.2 y=0.7 umieszczamy tekst Imię
    Report.PrintXY(1.2,0.7,'Nazwisko');
    Report.PrintXY(2.7,0.7,'Adres e-mail');
    Report.PrintXY(4.7,0.7,'Wiek');
    Report.PrintXY(5.7,0.7,'Fotka');
    Report.Bold := False;//reszta kolumn będzie niepogrubiona - normalna
    Report.NewLine;
    assignfile(f,'koledzy.txt');//kojarzymy zmienną plikową z plikiem
    reset(f);//otwieramy plik
    i:=0;//zmienna i będzie nam potrzebna do ustawiania wielkości y w kolejnych wierszach tabeli
    while not eof(f) do //dopóki nie ma końca pliku
    begin
        readln(f,imie); //czytamy imię z pliku
        Report.PrintXY(0.2,0.9+i,imie); //wyświetlamy imię na pozycji x=0.2, y=0.9+i
        readln(f,nazwisko); //czytamy nazwisko z pliku
        Report.PrintXY(1.2,0.9+i,nazwisko); wyświetlamy nazwisko na pozycji x=1.2, y=0.9+i
        readln(f,e_mail); //czytamy e-maila z pliku
        Report.PrintXY(2.7,0.9+i,e_mail); //wyświetlamy e-maila na pozycji x=2.7, y=0.9+i
        readln(f,wiek); //czytamy wiek z pliku
        Report.PrintXY(4.7,0.9+i,wiek); //wyświetlamy wiek na pozycji x=4.7, y=0.9+i
        readln(f,fotka); //czytamy nazwę pliku fotki z pliku
        Bitmap := TBitmap.Create; //dynamicznie tworzymy obiekt przechowujący bitmapę
        try
           Bitmap.LoadFromFile(fotka); //załadowywujemy odpowiednią fotke do obiektu Bitmap
           Report.PrintBitmap(5.7,0.9+i,1,1,Bitmap); //wyświetlamy bitmapę na pozycji x=5.7 y=0.9+i
        finally
           Bitmap.Free; //zwalniamy pamięć
        end;
        i:=i+1.5; //zwiększamy i o 1.5 - następny wiersz tabeli będzie 1.5 cala niżej
    end;
    closefile(f);//zamykamy plik
end;

Tak wyglada nasz raport
user image

A oto cały kod programu

unit MainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, RpDefine, RpBase, RpSystem, StdCtrls, ExtCtrls;

type
  TFormMain = class(TForm)
    RvSystem: TRvSystem;
    Button1: TButton;
    procedure RvSystemPrint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    procedure Raportkolegowzfotkami(Report : TBaseReport);
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

procedure TFormMain.RvSystemPrint(Sender: TObject);
begin
    Raportkolegowzfotkami(Sender as TBaseReport);
end;

procedure TFormMain.Button1Click(Sender: TObject);
begin
    RvSystem.Execute;
end;

procedure TFormMain.Raportkolegowzfotkami(Report: TBaseReport);
var
  f:TextFile;
  imie,nazwisko,e_mail,wiek,fotka:string;
  Bitmap:TBitmap;
  i:double;
begin
    Report.SetFont('Arial', 15);
    Report.NewLine;
    Report.PrintCenter('Dane moich znajomych', 3.5);
    Report.NewLine;
    Report.NewLine;
    Report.SetFont('Arial', 10);
    Report.Bold := True;
    Report.PrintXY(0.2,0.7,'Imię');
    Report.PrintXY(1.2,0.7,'Nazwisko');
    Report.PrintXY(2.7,0.7,'Adres e-mail');
    Report.PrintXY(4.7,0.7,'Wiek');
    Report.PrintXY(5.7,0.7,'Fotka');
    Report.Bold := False;
    Report.NewLine;
    assignfile(f,'koledzy.txt');
    reset(f);
    i:=0;
    while not eof(f) do
    begin
        readln(f,imie);
        Report.PrintXY(0.2,0.9+i,imie);
        readln(f,nazwisko);
        Report.PrintXY(1.2,0.9+i,nazwisko);
        readln(f,e_mail);
        Report.PrintXY(2.7,0.9+i,e_mail);
        readln(f,wiek);
        Report.PrintXY(4.7,0.9+i,wiek);
        readln(f,fotka);
        Bitmap := TBitmap.Create;
        try
           Bitmap.LoadFromFile(fotka);
           Report.PrintBitmap(5.7,0.9+i,1,1,Bitmap);
        finally
           Bitmap.Free;
        end;
        i:=i+1.5;
    end;
    closefile(f);
end;

end.

Ok, to by było na tyle - jak znajdę czas to następny artykuł napiszę o tworzeniu raportów za pomocą narzędzia Visual Designer. W załączniku są pliki trzeciego programu

13 komentarzy

Witam, artykuł fainy ale powtarzam pytanie za gilbert3 , nie mogę się doczytać jak zrobić automatyvzny podział na strony i dlaczego w podglądzie widzę tylko jedną stronę.
Siedzę już drugi dzień w sieci i nie mogę nicznaleźć.
AS

Bradzo ladna kopia :}

Artur nie dodałeś małej aczkolwiek pomocnej rzeczy. Mój raport powinien być na cirka 80 stron, tymczasem nie mogę znaleźć jak wykryć koniec strony i przejść do następnej. Kto podpowie?

mpedzik: musisz umieścić na formie komponent RvSystem i w SystemSetups ustawić aaAlowSetup na False. W RvProject jako Engine ustawić RvSystem. Teraz o tym gdzie pójdzie wydruk decyduje parametr DefaultDest w RvSystem.

Ciachu: zobacz tutaj http://www.nevrona.com/Default.aspx?tabid=73

Jeśli chodzi o polskie litery to ustawiam, we właściwościach czcionki, skrypt europejski i są polskie czcionki.

Aha, jeśli ktoś nie lubi podawać położenia kontrolek w calach to można to oczywiście zmienić przy pomocy właściwości TBaseReport.Units na unMM (milimetry) unCM (centymetry)

Artykul przyjemny, w zasadzie nauczyl mnie tego co chailem :-) Ale czy ktos wie jakiego polecenia trzeba uzyc, aby wrzucic do raportu obiekt typu TChart ???

Ja jeszcze nie poradziłem sobie z polskimi literami w Rave - zawsze te polskie litery przeszkadzają:) (że też Polacy musieli cos takiego wymyśleć) a jeśli chodzi o to, żeby podgląd przerobić tak żeby wszystkie funkcje menu i opcje były napisane w języku polskim - to jeszcze czegoś takiego nie widziałem - jak do czegoś dojdę w tych sprawach to napiszę:)

A czy ktos moze mi powiedziec jak sobie poradzic z polskimi literami z RaveReports? Pach 5_0_8 nie pomaga...

Witam
A czy jest możliwość aby okno podglądu przerobić tak żeby wszystkie funkcje menu i opcje były napisane w języku polskim ??? Albo czy chociaż jest możliwość wydrukowania raportu bezpośrednio na drukarkę, bez uprzedniego podglądu ??

Dzięki za komentarze, cieszę się, że artykuł podoba Wam się. Adam dzieki za ocenę - stylu pisania uczyłem się czytajac twoje książki (a mam ich 3) :)

Fajne masz znajome :)
5 za rozbudowany artykuł na popularny (ale nie jeśli chodzi o odpowiedzi :>) temat.

Pięknie, ładnie opisane na przykładach. Brawo z tego przynajmniej można zrozumieć o co biega.

Ode mnie też 5 - podoba mi się. Wybrałeś temat deficytowy na 4p :)

Ode mnie piąteczka :) Fajny artykul.