Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Impresión (https://www.clubdelphi.com/foros/forumdisplay.php?f=4)
-   -   Crear las columnas en Runtime (QReport) (https://www.clubdelphi.com/foros/showthread.php?t=6224)

sitrico 24-12-2003 18:06:41

Crear las columnas en Runtime (QReport)
 
Primero lo primero: Felices Fiestas.

Ya entrando en materia: necesito crear en runtime las columnas de un reporte qReport (QRDBText) y sus titulos asociados (QRDBLabel) en tiempo de ejecución. (Cada uno está en su QRBand respectivo).

La intención es poder imprimir una BDD (Paradox) que se muestra en un DBGrid, el usuario puede escojer entre las diferentes BDD del sistema para consultarlas y la intención es crear un reporte generico que incluya todos (o algunos) de los campos de la BDD consultada para generar el reporte.

Lo que necesito es un ciclo mas o menos:

For i := 0 to DBGrid.Colums.count-1 do
c := tQRDBText.Create(Self);
c.Top,Left,Width... // Asignar posicion y tamaño
c.DataSet, FieldName... // Asignar Tabla y campo
Como asigno el nuevo objeto al Band? ¿AddPrintable?
c.free???? // se debe liberar antes de crear el otro?
End;

Creo que es más o menos lo que se debe hacer pero en las pruebas que he realizado no logro asignar el objeto a la banda del reporte correspondiente.

Gracias por su ayuda.

Descendents 24-12-2003 18:51:02

Yo con quick reports creo que se puede hacer todo.Incluso he hecho algunas veces quickreports enteros por codigo.Te recomiendo en casos dificiles hacer lo que haces.

Vas bien encaminado, pero te falta el parent.
ejemplo:
c.parent := detailband1;



Saludos

__cadetill 24-12-2003 20:27:11

a parte de lo mencianado por Descendents, cuando tengo que hacer reportes de esta manera, lo que hago es declarar arrays de los componentes que necesito

Código:

var
  Labels : array of TQrLabel;
  Labels : array of TQrDBText;
........

de esta manera, en el momento de liberar la memoria, sólo hay que recorrer el array haviendo un free de cada posición.
La destrucción depende de como montes el reporte. Personalmente tengo la manía de declarar un procedimiento público (public) al cual llamo y es el encargado de hacer todo el trabajo sucio (creación de objetos, lanzar SQLs, crear temporales,.....), lanzar el reporte (Print o Preview) y, si se da el caso, liberar objetos creados

Espero que la idea te guste

sitrico 26-12-2003 18:09:20

Gracias por la ayuda, ya logre crear y generar el reporte, pero ahora tengo el problema que al cerrar el reporte se me genera una "Operación de Puntero Invalida" y el reporte no se destruye apropiadamente. (El Reporte está construido en un TQuickRep no en un TForm)

Aqui está el Código en cuestión:

Código:

procedure TfSA3710.QuickRepBeforePrint(Sender: TCustomQuickRep;
  var PrintReport: Boolean);
Var
fCons : TfSA1000;
Begin
FCons := TfSA1000(fSA0000.ActiveMDIChild);
dba.TableName := fCons.dbArancel.TableName;
CrearColumnas;
dbA.Open;
end;

procedure TfSA3710.QuickRepAfterPrint(Sender: TObject);
Var
i : Integer;
begin
// For i := 0 to fSa3700.DBGridArancel.Columns.Count-1 do
//  Begin
//  lbs[i].Free;
//  fld[i].Free;
//  End;
For i := SizeOf(lbs) DownTo 0 do
  lbs[i].Free;
For i := SizeOf(fld) DownTo 0 do
  fld[i].Free;
dbA.Close;
end;

procedure TfSA3710.CrearColumnas;
Const
Sep = 5;
Var
i,px,ATot,Ancho : Integer;
begin
aTot := 0;
For i := 0 to fSa3700.DBGridArancel.Columns.Count-1 do
  ATot := ATot + fSa3700.DBGridArancel.Columns.Items[i].Width;
px := 0;
// Definir el tamaño del Array;
SetLength(lbs,fSa3700.DBGridArancel.Columns.Count-1);
SetLength(fld,fSa3700.DBGridArancel.Columns.Count-1);
For i := 0 to fSa3700.DBGridArancel.Columns.Count-1 do
  Begin
  Ancho :=(Titulos.Width * fSa3700.DBGridArancel.Columns.Items[i].Width) div ATot;
  // Encabezados
  lbs[i] := TQRLabel.Create(Titulos);
  With lbs[i] do
      Begin
      Parent := Titulos;
      Top := 1;
      Left := px;
      AutoSize := False;
      Width := Ancho - Sep;
      Visible := fSa3700.DBGridArancel.Columns.Items[i].Visible;
      If Visible Then
        begin
        Alignment := fSa3700.DBGridArancel.Columns.Items[i].Alignment;
        Caption := fSa3700.DBGridArancel.Columns.Items[i].Title.Caption;
        End;
      End;
  // Campos
  fld[i] := TQRDBText.Create(detalle);
  With Fld[i] do
      begin
      Parent := Detalle;
      Top := 1;
      Left := px;
      AutoSize := False;
      Width := Ancho - Sep;
      Visible := lbs[i].Visible;
      If Visible Then
        Begin
        Alignment := lbs[i].Alignment;
        DataSet := dbA;
        DataField := fSa3700.DBGridArancel.Columns.Items[i].FieldName;
        WordWrap := True;
        AutoStretch := True;
        End;
      End;
  // Posicion X del proximo
  px := px + Ancho;
  End;
end;

Para crear el reporte uso
Código:

Var
Rep : TFsa3710;
begin
Rep := TFsa3710.Create(Self);
Try
  rep.PrinterSetup;
  If rep.Tag = 0 then
    rep.Preview;
//    rep.Print;
Finally
  rep.Free;
  End;
end;

y aun así no se completa el free del reporte (pese al finally)

Las declaraciones de los arreglos y el metodo crear columnas las puse como publicas y privadas.

Código:

    lbs : Array of TQRLabel;
    fld : Array of TQRDBText;
    Procedure CrearColumnas;

Si alguien sabe que ocurre, agradezco cualquier sugerencia.

PD Uso delphi 7 con QReport 3.5.1 (descargado de QuSoft)
el error me lo genera en la unidad System

Código:

function _FreeMem(P: Pointer): Integer;
{$IF Defined(DEBUG) and Defined(LINUX)}
var
  Signature: PLongInt;
{$IFEND}
begin
  if P <> nil then
  begin
{$IF Defined(DEBUG) and Defined(LINUX)}
    Signature := PLongInt(LongInt(P) - 4);
    if Signature^ <> 0 then
      Error(reInvalidPtr);
    Signature^ := FreeMemorySignature;
    Result := MemoryManager.Freemem(Pointer(Signature));
{$ELSE}
    Result := MemoryManager.FreeMem(P);
{$IFEND}
    if Result <> 0 then
      Error(reInvalidPtr);  // <- - - - - - - - - - - -  Linea de error
  end
  else
    Result := 0;
end;


delphi.com.ar 26-12-2003 18:57:25

Puedo sugerirte que pruebes el QueryPrint, es un componente hecho con este propósito, puedes bajarlo de mi página.

Saludos!

sitrico 26-12-2003 19:19:04

No quiero despreciar el ofrecimiento de delphi.com.ar pero creo que lo que me falta es una tonteria, de todas maneras voy a ver el componente como ejemplo (aunque trabajo con tablas). además, debe haber otra manera de correjir un error que no sea bajar un componente de delphi.com.ar o de catedetill* ;-)

Saludos y gracias

* o de cualquier otro participante que haya aportado componentes.

sitrico 26-12-2003 20:10:58

Como dicen: "El que persevera alcanza", el error estaba en la asignación del tamaño del array:

Código:

SetLength(lbs,fSa3700.DBGridArancel.Columns.Count-1);
SetLength(fld,fSa3700.DBGridArancel.Columns.Count-1);

Sobra el -1 ya que se nececitan "Count" elementos, la declaración correcta es:

Código:

SetLength(lbs,fSa3700.DBGridArancel.Columns.Count);
SetLength(fld,fSa3700.DBGridArancel.Columns.Count);

Adicionalmente encontre otros errores (menores), el valor de Width podia ser negativo (0-sep) = 0-5 = -5 y el evento AfterPrint y AfterPreview deben asignarse al procedimiento ReporteAfterPrint.

Con estas correciones se logra imprimir el DBGrid con una ancho de columnas proporcional al de pantalla.

Gracias a todos por su ayuda.

fjcg01 04-01-2004 20:36:35

Hola a todos,
fusilando por ahí fusilando por allá, he tomado de base este hilo para hacer una nueva versión de un generador de reportes que tenía hecho en Ace report y que el Delphi 7 no funciona - o yo por lo menos no he conseguido instalar los componentes ACE -.

Adjunto el código de llamada al form, que lo genera y prepara las propiedades de los objetos, que son los siguientes:
- Query que quieres imprimir
- DBGrid idem
- Titulo, empresa y condiciones son strings que se escriben el la cabecera.
- VisualizarTotales es un string con los nombres de los campos separados con comas - o sin separa, para que nos vamos a engañar - que queremos totalizar
- GrupoTotales es un string con los nombres de los campos que si cambia alguno de ellos se imprime la banda de totales.

Mi problema se plantea cuando ejecuto el informe. A veces se imprime OK, pero otras veces, en la banda de los totales no aparecen los mismos.

Como podeis ver, hay cuatro bandas, Cabecera de reporte, cabecera de grupo, detalle, pie de grupo y pie de reporte.

Si alguien me puede ayudar, pues lo agradecería mucho, ya que estoy pegándome un poquito con ello y ya me sale humo de la cabeza.


-------------------------------------------------------------

:: Funcion de generar listados a partir de un Grid automáticamente ::
:: Forma de llamarlo ::
:: GenerarListado( Query que queremos imprimir ADO ::
:: Grid asociado a la query ::
:: Titulo del Listado ::
:: empresa del listado ::
:: Condiciones del listado ( literal) ::
:: Campos que queremos sumar separados por comas ::
:: Campos que cuando cambian se imprimen los totales::
:: separados por comas ::
:: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: }
unit mdrpt;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
QuickRpt, Qrctrls, Db, DBTables, ExtCtrls,Grids, DBGrids,ADODB,printers;

type
TFrmQuick = class(TForm)
QuickRep1: TQuickRep;
PageHeaderBand1: TQRBand;
QRLTitulo: TQRLabel;
QRLEmpresa: TQRLabel;
QRLCondiciones: TQRLabel;
QRSysData1: TQRSysData;
QRShape1: TQRShape;
QRGroup1: TQRGroup;
DetailBand1: TQRSubDetail;
GroupFooterBand1: TQRBand;
QRBand1: TQRBand;
QRLabelP: TQRLabel;
QRSysData3: TQRSysData;
QRShape2: TQRShape;
QRLabel1: TQRLabel;
QRLabel2: TQRLabel;
QRShape3: TQRShape;
procedure QuickRep1AfterPrint(Sender: TObject);
private
{ Private declarations }
procedure PrepararListado(Query:TADOQuery; DBGRid: TDBGrid; VisualizarTotales,GrupoTotales:string);
public
{ Public declarations }
Titulos: array of TQrLabel;
Campos: array of TQRDBText;
Totales: array of TQrExpr;
function GenerarListado( Query:TADOQuery; DBGrid: TDBGrid; Titulo,empresa,Condiciones,VisualizarTotales,GrupoTotales: string): boolean;
end;
var
FrmQuick: TFrmQuick;
implementation

{$R *.dfm}

function TFrmQuick.GenerarListado( Query:TADOQuery; DBGrid: TDBGrid; Titulo,empresa,Condiciones,VisualizarTotales,GrupoTotales: string): boolean;
var Mibookmark: string ;
begin
Try
Application.CreateForm(TFrmQuick, FrmQuick);
with FrmQuick do
begin
QRLEmpresa.Caption:= Empresa;
QRLCondiciones.Caption:=condiciones;
QRLTitulo.Caption:=Titulo;
MiBookmark:=Query.Bookmark;
QuickRep1.Dataset:= Query; DetailBand1.Dataset:= Query;
PrepararListado (Query,DBGrid,VisualizarTotales,GrupoTotales);
QuickRep1.PreviewModal;
Query.Bookmark:= MiBookMark;
end;
finally
FrmQuick.Free;
end;
GenerarListado:=True
end;
procedure TFrmQuick.PrepararListado(Query:TADOQuery;DBGRid: TDBGrid; VisualizarTotales,GrupoTotales:string);
const Sep=2;
var n, px:integer;
begin
px:=Sep;
// definir tamaño del array
SetLength(Titulos, DBGrid.Columns.Count);
SetLength(Campos, DBGrid.Columns.Count);
SetLength(Totales, DBGrid.Columns.Count);
QRGroup1.Height:= 0; // para que no se visualice
if VisualizarTotales='' then GroupFooterBand1.Height:= 0;
for n:= 0 to DBGrid.Columns.count-1 do
begin
// titulos
Titulos[n]:= TQRLabel.Create(PageHeaderBand1);
with Titulos[n] do
begin
parent:= PageHeaderBand1; Top:= PageHeaderBand1.Height-25; Left:= px;
AutoSize:= False; Width:= DBGrid.Columns.Items[n].Width;
Alignment:=DBGrid.Columns.Items[n].Alignment;
Caption:= DBGrid.Columns.Items[n].Title.Caption;
Font.Size:= DBGrid.Font.Size; // Font.Style:=[fsBold];
end;
// Grupo de ruptura para los totales
if pos( DBGrid.Columns.Items[n].Title.Caption, GrupoTotales) > 0 then
begin
if QRGroup1.Expression='' then
QRGroup1.Expression:=DBGrid.Columns.Items[n].fieldname
else
QRGroup1.Expression:=DBGrid.Columns.Items[n].fieldname;
end;
// Campos para visualizar Totales
if pos( DBGrid.Columns.Items[n].Title.Caption, VisualizarTotales) > 0 then
begin
totales[n]:=TQRExpr.Create(GroupFooterBand1);
with Totales[n] do
begin
parent:= GroupFooterBand1; Top:= 5; Left:= px;
AutoSize:= False; Width:= DBGrid.Columns.Items[n].Width;
Alignment:=DBGrid.Columns.Items[n].Alignment;
Master:= DetailBand1; Mask:= '#,###,###';
Expression:= 'SUM('+DBGrid.Columns.Items[n].fieldname+')';
Font.Size:= DBGrid.Font.Size;ResetAfterPrint:= True;
end;
end;
// Campos
Campos[n]:= TQRDBText.Create(DetailBand1);
with Campos[n] do
begin
parent:= DetailBand1; Top:= 0; Left:= px;
AutoSize:= False; Width:= DBGrid.Columns.Items[n].Width;
Alignment:=DBGrid.Columns.Items[n].Alignment;
Dataset:= Query; Datafield:= DBGrid.Columns.Items[n].FieldName;
WordWrap:= True; Autostretch:= True;
Font.Size:= DBGrid.Font.Size;
end;
px:= px +DBGrid.Columns.Items[n].Width + sep;
end;
// Orientacion de la page dependiendo de los campos
if px >= 695 then
begin
QuickRep1.Page.Orientation:= poLandscape; // apaisado
QRShape1.Width:= 1027; QRShape2.Width:= 1027; QRShape3.Width:= 1027;
QRLTitulo.Width:= 1027;
QRSysData1.Left:= 1027 - QRSysData1.Width;
QRLabelP.Left:= 450; QRSysData3.Left:= 500;
end
else
QuickRep1.Page.Orientation:= poPortrait;
end;

procedure TFrmQuick.QuickRep1AfterPrint(Sender: TObject);
var n:integer;
begin
for n:= sizeof(Titulos) downto 0 do Titulos[n].Free;
for n:= sizeof(Campos) downto 0 do Campos[n].Free;
for n:= sizeof(Campos) downto 0 do totales[n].Free;
end;

end.

fjcg01 05-01-2004 19:39:50

Hola a todos,
estoy flipando un poquito, ya que la dificultad que tenia este metodo es que cuando le paso una consulta con transform de fechas

TRANSFORM Sum(Importe) AS SumaDeImporte
SELECT Vendedor
FROM Presupue
where Fecha>= #01/01/03# and Fecha<= #31/12/03#
GROUP BY Vendedor
PIVOT Format(Fecha,'MM-MMM')

** pongo este formato para que me numere los meses, de manera que me aparezcan 01-ene, 02-feb. Si no lo hago así, los meses me aparecen en orden alfabetico, es decir, abril, agosto, diciembre, ... **

y los campos que quiero totalizar son '01-ene,02-feb,03-mar, .....'


Cuando construyo la banda de los totales, el quickReport se vuelve loco porque la expresion queda SUM(01-ene), lo que no le gusta y no me saca los totales.
Sin embargo si le pongo el formato MMMM y le paso para totalizar 'enero, febrero, marzo, ...' , me los totaliza sin problemas

¿ Podría alguien echarme una mano ?

Gracias por vuestra colaboración.

Un saludo

fjcg02 07-01-2004 16:18:11

La respuesta is blowing in the wind

Gracias a todos los que habeis perdido vuestro tiempo, pero ya se la solución.

Por motivos deconocidos, el quickreport no suma los campos en los que su nombre mezcla numeros y letras.

Para solventar el problema, utilizo
PIVOT FORMAT(Fecha,'MMMM') in (enero, febrero, marzo, ....).

De esta manera, me salen las columnas ordenadas tal y como las he escrito, no mezclo numeros y letras y el quickrepot saca todo lo que tiene que sacar.

Un saludo y gracias.

ramiretor 10-01-2004 00:01:27

Hola:
Una ultima pregunta, ustedes que saben mucho más que yo, imaginemos que tenemos el Array de TQRDBText lo llamaremos Campos y creamos todo el arreglo en ejecucion, citrico puso una rutina parecida a esta, para liberarlo de la memoria:

for i:= o to NumeroCampos do
begin

Campos[i].Free;


end;

donde i es un acumulador y NumeroCampos es los campos que se generaron...


Bueno la pregunta es que se van liberando los elementos de Campos uno a uno pero ¿será posible que el Array se quede con los elementos nulos?, es decir si se generaron por ejemplo 10 columnas no se quedarán diez ( hablando en términos de matrices) columnas nulas?

Saludos y muchas gracias por escuchar

:D

__cadetill 10-01-2004 10:26:18

no se si te he entendido, pero si es un array dinámico, puedes establecer su longitud a 0 con Setlength

ramiretor 12-01-2004 16:27:21

Hola:
Gracias por responder efectivamente, me entendiste y esa era la respuesta que esperaba oir.

Un saludo

:D

sitrico 14-01-2004 23:55:14

Tuve la misma duda, pero yo liberé el array con finalize(TArray). ¿es lo mismo que SetLenght(0)?

Saludos

__cadetill 15-01-2004 12:34:47

Pues no estoy seguro, pero según dice la ayuda, también puede utilizarse

Cita:

Dynamic arrays can never be deallocated using the Dispose procedure, but can be freed by passing them to Finalize.


La franja horaria es GMT +2. Ahora son las 20:32:14.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi