Difference between revisions of "LazReport Tutorial"

From Free Pascal wiki
(up to printers)
 
(24 intermediate revisions by 8 users not shown)
Line 1: Line 1:
 +
{{LazReport Tutorial}}
 +
 
==Overview==
 
==Overview==
  
This page started as a translation of the Portuguese language [[Tutorial de LazReport]] (which itself appears to be a translation from a French document). It was updated for LazReport (instead of FastReport) on Lazuars (instead of Delphi), using T*Connection instead of a DBase table.
+
This page started as a translation of the Portuguese language [[LazReport Tutorial/pt|Tutorial de LazReport]] (which itself appears to be a translation from a French document). It was updated for LazReport (instead of FastReport) on Lazarus (instead of Delphi), using T*Connection instead of a DBase table.
  
 
==Starting with LazReport==
 
==Starting with LazReport==
Line 16: Line 18:
 
* TSQLTransaction
 
* TSQLTransaction
 
* TSQLQuery:
 
* TSQLQuery:
 +
** Database := IBConnection1
 
** Active: true
 
** Active: true
 
** SQL: select * from employee
 
** SQL: select * from employee
Line 43: Line 46:
 
[[Image:lazreptutvariableseditor.png]]
 
[[Image:lazreptutvariableseditor.png]]
  
Click on the button '''Variables''' in order to define some variables. Write the contents according ot the following image. "Demo Variables" represents a category and the other entries (xxx field) represent variables. To differentiate the two, the variables start with a space.
+
Click on the button '''Variables''' in order to define some variables. Write the contents according ot the following image. "Demo Variables" represents a category and the other entries (xxx field) represent variables. To differentiate the two, the variables start with a space.
 +
 
 +
'''Note'''. The distinction between variables and variables categories by starting variables with a space is needed unique and exclusively in this dialog ('''Variables List'''). Anywhere else (Report Designer, main source code and scripts) a variable should be invoked without spaces.
  
 
[[Image:lazreptutdefinevariables.png]]
 
[[Image:lazreptutdefinevariables.png]]
Line 87: Line 92:
 
|Master header||Shown at the beginning of the first detail band/level
 
|Master header||Shown at the beginning of the first detail band/level
 
|----
 
|----
|Master data||First detail level data
+
|Master data||First detail level data; repeated for each record in dataset
 
|----
 
|----
 
|Master footer||Shown at the end of the first detail band/level
 
|Master footer||Shown at the end of the first detail band/level
Line 103: Line 108:
 
|Subdetail footer||Shown at the end of the third detail band/level
 
|Subdetail footer||Shown at the end of the third detail band/level
 
|----
 
|----
|Overlay||  
+
|Overlay||Shown once on each page lower layer
 
|----
 
|----
 
|Column header||Shown at the top of every column
 
|Column header||Shown at the top of every column
Line 128: Line 133:
  
 
=== The main event: showing data in the master data band ===
 
=== The main event: showing data in the master data band ===
Let's insert a master data band. LazReport prompts you for a data source; choose '''frDBDataSet1''', the main data source for our example. You can double-click later on the band if you want to change the data source.
+
Let's insert a master data band. LazReport prompts you for a data source; choose '''frDBDataSet1''', the main data source for our example. The master band will be repeated for each record in the data source. You can double-click later on the band if you want to change the data source.
  
 
[[Image:lazreptutbanddatasource.png]]
 
[[Image:lazreptutbanddatasource.png]]
Line 140: Line 145:
 
The next line '''Salary''' [Salary field] is written similarly.
 
The next line '''Salary''' [Salary field] is written similarly.
  
The third line - Salary divided by job grade [[Salary field]/[Job grade field]] - is done likewise. This shows you can perform calculations with fields coming from the data source. Note the extra set of square brackets around the calculation. As you may guess, you can not only use the variables listed via the '''Variable'' button, but also database fields via '''DB Field'''.
+
The third line - Salary divided by job grade [[Salary field]/[Job grade field]] - is done likewise. This shows you can perform calculations with fields coming from the data source. Note the extra set of square brackets around the calculation. As you may guess, you can not only use the variables listed via the '''Variable''' button, but also database fields via '''DB Field'''.
  
 
[[Image:lazreptutmasterdata.png]]
 
[[Image:lazreptutmasterdata.png]]
  
==== A picture is worth... ====
+
You can use all kinds of operators and functions in your calculations; see [[LazReport Documentation#Documentation]] for an overview.
to do: we'll need to add a database field for images. Also check how this works as Laz images have an image identifier prepended in the field data by default... which is useful to keep.
+
 
Now on the right side of the band, select an image. If you want to always show the same picture, you'd just need to double click on the picture and upload the desired image. However, let's press F11 to show the Object Inspector:
+
==== Formatting output ====
 +
You can control how the text in the rectangles is displayed.  
 +
 
 +
Let's first add a new rectangle with the ''Hire date field'' variable.
  
[[Image:imagem13.jpg]]
+
In the variable editor, let's set a format. A format is valid for all variables of a rectangle/object. To show the format editor, double click on a rectangle to get the Text Editor, then click the ''Format'' button.
  
Enter the property with the Memo [ImageField] property. This turns the picture into an object associated with the data field. Our first stage is completed. Save it with same name rapff. A click on the preview gives us:
+
Let's set the date format you want here:
  
[[Image:imagem14.jpg]]  
+
[[Image:lazreptutformattingdate.png]]
  
Close the report editor and save the report definition as '''lazreporttutorial.lrf'''.
+
The first ComboBox indicates the type of data (text, numeric, date, time, logical/boolean). The second lets you  select a preset format.... with some obvious restrictions:
 +
* The Text type does not allow any Number type formatting.
 +
* The Number type requires a number of any type (Integer, Double, Currency...).
 +
* The Date type: you can choose a pre-defined format or write a custom format like MM/DD/YY for example.
 +
Except for Text and Logic types, you can use the formatting strings you also use in Lazarus.  
  
=== Loading the report from the form ===
 
Now let's write the code required to load and view or edit our report. To this add the following code to the project:
 
<syntaxhighlight>
 
TForm1.Button2Click(Sender procedure: TObject); 
 
begin
 
  frReport1.LoadFromFile('lazreporttutorial.lrf'); 
 
  frReport1.ShowReport; 
 
end;
 
TForm1.ButtonClick(Sender procedure: TObject); 
 
begin
 
  frReport1.LoadFromFile('lazreporttutorial.lrf'); 
 
  frReport1.DesignReport; 
 
end;
 
</syntaxhighlight>
 
  
You have a preview option that gives end users a way to modify the report without changing the basic application.
+
Other formatting options are present on the button bar below the menu (e.g. bold, italic, highlighting).  
  
=== Printing ===
+
Highlighting is very useful for emphasizing data. You can specify a condition that determines when highlighting is applied.  
Now we're going to add features to our basic example.  
 
  
Add a TButton component with Caption := 'Print...', and a TPrintDialog. We end up with this:
+
For that, use the keyword Value to indicate the value of the variable. Here is the highlighting for all hire dates later than 1989. Because Hire date is a TDateTime field (which internally is a float), I cheated by just using the float value of January 1st, 1990 which is 32874. By default highlighter is a simple bold on white background. You can set these parameters to an area by clicking the Highlight button.
  
[[Image:lazreptutformwithprintbutton.png]]
+
[[Image:lazreptutconditionalhighlight.png]]
  
Also add the '''Printers'''? '''Printer4Lazarus'''?  units in the uses clause of your form...
+
==== A picture is worth... ====
 +
If you haven't already, we'll need to add a PHOTO field to our test database; using an SQL statement like this (note: unfortunately different databases use different syntax; BLOB can be CLOB,IMAGE,OLE...):
  
... and put the following code in the OnClick event of the '''Print''' button:
+
  ALTER TABLE EMPLOYEE ADD PHOTO BLOB;
<syntaxhighlight>
 
TForm1.bPrintClick(Sender procedure: TObject); 
 
var
 
  FromPg, ToPg, Cpy: Integer; 
 
  ind: Integer; 
 
  St: String; 
 
  Collap: Boolean; 
 
begin
 
  // Load report definition
 
  St:=ExtractFilePath(ParamStr(0)); 
 
  frReport1.LoadFromFile(St+' rap1.frf'); 
 
  // Mudando a impressora padrão
 
  ind: = Printer.PrinterIndex; 
 
  // Preparar o estado; para sair se a preparação não  funcionar
 
  if not frReport1.PrepareReport then Exit; 
 
  // inicializa a limpeza do diálogo para edição de parâmetros
 
  with PrintDialog1 C
 
  begin
 
    Options:=[poPageNums ]; // allows selecting pages/page numbers
 
    Copies: = 1;          // apenas uma espécie
 
    Collate: = True;    // cópias ordenadas
 
    FromPage: = 1;    // start page
 
    ToPage: = frReport1.EMFPages.Count; //  last page
 
    MaxPage: = frReport1.EMFPages.Count; // maximum number of pages
 
    if Execute then    //    limpeza do diálogo 
 
    begin
 
      if (Printer.PrinterIndex < > ind ) // a impressora mudou? 
 
        or frReport1.CanRebuild // possui uma regeneração de estado?
 
      // mudança de impressora 
 
        or frReport1.ChangePrinter(ind, Printer.PrinterIndex) then               
 
        frReport1.PrepareReport 
 
      else Exit; / / a mudança de impressora ocorreu de forma errônea
 
    if PrintDialog1.PrintRange = prPageNums then // fez-se uma seleção de páginas
 
    begin
 
        FromPg : = PrintDialog1.FromPage; // first page
 
        ToPg : = PrintDialog1.ToPage;      // last page
 
    end;
 
    Cpy :=PrintDialog1.Copies;  // number of copies
 
    Collap :=PrintDialog1.Collate;  // order copies
 
    // recorte de edição da página FromPg com ToPg  , " Cpy "
 
    // exemplares ordenados  (Collap)) 
 
    frReport1.PrintPreparedReport(FromPg, ToPg, Cpy, Collap); 
 
    end;
 
  end;
 
end;
 
</syntaxhighlight>
 
  
It's a bit long, but if needed, you can create a descendant of TfrReport with an easier printing method or class , create a PrintReport (Report: string) procedure to load the report and print.
+
Now add some test data - the example here uses jpg files - into the PHOTO field with your favourite database tool. Note: this is the way Delphi DBImage works: just binary blobs with image data. By default, Lazarus DBImage is more flexible and stores the file extension before the image data, allowing different file formats to be used. For simplicity's sake, we'll use the Delphi approach.
  
In the Edit File> Page Options menu you get this:
+
{{Note|For this "DBImage BLOB without file extension info" to work, you'll need the patch from issue 25453/at least revision ... of LazReport}}
  
[[Image:imagem24.jpg]]
+
Now we have to tell the report you need to treat all data as binary jpg image files. Click on frReport1, go to Object Inspector, events and initiates a procedure OnDBImageReader:
+
<source lang="delphi">
Select paper size:
+
procedure TForm1.frReport1DBImageRead(Sender: TObject; S: TStream;
 +
  var GraphExt: string);
 +
begin
 +
  // Force TDBImage to Delphi compatible behaviour: únicamente acepta un formato de fichero, e.g. jpg
 +
  // esto significa que los BLOBs de la base de datos deben ser justamente ficheros jpg binarios sin más datos.
 +
  GraphExt:='jpg';
 +
end;
 +
</source>
  
[[Image:imagem16.jpg]]
+
In File>Variables List, add the SQLQuery1.PHOTO field as variable Photo field, similarly to how you did this before. If you don't see the field, you may need to set the form's SQLQuery to inactive and active again to refresh the list of columns.
  
Choose desired margins:
+
Click on the Insert image... icon on the left of the window; now click on the right side of the band to drop the image control. A Picture editor shows up:
  
[[Image:imagem17.jpg]]
+
[[Image:lazreptutpictureeditor.png]]
  
You can specify the number of columns and the space between them. In the editor's main menu, select File> Report options to display the following dialog box:
+
If you wanted to always show the same picture, you'd just need to click Load ... (or double click on the picture control) and choose your image.
  
[[Image:imagem18.jpg]]
+
However, let's press the Memo button and insert your Photo variable:
  
Here you specify the printer required for loading reports and the preparation is done in two ways.
+
[[Image:lazreptutphotofield.png]]
* If you want to include the total number of pages in your page number, you should choose the Two-pass report options.
 
  
Here we'll add  some variables unrelated to the data to our example. They make it possible, for example, to show the different names according to the context.  
+
This turns the picture into an object associated with the data field. Press OK as needed to get back to the report designer.
  
To do this, open the report editor and select the File menu and the Variable List. Test with a variable, as explained above. The only difference is that you will not select a value field, but leave None (the default value). Save the objects and add the Test variable as seen previously. Return to Lazarus (do not forget to save your report, as lazreporttutorial.lrf) and use the OnGetValue event of the FrRepor1 component by adding the following code:
 
<syntaxhighlight>
 
procedure TForm1.frReport1GetValue(const ParName: String; 
 
var ParValue: Varying; VAr fld: TField); 
 
begin
 
// deal with variable number of dependencies
 
if UpperCase(ParName)=' TEST' then
 
  ParValue:=' Answer: Variable " test1 " '; 
 
end;
 
</syntaxhighlight>
 
  
Alternatively, you can also pass the value directly to the variable using the following code:
 
<syntaxhighlight>
 
procedure TForm1.Button1Click(Sender:TObject);
 
begin
 
  frReport1.Values.FindVariable('test').Field := QuotedStr('test1');
 
  frReport1.ShowReport;
 
end;
 
</syntaxhighlight>
 
  
The below(?) is not used for data iself, but to enable formatting the data with DisplayFormat.
+
Adjust height and width as needed.
  
Now let's see how to create a calculated variable. As before, we will create a new category (optional) and a variable. Test Calculation(?).
+
You can also press F11 to show the Object Inspector which allows you to edit more if needed:
  
Select a new variable and select a value from the ComboBox "Other" and "Expression". This selection activates data entry for Expression. Type the following: [Table1 "SIZE".] + [Table1 "WEIGHT."]. That way you will get the sum of the fields of Size and Weight data (which is a bit ridiculous but it shows the principle). You can also use the variable name. You can also use the following operators:
+
[[Image:lazreptutobjectinspector.png]]
{| BORDER="1" CELLSPACING="0"
 
!COLSPAN="1" STYLE="background:#ffdead;"|Operator type
 
!COLSPAN="1" STYLE="background:#ffdead;"|Operators
 
|----
 
|Logical||>, <, BUT, AND, NOT, =, < >, > =, < =
 
|----
 
|Mathematical||-, *, +, MOD, /
 
|}
 
  
Further you can use the following standard functions:
+
Close the report editor and save the report definition (with the same name, '''lazreporttutorial.lrf''').
  
{| BORDER="1" CELLSPACING="0"
+
If you go to File>Preview, you'll see your report includes the images.
!COLSPAN="1" STYLE="background:#ffdead;"|Function
 
!COLSPAN="1" STYLE="background:#ffdead;"|Descriptions
 
!COLSPAN="1" STYLE="background:#ffdead;"|Examples
 
|----
 
|SUM ||Sum||
 
|----
 
|AVG||Average||
 
|----
 
|MIN||Mínimum||
 
|----
 
|MAX||Maximum||
 
|----
 
|FORMATDATETIME||Formatted date and time||
 
|----
 
|FORMATFLOAT||Formatted float value||
 
|----
 
|LOWERCASE||Lowercase text|| 
 
|----
 
|NAMECASE||Initial capitals, rest lowercase||
 
|----
 
|STRTODATE ||String converted to date||
 
|----
 
|STRTOTIME||String converted to time||
 
|----
 
|UPPERCASE||Uppercase text||
 
|}
 
  
If this is not enough you can create your own functions, but that we will see later.
+
Close the report editor and save the report definition as '''lazreporttutorial.lrf'''.
  
Now we define a format for the variables of an area. A format is valid for all variables of a display area. To show the format editor, click the right button on an area and then in Variable Format.
+
=== Detail data ===
 +
To do: show how to use the detail part of master/detail. see e.g. sample in components/lazreport/samples/editor directory; see the button title "Master-Detail" '''Please write me.'''
  
[[Image:imagem19.jpg]]
+
=== Loading the report from the form ===
 +
Now let's write the code required to load and view or edit our report. To this add the following code to the project:
 +
<source lang="delphi">
 +
TForm1.Button2Click(Sender procedure: TObject); 
 +
begin
 +
  frReport1.LoadFromFile('lazreporttutorial.lrf'); 
 +
  frReport1.ShowReport; 
 +
end;
 +
TForm1.ButtonClick(Sender procedure: TObject); 
 +
begin
 +
  frReport1.LoadFromFile('lazreporttutorial.lrf'); 
 +
  frReport1.DesignReport; 
 +
end;
 +
</source>
  
The first ComboBox indicates the type of data (text, numeric, date, time, logical/boolean). The second allows you to select a preset format. The Text type does not allow any  Number type formatting. The Number type requires a decimal number with a comma ('''to do: verify this. Is this country specific?'''). Except for Text and Logic types, you can use the formatting features of Lazarus.  
+
You have a preview option that gives end users a way to modify the report without changing the basic application.
  
You can also include the concept of highlighting, which is very useful for emphasizing data. You can specify a condition that determines when highlighting is applied. For that use the keyword Value to indicate the value of the variable. Here is the highlighting for all values greater than 1000. By default highlighter is a simple bold on white background. You can set these parameters to an area by clicking the Highlight button.
+
=== Printing ===
 +
Now we're going to add features to our basic example.  
  
[[Image:imagem20.jpg]]
+
Add a TButton component with Caption := 'Print...', and a TPrintDialog. Behind the scenes, a project dependency on the '''Printer4Lazarus''' packages will be added by the IDE, so make sure that package is installed.
  
You can also set the color of the background and style and color of the used font by selecting Intensified brightness.
+
We end up with this:
  
==Functions==
+
[[Image:lazreptutformwithprintbutton.png]]
You need to add the following line to the file FR.lng ('''is this still the correct name for Lazreprot?'''):
 
  
, FCT = CATEGORY | FCT (<X> <Y>, <z>) | Help function on the
+
Now add the '''printers''' unit to your form's uses clauses.
  
where:
+
Put the following code in the OnClick event of the '''Print''' button:
* FCT is the function name.  
+
<source lang="delphi">
* CATEGORY is the category name.  
+
procedure TForm1.Button3Click(Sender: TObject);
* FCT (<X> <Y>, <z>), is the layout of the required parameters, with a maximum of 3 parameters.  
+
var
* Help Is a small help text which should allow the user to understand the purpose of the function.  
+
  FromPage, ToPage, NumberCopies: Integer;
* | is a separator.
+
  ind: Integer;
 +
  Collap: Boolean;
 +
begin
 +
  // Load report definition from application directory
 +
  AppDirectory:=ExtractFilePath(ParamStr(0));
 +
  frReport1.LoadFromFile('lazreporttutorial.lrf');
 +
  // Need to keep track of which printer was originally selected to check for user changes
 +
  ind:= Printer.PrinterIndex;
 +
  // Prepare the report and just stop if we hit an error as continuing makes no sense
 +
  if not frReport1.PrepareReport then Exit;
 +
  // Set up dialog with some sensible defaults which user can change
 +
  with PrintDialog1 do
 +
  begin
 +
    Options:=[poPageNums ]; // allows selecting pages/page numbers
 +
    Copies:=1;
 +
    Collate:=true; // ordened copies
 +
    FromPage:=1; // start page
 +
    ToPage:=frReport1.EMFPages.Count; // last page
 +
    MaxPage:=frReport1.EMFPages.Count; // maximum allowed number of pages
 +
    if Execute then // show dialog; if succesful, process user feedback
 +
    begin
 +
      if (Printer.PrinterIndex <> ind ) // verify if selected printer has changed
 +
        or frReport1.CanRebuild // ... only makes sense if we can reformat the report
 +
        or frReport1.ChangePrinter(ind, Printer.PrinterIndex) //... then change printer
 +
        then
 +
        frReport1.PrepareReport //... and reformat for new printer
 +
      else
 +
        exit; // we couldn't honour the printer change
  
Each parameter is separated by commas and can be an expression, a constant, a variable, a field or data.
+
      if PrintDialog1.PrintRange = prPageNums then // user made page range selection
Alphanumeric type constants should be limited by a character. The variables are delimited by '''and'''.
+
      begin
 +
        FromPage:=PrintDialog1.FromPage; // first page
 +
        ToPage:=PrintDialog1.ToPage;  // last page
 +
      end;
 +
      NumberCopies:=PrintDialog1.Copies; // number of copies
 +
      // Print the report using the supplied pages & copies
 +
      frReport1.PrintPreparedReport(inttostr(FromPage)+'-'+inttostr(ToPage), NumberCopies);
 +
    end;
 +
  end;
 +
end;
 +
</source>
  
Data fields are limited to the variables and have the following format:
+
It's a bit long, but if needed, you can create a descendant of TfrReport with an easier printing method or class , create a PrintReport (Report: string) procedure to load the report and print.
;Dataset_Name. "Field_Name"
 
  
Statistical functions
+
When double clicking on '''frReport1''', in the report designer File>Page Options menu you get this:
  
;SUM(<X >) : Returns the sum of values represented by <X>, which is generally a data field.
+
Select paper size:
;AVG(<X >) : Returns the average of <X> values.
 
;COUNT    : Count the number of occurrences.
 
;MIN(<X >) : Returns the lowest value of all values represented by <X>
 
;MAX(<X >) : Returns the highest value of all values represented by <X>
 
  
===Date functions===
+
[[Image:lazreptutpapersize.png]]
;FORMATDATETIME(<X>, <Y >) : Returns <Y> TDateTime in format <X>. <X> uses the default syntax for Lazarus formats.
 
;FORMATFLOAT(<X>, <Y >) : Returns the numeric value <Y> in format <X>. <<X> uses the default syntax for Lazarus formats.
 
;STRTODATE(<X >) : Returns the TDateTime(? not date?) representation of string < X >. < X > must be formatted correctly.
 
;STRTOTIME(<X >) : Returns the TDateTime time part representation of string < X >. < X > must be formatted correctly.
 
  
===String functions:===
+
Choose desired margins, number of columns, and the space between them, layout order etc.
;LOWERCASE(<X >) : Returns lowercase <X>
+
[[Image:lazreptutprintoptions.png]]
;UPPERCASE(<X >) : Returns uppercase <X>
 
;NAMECASE(<X >) : Returns <X> with initial capitals
 
;COPY(<X>, <Y>, <Z >) : Similar to Pascal Copy: returns string <X> from position < Y > for < Z > characters.
 
  
===Numeric functions===
+
=== Report options ===
;FRAC(<X >) : Returns the decimal part of number < X >
+
Via File>Report Options you can set a default printer that will be used for the report (using the "Select when report loaded" checkbox).
;INT(<X >) : Returns the integer part of number < X >
 
;ROUND(<X >) : Returns the number < X > rounded to an integer (up? down? nearest integer? banker's rounding?)
 
;STR(<X >) : Returns the string representation of number < X >
 
  
===Logical/boolean functions===
+
If you want to include the total number of pages with the page number on each page, you need to select '''Two-pass report''' (which tells Lazreport to build the report twice, inserting total number of pages on the second run).
;IF(<X>, <Y>, <Z >) : Returns < Y > if expression < X > is true. If false, returns retorna < Z >.
 
  
To add your own functions is really quite simple. Some source code to add the functions POS and SQRT below:
+
[[Image:lazreptutreportoptions.png]]
<syntaxhighlight>
 
Unit FR_OGFct;
 
interface
 
implementation
 
uses FR_Pars, FR_Class; // Standard declarations needed for Lazreport!! Note: FR_pars etc? Are these names ok?
 
type
 
// Our own functions are declared here.
 
TfrOGFunctionLibrary = class(TfrFunctionLibrary)
 
public 
 
  constructor Create; override; 
 
  procedure DoFunction(FNo:integer procedure;  p1, p2,
 
            p3:Variant; var valley:string); override; 
 
            p3:Variant; var valley:string);
 
end; 
 
//**************************
 
// * TfrOGFunctionLibrary
 
constructor TfrOGFunctionLibrary.Create;
 
begin 
 
inherited Create; 
 
// Add our functions to the list of available functions:
 
with List do
 
begin
 
  Add('POS');
 
  Add('SQRT');
 
end;
 
end;
 
  
Procedure TfrOGFunctionLibrary.DoFunction(FNo:Integer procedure;  p1,
+
On the '''Other''' tab, you can fill out some report metadata, as is done on the screenshot below:
p2, p3:Variant; Var valley:String); //valley?
+
[[Image:lazreptutreportoptionsother.png]]
Var
 
  Par1, Par2:  Varying;
 
  Result:  Variantying; //variantying? variant?
 
begin
 
Try
 
Case FNo of  //this is the index of the functions as added in the constructor
 
  0 :  
 
begin  // POS  function
 
    Par1:=VarToStr(Parser.Calc(p1));
 
    Par2:=VarToStr(Parser.Calc(P2));
 
    Result:=Pos(Par1,Par2);
 
  end;
 
  1 :  // SQRT function
 
Result:=SQRT(Parser.Calc(P1));
 
end;
 
Except 
 
// String with error details
 
Result:='Error in function '+List.Strings[FNo];
 
end;
 
Val:=VarToStr(Result); // Resulting string
 
end;
 
  
rocedure DoInit;
+
=== General purpose variables ===
begin
+
Now we'll add some report variables unrelated to the database data in our example. They let you e.g. show different names according to the context.
frRegisterFunctionLibrary(TfrOGFunctionLibrary);
 
end;
 
</syntaxhighlight>
 
In the FR.lng file, you'll need to add the following lines:
 
  
;SQRT = Matemática e Trigonometria | SQRT(<X>) |Retorna a raiz quadrada de <X> 
+
To do this, open the report editor and select the File menu and the Variable List. Add a Test category, and a test1 variable the same way we did earlier (with a space before the variable name):
;POS = Strings  | POS(<X>,<Y>) Retorna a posição da string <X> em  <Y> 
 
 
Publish a report then File>List and this window will appear:
 
  
[[Image:imagem21.jpg]]
+
[[Image:lazreptutvariableseditortestvar.png]]
  
You may notice the appearance of a small (...) button, active only in the case of an expression. Click below to open the editor:
+
The only difference is that you will not select a value field, but leave None (the default value):
 +
[[Image:lazreptutvariabletest.png]]
  
[[Image:imagem22.jpg]]
+
Return to Lazarus (do not forget to save your report, if necessary) and set up the '''OnGetValue'' event of the '''frReport1''' component by adding the following code:
  
You may notice the similarity. If you click the Add button it will load the editor parameters:
+
<source lang="delphi">
 +
procedure TForm1.frReport1GetValue(const ParName: String; var ParValue: Variant
 +
  );
 +
begin
 +
// Conditionally assign value:
 +
if ParName='test1' then
 +
  ParValue:='Answer: Variable "test1"'; //perhaps replace with a call to a random function or something?
 +
end;
 +
</source>
  
[[Image:imagem23.jpg]]
 
  
Depending on the number of parameters, you have more or less edit areas. The (...) button makes it possible to open the expression generator to set the parameter.
+
Alternatively, you can also pass the value directly to the variable using the following code:
 +
<source lang="delphi">
 +
procedure TForm1.Button1Click(Sender:TObject);
 +
begin
 +
  frReport1.Values.FindVariable('test').Field := QuotedStr('test1');
 +
  frReport1.ShowReport;
 +
end;
 +
</source>
  
 
== See also ==
 
== See also ==
* [[Tutorial de LazReport]] Version used as a basis for this translation.
+
* [[LazReport Tutorial/pt|Tutorial de LazReport]] Version used as a basis for this translation.
 +
* [[LazReport Documentation]]
  
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]
 
[[Category:Lazarus]]
 
[[Category:Lazarus]]
 
[[Category:LazReport]]
 
[[Category:LazReport]]

Latest revision as of 18:04, 2 April 2020

Deutsch (de) English (en) español (es) português (pt)

Overview

This page started as a translation of the Portuguese language Tutorial de LazReport (which itself appears to be a translation from a French document). It was updated for LazReport (instead of FastReport) on Lazarus (instead of Delphi), using T*Connection instead of a DBase table.

Starting with LazReport

We're going to use a demo database which is provided by Delphi (DBDEMOS). You'll need to create a new application with a main form Form1.

We're assuming you already have your database set up as specified in SQLdb Tutorial0. If not, please do so.

Setting up your form

Place the following components on the form:

  • A T*Connection, e.g. TIBConnection:
    • DatabaseName := your database, e.g. C:\Program Files\Firebird\Firebird_2_5\examples\empbuild\EMPLOYEE.FDB
    • UserName, Password, HostName: as appropriatie (e.g. SYSDBA,password,<none> for Firebird embedded)
    • Transaction: TSQLTransaction1
  • TSQLTransaction
  • TSQLQuery:
    • Database := IBConnection1
    • Active: true
    • SQL: select * from employee
  • TDataSource
    • DataSource1.DataSet := SQLQuery1
  • TfrDBDataSet
    • frDbDataSet.DataSource := DataSource1
  • TfrReport
  • TfrDesigner
  • 2 TButton
  • Button1.Caption := 'Editing'
  • Button2.Caption := 'Preview'

This results in:

lazreptut01.png

Double-click on the TfrReport icon (or right-click, Design Report) to bring up the report designer:

lazreptut01b.png

Setting up variables

LazReport lets you link variable names to database fields or system values, in order to spare your end users from having to decipher cryptic field names. In our example, we're going to create a variable for our database fields FIRST_NAME, LAST_NAME etc.

In the File menu, click Variables list. Now you get an overview of the defined variables:

lazreptutvariableseditor.png

Click on the button Variables in order to define some variables. Write the contents according ot the following image. "Demo Variables" represents a category and the other entries (xxx field) represent variables. To differentiate the two, the variables start with a space.

Note. The distinction between variables and variables categories by starting variables with a space is needed unique and exclusively in this dialog (Variables List). Anywhere else (Report Designer, main source code and scripts) a variable should be invoked without spaces.

lazreptutdefinevariables.png

Finally click on OK to confirm your entries, and you'll see this:

lazreptutvariablesdefined.png

As you can see, the category we defined is shown in the ComboBox above the variables. Each variable must be linked with a field. To do this, select the variable and then the field. Do this for all variables; the screenshot shows the link for Hire date:

lazreptutvarhiredatetofield.png

Objects and bands

After closing the variables editor, we're back in our report designer. You may have noticed a small number of objects in the object bar to the left of the report surface. These are:

  • the mouse icon pointer
  • rectangle object
  • band
  • picture object
  • subreport
  • draw lines
  • insert barcode object

lazreptut01b.png

Click the band icon, go to the report surface, and click on it to drop a band object.

lazreptutinsertnewband.png

You must click on the type of band you want to create. Select Report title and click OK.

Band type Use
Report title Shown only on the first page
Report summary Shown only on the last page
Page header Shown at the top of each page
Page footer Shown at the bottomof each page
Master header Shown at the beginning of the first detail band/level
Master data First detail level data; repeated for each record in dataset
Master footer Shown at the end of the first detail band/level
Detail header Shown at the beginning of the second detail band/level
Detail data Second detail level data
Detail footer Shown at the end of the second detail band/level
Subdetail header Shown at the beginning of the third detail band/level
Subdetail data Third detail level data
Subdetail footer Shown at the end of the third detail band/level
Overlay Shown once on each page lower layer
Column header Shown at the top of every column
Column footer Shown at the bottom of every column
Group header Shown at the top of every group
Group footer Shown at the bottom of every group
Cross header Shown at the top of cross tables/pivot tables
Cross data Shows cross tables/pivot table data
Cross footer Shown at the bottom of cross tables/pivot tables

Report title

In the Report title band, add a rectangle. The text editor will show up. In the first line the title of the report should be indicated. On the the second, system date and time need to be added. They are obtained by clicking Variable and then Category Other, then double-click on the desired variable (once you are familiar with the variables, you can also just type them). Now we have:

lazreptutreporttitle.png

Click OK to exit the editor, then adjust the title rectangle to the desired size. Choose a color for the background with the Bucket Fill button on the toolbar.

The main event: showing data in the master data band

Let's insert a master data band. LazReport prompts you for a data source; choose frDBDataSet1, the main data source for our example. The master band will be repeated for each record in the data source. You can double-click later on the band if you want to change the data source.

lazreptutbanddatasource.png

In the left half of the band we insert a rectangle and double click. The familiar text editor appears.

Now we will add several fields in the one rectangle. If needed though, you can use one rectangle per field if you want to apply different formatting etc.

The first line Name [Full name field] is obtained by writing Name and then, with the Variable button, entering the variable [Field Name]. Alternatively, as indicated before, you can write the resulting text directly.

The next line Salary [Salary field] is written similarly.

The third line - Salary divided by job grade [[Salary field]/[Job grade field]] - is done likewise. This shows you can perform calculations with fields coming from the data source. Note the extra set of square brackets around the calculation. As you may guess, you can not only use the variables listed via the Variable button, but also database fields via DB Field.

lazreptutmasterdata.png

You can use all kinds of operators and functions in your calculations; see LazReport Documentation#Documentation for an overview.

Formatting output

You can control how the text in the rectangles is displayed.

Let's first add a new rectangle with the Hire date field variable.

In the variable editor, let's set a format. A format is valid for all variables of a rectangle/object. To show the format editor, double click on a rectangle to get the Text Editor, then click the Format button.

Let's set the date format you want here:

lazreptutformattingdate.png

The first ComboBox indicates the type of data (text, numeric, date, time, logical/boolean). The second lets you select a preset format.... with some obvious restrictions:

  • The Text type does not allow any Number type formatting.
  • The Number type requires a number of any type (Integer, Double, Currency...).
  • The Date type: you can choose a pre-defined format or write a custom format like MM/DD/YY for example.

Except for Text and Logic types, you can use the formatting strings you also use in Lazarus.


Other formatting options are present on the button bar below the menu (e.g. bold, italic, highlighting).

Highlighting is very useful for emphasizing data. You can specify a condition that determines when highlighting is applied.

For that, use the keyword Value to indicate the value of the variable. Here is the highlighting for all hire dates later than 1989. Because Hire date is a TDateTime field (which internally is a float), I cheated by just using the float value of January 1st, 1990 which is 32874. By default highlighter is a simple bold on white background. You can set these parameters to an area by clicking the Highlight button.

lazreptutconditionalhighlight.png

A picture is worth...

If you haven't already, we'll need to add a PHOTO field to our test database; using an SQL statement like this (note: unfortunately different databases use different syntax; BLOB can be CLOB,IMAGE,OLE...):

ALTER TABLE EMPLOYEE ADD PHOTO BLOB;

Now add some test data - the example here uses jpg files - into the PHOTO field with your favourite database tool. Note: this is the way Delphi DBImage works: just binary blobs with image data. By default, Lazarus DBImage is more flexible and stores the file extension before the image data, allowing different file formats to be used. For simplicity's sake, we'll use the Delphi approach.

Note-icon.png

Note: For this "DBImage BLOB without file extension info" to work, you'll need the patch from issue 25453/at least revision ... of LazReport

Now we have to tell the report you need to treat all data as binary jpg image files. Click on frReport1, go to Object Inspector, events and initiates a procedure OnDBImageReader:

procedure TForm1.frReport1DBImageRead(Sender: TObject; S: TStream;
  var GraphExt: string);
begin
  // Force TDBImage to Delphi compatible behaviour: únicamente acepta un formato de fichero, e.g. jpg
  // esto significa que los BLOBs de la base de datos deben ser justamente ficheros jpg binarios sin más datos.
  GraphExt:='jpg';
end;

In File>Variables List, add the SQLQuery1.PHOTO field as variable Photo field, similarly to how you did this before. If you don't see the field, you may need to set the form's SQLQuery to inactive and active again to refresh the list of columns.

Click on the Insert image... icon on the left of the window; now click on the right side of the band to drop the image control. A Picture editor shows up:

lazreptutpictureeditor.png

If you wanted to always show the same picture, you'd just need to click Load ... (or double click on the picture control) and choose your image.

However, let's press the Memo button and insert your Photo variable:

lazreptutphotofield.png

This turns the picture into an object associated with the data field. Press OK as needed to get back to the report designer.


Adjust height and width as needed.

You can also press F11 to show the Object Inspector which allows you to edit more if needed:

lazreptutobjectinspector.png

Close the report editor and save the report definition (with the same name, lazreporttutorial.lrf).

If you go to File>Preview, you'll see your report includes the images.

Close the report editor and save the report definition as lazreporttutorial.lrf.

Detail data

To do: show how to use the detail part of master/detail. see e.g. sample in components/lazreport/samples/editor directory; see the button title "Master-Detail" Please write me.

Loading the report from the form

Now let's write the code required to load and view or edit our report. To this add the following code to the project:

TForm1.Button2Click(Sender procedure: TObject);  
begin 
  frReport1.LoadFromFile('lazreporttutorial.lrf');  
  frReport1.ShowReport;  
end;
TForm1.ButtonClick(Sender procedure: TObject);  
begin 
  frReport1.LoadFromFile('lazreporttutorial.lrf');  
  frReport1.DesignReport;  
end;

You have a preview option that gives end users a way to modify the report without changing the basic application.

Printing

Now we're going to add features to our basic example.

Add a TButton component with Caption := 'Print...', and a TPrintDialog. Behind the scenes, a project dependency on the Printer4Lazarus packages will be added by the IDE, so make sure that package is installed.

We end up with this:

lazreptutformwithprintbutton.png

Now add the printers unit to your form's uses clauses.

Put the following code in the OnClick event of the Print button:

procedure TForm1.Button3Click(Sender: TObject);
var
  FromPage, ToPage, NumberCopies: Integer;
  ind: Integer;
  Collap: Boolean;
begin
  // Load report definition from application directory
  AppDirectory:=ExtractFilePath(ParamStr(0));
  frReport1.LoadFromFile('lazreporttutorial.lrf');
  // Need to keep track of which printer was originally selected to check for user changes
  ind:= Printer.PrinterIndex;
  // Prepare the report and just stop if we hit an error as continuing makes no sense
  if not frReport1.PrepareReport then Exit;
  // Set up dialog with some sensible defaults which user can change
  with PrintDialog1 do
  begin
    Options:=[poPageNums ]; // allows selecting pages/page numbers
    Copies:=1;
    Collate:=true; // ordened copies
    FromPage:=1; // start page
    ToPage:=frReport1.EMFPages.Count; // last page
    MaxPage:=frReport1.EMFPages.Count; // maximum allowed number of pages
    if Execute then // show dialog; if succesful, process user feedback
    begin
      if (Printer.PrinterIndex <> ind ) // verify if selected printer has changed
        or frReport1.CanRebuild // ... only makes sense if we can reformat the report
        or frReport1.ChangePrinter(ind, Printer.PrinterIndex) //... then change printer
        then
        frReport1.PrepareReport //... and reformat for new printer
      else
        exit; // we couldn't honour the printer change

      if PrintDialog1.PrintRange = prPageNums then // user made page range selection
      begin
        FromPage:=PrintDialog1.FromPage; // first page
        ToPage:=PrintDialog1.ToPage;  // last page
      end;
      NumberCopies:=PrintDialog1.Copies; // number of copies
      // Print the report using the supplied pages & copies
      frReport1.PrintPreparedReport(inttostr(FromPage)+'-'+inttostr(ToPage), NumberCopies);
    end;
  end;
end;

It's a bit long, but if needed, you can create a descendant of TfrReport with an easier printing method or class , create a PrintReport (Report: string) procedure to load the report and print.

When double clicking on frReport1, in the report designer File>Page Options menu you get this:

Select paper size:

lazreptutpapersize.png

Choose desired margins, number of columns, and the space between them, layout order etc. lazreptutprintoptions.png

Report options

Via File>Report Options you can set a default printer that will be used for the report (using the "Select when report loaded" checkbox).

If you want to include the total number of pages with the page number on each page, you need to select Two-pass report (which tells Lazreport to build the report twice, inserting total number of pages on the second run).

lazreptutreportoptions.png

On the Other tab, you can fill out some report metadata, as is done on the screenshot below: lazreptutreportoptionsother.png

General purpose variables

Now we'll add some report variables unrelated to the database data in our example. They let you e.g. show different names according to the context.

To do this, open the report editor and select the File menu and the Variable List. Add a Test category, and a test1 variable the same way we did earlier (with a space before the variable name):

lazreptutvariableseditortestvar.png

The only difference is that you will not select a value field, but leave None (the default value): lazreptutvariabletest.png

Return to Lazarus (do not forget to save your report, if necessary) and set up the OnGetValue event of the frReport1' component by adding the following code:

procedure TForm1.frReport1GetValue(const ParName: String; var ParValue: Variant
  );
begin
 // Conditionally assign value:
 if ParName='test1' then 
   ParValue:='Answer: Variable "test1"'; //perhaps replace with a call to a random function or something?
end;


Alternatively, you can also pass the value directly to the variable using the following code:

procedure TForm1.Button1Click(Sender:TObject);
begin
  frReport1.Values.FindVariable('test').Field := QuotedStr('test1');
  frReport1.ShowReport;
end;

See also