imprimir list1

Iniciado por corlo, 3 Octubre 2020, 16:32 PM

0 Miembros y 4 Visitantes están viendo este tema.

Serapis

Básicamente hay que hacer dos cosas (asumiendo todo del modo más simple posible):
1 - Establecer una fuente monoespaciada.
Es necesario para simplificar los cálculos, porque así cualquiera que sea el carácter ocupa el mismo espacio al ser impreso, si cada carácter tiene su medida individual hay que calcular uno a uno, mientras que siendo todos iguales, es una multiplicación.
(al final de imprimir la lista debe ser restablecida la fuente a la que tenía, por eso hay que salvaguardar la fuente previamente usada).

2 - Repartir el ancho del papel entre las columnas a usar y posicionar el cursor en cada comienzo de columna e imprimir ahí el ítem. Damos por hecho que ningún ítem supera el ancho de una columna, de ser así debe invocarse una función que calcule y corte el exceso de caracteres, o reducir el número de columnas, etc... por eso digo asumiendo 'todo lo más simple posible').
Dadas x columnas, se recurre a dividir el ancho disponible del papel entre el número de columnas. Para esto los datos necesarios son: saber los márgenes izquierdo, derecho y ancho del papel.
Entonces en un bucle se recorre de x columnas en x columnas, (es deicr las que caben en una línea son un ciclo de este bucle) y en un bucle interno se recorre desde 0 a x columnas -1 (es decir en el bucle interno se recorre cada columna de la línea y es en éste donde se imprime. el bucle exterior simplemente sirve para preparar la línea.
Ahora imprimes el siguiente ítem del texto en la posición con la que empieza la columna (dando por hecho una alineación izquierda).

Supongamos que el papel mide 10.000 puntos de ancho, que son 5 columnas, que el margen izquierdo son 400 y el derecho 300.... y pongamos que la lista son los meses del año:

el pseudocódigo sería:

set tempfuente = impresora.fuente
impresora.fuente.nombre = "Courier New"

anchoCol = (((anchoPapel - (margenderecho + margenIzquierdo)) / numColumnas)
anchoCol = (((10.000 - (400 + 300)) / 5) = ((10.000-700)/5) = 1.860

bucle Y desde 0 hasta lista.count -1 en pasos de NumColumnas ' desde 0 hasta 11 (pués hay 12 meses)
    impresora.PosicionY = (impresora.PosicionY + impresora.altofuente)
   
    Bucle x desde 0 hasta numColumnas -1    ' desde 0 hasta 4 (5 columnas en total)
        impresora.PosicionX = (margenizquierdo + (x * anchoCol))
        impresora.Imprimir meses(y+x)
    siguiente en bucle
siguiente en bucle

' como 5+5 son 10 y hay 12 meses, sobran 2 meses que caben en la última línea:
resto = (lista.Count modulo numCols)
Si (resto > 0) luego
    impresora.PosicionY = (impresora.PosicionY + impresora.altofuente)

    Bucle x desde 0 hasta resto -1    ' desde 0 hasta ? (? columnas restantes)
        impresora.PosicionX = (margenizquierdo + (x * anchoCol))
        impresora.Imprimir meses(y+x)
    siguiente en bucle
Fin si

set impresora.fuente = tempFuente  ' restablecemos la fuente a la que había previamente...


Supongamos que la posición Y actual de la impresora fuera 1.250 y que el alto de la fuente fuera 230 puntos. Estos serían pués los valores que irían arrojando los bucles;

LineaActual = posicionY
PosicionY = (LineaActual + altoFuente) = (1.250 +230) = 1.480
    PosicionX para columna 0 = 400 + (0 * 1.860) =  400
      imprimir lista(0+0) = "Enero"
    PosicionX para columna 1 = 400 + (1 * 1.860) = 2.260
      imprimir lista(0+1) = "Febrero"
    PosicionX para columna 2 = 400 + (2 * 1.860) = 4.120
      imprimir lista(0+2) = "Marzo"
    PosicionX para columna 3 = 400 + (3 * 1.860) = 5.980
      imprimir lista(0+3) = "Abril"
    PosicionX para columna 4 = 400 + (4 * 1.860) = 7.840
      imprimir lista(0+4) = "Mayo"

LineaActual = posicionY
PosicionY = (LineaActual + altoFuente) = (1.480 +230) = 1.710
    PosicionX para columna 0 = 400 + (0 * 1.860) =  400
      imprimir lista(5+0) = "Junio"
    PosicionX para columna 1 = 400 + (1 * 1.860) = 2.260
      imprimir lista(5+1) = "Julio"
    PosicionX para columna 2 = 400 + (2 * 1.860) = 4.120
      imprimir lista(5+2) = "Agosto"
    PosicionX para columna 3 = 400 + (3 * 1.860) = 5.980
      imprimir lista(5+3) = "Septiembre"
    PosicionX para columna 4 = 400 + (4 * 1.860) = 7.840
      imprimir lista(5+4) = "Octubre"

LineaActual = posicionY
PosicionY = (LineaActual + altoFuente) = (1.480 +230) = 1.710
    PosicionX para columna 0 = 400 + (0 * 1.860) =  400
      imprimir lista(10+0) = "Noviembre"
    PosicionX para columna 1 = 400 + (1 * 1.860) = 2.260
      imprimir lista(10+1) = "Diciembre"

Los puntos 1 y 2 remarcados en negrita son el resumen general (si cambian detalles hay que hacer más o menos funcionalidad para que cumplan todas las expectativas de los detalles).

En el código primero, concatenaba los valores de los items que forman una línea a base añadir los espacios necesarios tras un ítem hasta llegar a la posición de inicio de la siguiente columna, lo cual exigía calcular la cantidad de espacios a añadir o caracteres a cortar (si excedía el ancho de la columna). Y así imprimia toda una línea d euna sola vez... es un código más simple (de hacer y entender) imprimir el dato de cada columna, pero menos eficiente y solo sirve si no hay detalles adicionales que lo compliquen (como otra alineación que no sea izquierda y valores que superen el ancho de la columna).

corlo


corlo

Hola nebire por fin he podido correr el programa

hasta ahora tengo esto



Private Sub Command7_Click()
' Imprimir
Dim factura As Integer






Dim total As String
Dim total1 As String
Dim total2 As String


Dim jk As String
'jk = App.Path & "\facturas\" & Combo2.Text & "\" & Txtnum.Text & ".txt"

Printer.Print Tab(5); "HORA: " & UCase(Format(Now, "hh:mm am/pm"))



Dim i As Integer
Dim aux As String


Printer.FontSize = 18

Printer.CurrentX = 4000
Printer.CurrentY = 0
Printer.Print "Factura Nº:"
Printer.CurrentX = 6000
Printer.CurrentY = 0
Printer.Print Txtnum.Text


Printer.CurrentX = 1000
Printer.CurrentY = 3000
Printer.Print "Cantidad"
Printer.CurrentX = 3500
Printer.CurrentY = 3000
Printer.Print "Producto"
Printer.CurrentX = 7350
Printer.CurrentY = 3000
Printer.Print "Precio"
Printer.CurrentX = 9900
Printer.CurrentY = 3000
Printer.Print "Subtotal"



Printer.CurrentX = 3000
Printer.CurrentY = 3500







  listarcolumnas



'Label5.Caption = total
'Label6.Caption = total1
'Label7.Caption = total2

Printer.CurrentX = 8400
Printer.CurrentY = 10000
Printer.Print "Subtotal:"
Printer.CurrentX = 9300
Printer.CurrentY = 10500
Printer.Print "iva:"
Printer.CurrentX = 9000
Printer.CurrentY = 11000
Printer.Print "Total:"

Printer.CurrentX = 10000
Printer.CurrentY = 10000
Printer.Print Label5.Caption
Printer.CurrentX = 10000
Printer.CurrentY = 10500
Printer.Print Label6.Caption
Printer.CurrentX = 10000
Printer.CurrentY = 11000
Printer.Print Label7.Caption



Printer.Print







Printer.EndDoc



End Sub





Private Sub listarcolumnas()
Dim AnchoPapel As Integer
    Dim MargenIzquierdo As Integer
    Dim numCols As Integer
    Dim filas As Long
    Dim ultimafila As Long
    Dim k As Long, j As Long, i As Integer, n As Integer, anchocol As Integer
    Dim linea As String
    Dim margen As Integer
    Dim fuente As StdFont


    Set fuente = Printer.Font
    numCols = 4
    Printer.FontName = "Courier New"  ' una fuente monoespaciada, si no el trabajo es más laborioso...
    filas = ((List1.ListCount + 1) \ numCols)
    ultimafila = ((List1.ListCount + 1) Mod numCols) ' columnas que tendrá la última fila.
    anchocol = ((AnchoPapel - MargenIzquierdo) \ numCols)
   
   
    For k = 0 To filas - 1
        n = margen
        For i = 0 To numCols - 1
            Printer.CurrentX = n  ' imprime el texto de la columna 'i'
            Printer.Print List1.List(j + i)
            n = (n + anchocol)
        Next

        j = (j + numCols)
        Printer.CurrentY = (Printer.CurrentY + Printer.TextHeight("t"))  ' el textheight depende de la fuente seleccionada en la impresora, no importa el texto entre paréntesis... es fijo para la  fuente.
    Next

    If (ultimafila > 0) Then ' la última fila tiene 1 o más columnas, pero menos que 'numcols'.
        n = margen
        For i = 0 To ultimafila - 1
            Printer.CurrentX = n  ' imprime el texto de la columna 'i'
            Printer.Print List1.List(j + i)
            n = (n + anchocol)
        Next
    End If

    Set Printer.Font = fuente
End Sub











y me sale lo siguiente:

la primera fila casi lo hace bien

la segunda fila: primera columna bien segunda columna bien tercera columna mal y cuarta columna mal

faltaria arreglar los datos de la segunda fila



gracias








corlo

Hola nebire

el problema esta en la columna producto, porque no siempre tiene los mismos caracteres

como seria el codigo para arreglar la columna producto

Gracias

Serapis

#14
Puedes mostrar una foto de la impresión, que se vea como va quedando... así me hago cargo del tamaño de los textos, etc...????

p.d.:
Lo que si veo repasando el código, es que a la función ' listarcolumnas', no le has puesto los parámetros necesarios, y por tanto no los recibe, luego parece que simplemente estás imprimiendo un texto detrás del otro, pero sin una consideración correcta de columnas, por eso te está fallando.


Dicho de otro modo... te redacto la función con los parámetros adecuados, ya que parece que no los pasas pero tampoco los inicializas (aunque si el list1)
Código (vb) [Seleccionar]

Private Sub ListarColumnas(byval NumCols as integer, byval MargenIzquierdo as integer, byval AnchoPapel as integer)
Dim AnchoPapel As Integer
    'Dim MargenIzquierdo As Integer, numCols As Integer
    Dim filas As Long
    Dim ultimafila As Long
    Dim k As Long, j As Long, i As Integer, n As Integer, anchocol As Integer
    Dim linea As String
    'Dim margen As Integer
    Dim fuente As StdFont


    Set fuente = Printer.Font
    'numCols = 4  Se recibe por parámetro mejor...
    Printer.FontName = "Courier New"  ' una fuente monoespaciada, si no el trabajo es más laborioso...
    filas = ((List1.ListCount + 1) \ numCols)
    ultimafila = ((List1.ListCount + 1) Mod numCols) ' columnas que tendrá la última fila.
    anchocol = ((AnchoPapel - MargenIzquierdo) \ numCols)
   
   
    For k = 0 To filas - 1
        n = MargenIzquierdo
        For i = 0 To numCols - 1
            Printer.CurrentX = n  ' imprime el texto de la columna 'i'
            Printer.Print List1.List(j + i)
            n = (n + anchocol)
        Next

        j = (j + numCols)
        Printer.CurrentY = (Printer.CurrentY + Printer.TextHeight("t"))  ' el textheight depende de la fuente seleccionada en la impresora, no importa el texto entre paréntesis... es fijo para la  fuente.
    Next

    If (ultimafila > 0) Then ' la última fila tiene 1 o más columnas, pero menos que 'numcols'.
        n = MargenIzquierdo
        For i = 0 To ultimafila - 1
            Printer.CurrentX = n  ' imprime el texto de la columna 'i'
            Printer.Print List1.List(j + i)
            n = (n + anchocol)
        Next
    End If

    Set Printer.Font = fuente
End Sub

' y la invocas así:
'          numero de columnas, margen izquierdo donde empezar, ancho que ocuparán las columnas.
call ListarColumnas(4, 240, printer.width - MargenDerecho)


b]Nota que:[/b]
- MargenIzquierdo y MargenDerecho pueden valer 0, lo que significará que se usará todo el ancho disponible del papel por parte de la impresora (printer.width). Ahora bien si ves que 'queda feo', porque 4 columnas son pocas, puedes o bien añadir alguna columna o utilizar márgenes a ambos lados.
- Puedes omitir el parámetro listbox, si siempre vas a usar un único listbox, pero es acertado que una función reciba por parámetro los datos que utiliza.
Si más adelante copias y pegas la función a otro proyecto, seguirá funcionando porque requerirá el listbox que en dicho proyecto estés usando (se llame como se llame, queda aislado dentro de la función), lo que es mejor que andar renombrando cada aparición del listbox...
Para no liarte visto que ya tienes la función usando el control list1, lo retiro como parámetro, pero es más adecuado que se incluya como tal.


Recuerda que todo lo que se haga con printer.print, printer.line, printer.circle, priter.paintpicture y printer.pset se dibuja a un backbuffer, donde se va recreando la 'imagen' a imprimir, cuando se invoca enddoc, equivale a invocar 'draw' del backbuffer, es decir es cuando se vuelca la imagen completa a la impresora.


corlo

aqui te paso un ejemplo


         cantidad        producto                             precio                       subtotal
            10             tomates de barbastro                2                            20
            30             patatas  0,60                             18









                                                                                            subtotal:  38
                                                                                                   iva:   4,56
                                                                                                 total:  42.56




gracias


       

Serapis

#16
Me acabo de editar (mientras tu respondías), corrigiéndote la función al completo.... revísa mi mensaje previo y cambia la función en tu código así como la línea que invoca dicha función.

Nota los parámetros que deben ser pasados: si con 4 columnas, pasas 4, si no hay margen izquierdo, pasa 0, etc...

Por cierto, veo que ya le das un valor posicional a cada columna, cuando estableces los valores de currentX, al declarar:
Código ( vb) [Seleccionar]

Printer.CurrentX = 1000: Printer.Print "Cantidad"
Printer.CurrentX = 3500: Printer.Print "Producto"
Printer.CurrentX = 7350: Printer.Print "Precio"
Printer.CurrentX = 9900: Printer.Print "Subtotal"


Es decir están a 1000, 3500, 7350 y 9900. es decir lo estableces manualmente y no tienen un reparto equitativo, si quieres que queden con ese mismo reparto, deberías meter dichos valores en un array. Y usarlo como posición para cada columna.

corlo

hola nebire

he hecho los cambios de la funcion y el resultado me sale igual


me podrias hacer un ejemplo lo de un array y usarlo como posicion


gracias

corlo

hola nebire

he resuelto el tema

en el codigo he modificado declarar la variable prod que es el producto

el prod me acepta 19 caracteres en el list1 a la hora de declarar la variable me lo hace bien


dim prod as string * 19



ahora si


gracias nebire

Serapis

#19
Me alegro que lo hayas resuelto.

El uso de string de tamaño fijo es otra solución, aunque si por ejemplo decidiras luego cambiar dinámicamente a 5 u otro cualquier número de columnas, habría que cambiar código.
Una rcomendación es en lo posible adoptar soluciones dinámicas, de tal manera que un cambio de valores no suponga un cambio del código, si no simplemente, pasar los valores adecuados en los parámetros.

Una solución basado en un array sería algo como:
Código ( VB) [Seleccionar]

Dim PosCols() as integer
dim Headers() as string

' Al inicializar la ventana va bien...
'   (dos formas de inicializar un array)
private sub form1_Load()
  posCols = Array(1000, 3500, 7350, 9900)
  Headers = Split("Cantidad, Producto, Precio, Subtotal", ", ")
end sub


Ya tenemos el array con los valores de inicio de cada columna...
Ahora solo falta imprimir en tales posiciones...

En los  headers, remplaza esto:
Código ( VB) [Seleccionar]

Printer.CurrentX = 1000: Printer.Print "Cantidad"
Printer.CurrentX = 3500: Printer.Print "Producto"
Printer.CurrentX = 7350: Printer.Print "Precio"
Printer.CurrentX = 9900: Printer.Print "Subtotal"


por esto:
Código ( VB) [Seleccionar]

    Dim k as integer

    printer.CurrentY = 3000
    for k = 0 to 3
        Printer.CurrentX = posCols(k): Printer.Print Headers(k)
    next


Y luego en el bucle interno (el bucle 'i', que aparece dos veces)...
Código ( VB) [Seleccionar]

        For i = 0 To numCols - 1
            Printer.CurrentX = posCols(i)
            Printer.Print List1.List(j + i)
        Next


Esto invalida la necesidad de 'n', 'anchocol', 'margenizquierdo' y 'anchopapel', aunque insisto en que es preferible parametrizar las funciones, pues les da flexibilidad y puedes reaprovechar código para otros proyectos... el código estático, necesita ser modificado para cada situación. Contradice el paradigma 'orientado a objetos' y favorece el 'código espagueti'...