Comunidad Gambas-es

Versión completa: Exportando datos de un GridView a un CSV
Actualmente estas viendo una versión simplificada de nuestro contenido. Ver la versión completa con el formato correcto.
A raíz de una consulta en otro hilo del foro, y como tiempo tengo, he hecho un pequeño vídeo sobre cómo hacer eso: pasar los datos que visualizamos en un GridView a un fichero CSV.



Saludos
Saludos Shordi,
Hay dos características que me parecen interesantes para agregar a tu EXTGRID.

1-Permitir copiar desde una hoja de cálculo y pegar en el grid.
He utilizado un TextArea para pegar los datos ahí, luego los analizo desde el siguiente código y los paso al Grid.

Esto lo he logrado con el siguiente código (en un botón el cual llamo analizar):
GAMBAS
  1.     Dim l As Single
  2.     Dim z As String
  3.     Dim final As String
  4.     Dim d As String
  5.     Dim tupla As New String[17]
  6.     Dim i As Integer
  7.  
  8.     
  9.     TituloGrid
  10.     
  11.     tm = Len(ta.Text)
  12.     For l = 1 To tm
  13.         z = Mid(ta.Text, l, 1)
  14.         If z = "\t" Then
  15.  
  16.              tupla[i] = d
  17.              i += 1
  18.              d = ""
  19.              
  20.         Else
  21.             If z = "\n" Then
  22.                 tabla2[Fl2, 0].Text = tupla[0]
  23.                 tabla2[Fl2, 1].Text = tupla[1]
  24.                 tabla2[Fl2, 2].Text = tupla[2]
  25.                 tabla2[Fl2, 3].Text = tupla[3]
  26.                 tabla2[Fl2, 4].Text = tupla[4]
  27.                 tabla2[Fl2, 5].Text = tupla[5]
  28.                 tabla2[Fl2, 6].Text = tupla[6]
  29.                 tabla2[Fl2, 7].Text = tupla[7]
  30.                 tabla2[Fl2, 8].Text = tupla[8]
  31.                 tabla2[Fl2, 9].Text = tupla[9]
  32.                 tabla2[Fl2, 10].Text = tupla[10]
  33.                 
  34.                 tabla2[Fl2, 11].Text = tupla[11]
  35.                 tabla2[Fl2, 12].Text = tupla[12]
  36.                 tabla2[Fl2, 13].Text = tupla[13]
  37.                 tabla2[Fl2, 14].Text = tupla[14]
  38.                 tabla2[Fl2, 15].Text = tupla[15]
  39.                 tabla2[Fl2, 16].Text = tupla[16]
  40.                 
  41.                 tupla[0] = "" 'nombre
  42.                 tupla[1] = "" 'apellido
  43.                 tupla[2] = "" 'cedula
  44.                 tupla[3] = "" 'codigo
  45.                 tupla[4] = "" 'cargo
  46.                 tupla[5] = "" 'banco
  47.                 tupla[6] = "" 'cuenta
  48.                 tupla[7] = "" 'sueldo fijo
  49.                 tupla[8] = "" 'sueldo hora
  50.                 tupla[9] = "" 'sexo
  51.                 tupla[10] = "" 'comunidad
  52.                 tupla[11] = "" 'academico
  53.                 tupla[12] = "" 'clasificacion
  54.                 tupla[13] = "" 'telefono 1 y 2
  55.                 tupla[14] = "" 'fecha
  56.                 tupla[15] = "" 'contrato
  57.                 tupla[16] = "" 'PAD
  58.                 
  59.                 tupla[i] = d
  60.                 d = ""
  61.                 
  62.                 i = 0
  63.                 Fl2 += 1
  64.                 Wait
  65.                 lblanalizado.Text = Fl2
  66.             Else
  67.                 d &= z 'completa un campo carater por caracter
  68.             Endif
  69.         Endif
  70.     Next


GAMBAS
  1. Private Sub TituloGrid()
  2.     tabla2.Clear
  3.     Fl2 = 0
  4.     
  5.     tabla2.Columns.Count = 17  'Cantidad de Columnas de la tabla
  6.     tabla2.Rows.Count = 10000 'Cantidad del Filas
  7.     tabla2.columns.Resizable = True
  8.     
  9.     tabla2.Columns[0].Text = "Nombre"
  10.     tabla2.Columns[0].Width = 110
  11.     tabla2.Columns[1].Text = "Apellidos"
  12.     tabla2.Columns[1].Width = 110
  13.     tabla2.Columns[2].Text = "Cédula"
  14.     tabla2.Columns[2].Width = 90
  15.     tabla2.Columns[3].Text = "Código"
  16.     tabla2.Columns[3].Width = 65
  17.     tabla2.Columns[4].Text = "Cargo"
  18.     tabla2.Columns[4].Width = 70
  19.     tabla2.Columns[5].Text = "Banco"
  20.     tabla2.Columns[5].Width = 70
  21.     tabla2.Columns[6].Text = "Cuenta"
  22.     tabla2.Columns[6].Width = 85
  23.     tabla2.Columns[7].Text = "Suel.Fijo"
  24.     tabla2.Columns[7].Width = 65
  25.     tabla2.Columns[8].Text = "Suel.Hora"
  26.     tabla2.Columns[8].Width = 65
  27.  
  28.     tabla2.Columns[9].Text = "Sexo"
  29.     tabla2.Columns[9].Width = 40
  30.     tabla2.Columns[10].Text = "Comunidad"
  31.     tabla2.Columns[10].Width = 50
  32.     tabla2.Columns[11].Text = "Academico"
  33.     tabla2.Columns[11].Width = 50
  34.     tabla2.Columns[12].Text = "Clasific."
  35.     tabla2.Columns[12].Width = 50
  36.     tabla2.Columns[13].Text = "telefono1 y 2"
  37.     tabla2.Columns[13].Width = 50
  38.     tabla2.Columns[14].Text = "fecha"
  39.     tabla2.Columns[14].Width = 50
  40.     tabla2.Columns[15].Text = "contrato"
  41.     tabla2.Columns[15].Width = 50
  42.     tabla2.Columns[16].Text = "pad"
  43.     tabla2.Columns[16].Width = 1



2- Exportar los datos del Grid a HTML y de ahí convertirlos en PDF con el programa WKHTMLTOPDF desde usando un shell
lo he conseguido así:
GAMBAS
  1. Procedure eTabla()
  2.   Dim DocHtml As String
  3.  
  4.    DocHtml = "<html>"
  5.         DocHtml &= "<head>"
  6.             DocHtml &= "<title > GRID EXPORTADO </title >"
  7.         DocHtml &= "</head >"
  8.         
  9.         DocHtml &= "<body>"
  10.             
  11.         'INICIA TABLA
  12.         DocHtml &= "<table align='center' width= 100% border= '1' cellpadding='0' cellspacing='0'>"
  13.             DocHtml &= "<tr align='center' width='10%'>"
  14.             DocHtml &= "<td>"
  15.  
  16.                   For i = 0 To tabla2.Rows.Count - 1
  17.                      DocHtml &= "<tr> \n"
  18.                      
  19.                      DocHtml &= "<td align='right'><font color=black face='Courier, arial' size=2><b>" & tabla2[i, 0].Text & "</b></font></td>\n"
  20.                      DocHtml &= "<td align='right'><font color=black face='Courier, arial' size=2><b>" & tabla2[i, 1].Text & "</b></font></td>\n"
  21.                      DocHtml &= "<td align='right'><font color=black face='Courier, arial' size=2><b>" & tabla2[i, 2].Text & "</b></font></td>\n"
  22.                      DocHtml &= "<td align='right'><font color=black face='Courier, arial' size=2><b>" & tabla2[i, 3].Text & "</b></font></td>\n"
  23.                      
  24.                      DocHtml &= "</tr>\n"
  25.                   Next
  26.  
  27.             
  28.             DocHtml &= "</td>"
  29.         DocHtml &= "</table>"
  30.         'FIN DE LA TABLA
  31.         
  32.         DocHtml &= "</body>"
  33.         
  34.     DocHtml &= "</html>"
  35.     
  36.     file.save(User.home & "/mi_Grid.html", DocHtml)



El código se puede refinar mucho más, se puede usar poppler en vez de WKHTMLTOPDF, pero no lo he usado nunca. 
Mientras se estan pasando los datos a HTML se pueden hacer muchas cosas como: cambiar color al texto, ancho de celdas, negritas, tipografía,
y hasta una CSS se puede usar.
Saludos.
Hola, Shordi

Dos cuestiones:

Primera, veo que en el último campo de cada registro se crea un separador ; aún no siendo necesario porque ya tienes el salto de línea al siguiente registro. Esto podría provocar que se creen campos vacíos al final de cada registro dependiendo del programa que importe el fichero.

Segunda, si utilizamos el evento Data para cargar datos en el grid, podría ocurrir que sólo se exportasen los datos visibles en éste, ya que el evento sólo carga datos cuando deben visualizarse y no antes. Lo digo porque si la base de la creación del CSV es la iteración por las celdas del grid, no parece una buena idea.

Normalmente uno exporta y convierte los datos provenientes de la consulta, ya sea SQL o de otro tipo de fuente de datos, que es más fiable (y posiblemente más rápido) que "leer" las celdas una por una.

En cualquier caso, puede ser de mucha utilidad para determinadas tareas pequeñas, gracias por enseñarnos tu trabajo, shordi.

Saludos
Alessandri gracias por tu interés en mi trabajo. Unas puntualizaciones:
Cita:1-Permitir copiar desde una hoja de cálculo y pegar en el grid.
El problema ahí es lo que llamas "copiar y pegar". Las hojas de cálculo tienen todas una opción de "guardar como csv", que creo que es lo correcto. y luego pasas ese csv como fuente de datos del Gridview, como digo en el ejemplo de la segunda mitad del vídeo.
Cita:
Código:
....
...
Dim tm As String
    Dim l As Single
    Dim z As String
    Dim final As String
    Dim d As String
    Dim tupla As New String[17]
    Dim i As Integer
 
    
    TituloGrid
    
    tm = Len(ta.Text)
    For l = 1 To tm
        z = Mid(ta.Text, l, 1)
        If z = "\t" Then
 
             tupla[i] = d
             i += 1
             d = ""
             
        Else
            If z = "\n" Then
                tabla2[Fl2, 0].Text = tupla[0]
                tabla2[Fl2, 1].Text = tupla[1]
                tabla2[Fl2, 2].Text = tupla[2]
                tabla2[Fl2, 3].Text = tupla[3]
                tabla2[Fl2, 4].Text = tupla[4]
                tabla2[Fl2, 5].Text = tupla[5]
                tabla2[Fl2, 6].Text = tupla[6]
                tabla2[Fl2, 7].Text = tupla[7]
                tabla2[Fl2, 8].Text = tupla[8]
                tabla2[Fl2, 9].Text = tupla[9]
                tabla2[Fl2, 10].Text = tupla[10]
...
...
En este procedimiento estás "destripando" un CSV que utiliza el tabulado como separador. En el control se asume que el separador es un ;. como puedes ver en éste código sacado de la clase extgrid del proyecto adjunto al vídeo (te subrayo en verde la línea donde se asume)
Cita:Private Sub Source_Write(Value As Variant)

    Dim n As Integer
    Dim asLine As String[]
    Dim sValue As String
    Dim v As Variant
    Dim col As Collection
    
    Me.Clear()
    Try sValue = Value 'Intentamos meter el Value en un string
    If Not Error Then
        If Exist(Value) Then ' es un Path
            asLine = Split(File.Load(Value), "\n", "", True)
        Else
            asLine = Split(Value, "\n", "", True) 'es el contenido del csv o un texto
        Endif
        $avSource = New Variant[]
        For n = 0 To asLine.Max
            $avSource.Add(Split(asLine[n], ";")) 'Asumimos el ; como separador. Se podría crear una propiedad, usar un inputbox o un formulario de opciones para afinar esto
        Next

        $asFieldNames = $avSource[0].Copy() 'usamos la primera línea como nombre de campo
        $avSource.Extract(0) 'y la eliminamos del source
        For n = 0 To $avSource[0].Max
            $anFieldTypes.Add(CellType($avSource[0][n])) 'Determinamos el tipo de dato de cada celda según el contenido de la primera fila
        Next
    Else If Value Is String[] Then 'Es un String[] y lo convertimos en un Variant[][]
         $avSource = New Variant[]
....
....
Como ves, el control que paso a los vídeos es una simplificación para no liarla mucho. Sin cambiarle nada, puedes, hacer algo como:
GAMBAS
  1. micsv = File.load(mipath&/nombreficherocsv)
  2. micsv=replace(micsv,"\t",";")
  3. file.save(mipath&/nombreficherocsv,micsv)
  4. miExtGrid.Source=micsv


Esto si el carácter ";" no va a ocasionar conflicto con el contenido del fichero, claro. Si así fuese habría que cambiar el carácter en el código o añadir una propiedad "separator" al control para poder cambiarlo sin problemas.
Pero es mucho más sencillo, si eres tú quien genera el CSV decirle a la hoja de cálculo que utilice ";" como separador.
 
Cita:
Código:
     Private Sub TituloGrid()
        tabla2.Clear
        Fl2 = 0
        
        tabla2.Columns.Count = 17  'Cantidad de Columnas de la tabla
        tabla2.Rows.Count = 10000 'Cantidad del Filas
        tabla2.columns.Resizable = True
        
        tabla2.Columns[0].Text = "Nombre"
        tabla2.Columns[0].Width = 110
        tabla2.Columns[1].Text = "Apellidos"
        tabla2.Columns[1].Width = 110
        tabla2.Columns[2].Text = "Cédula"
        tabla2.Columns[2].Width = 90
        tabla2.Columns[3].Text = "Código"
        tabla2.Columns[3].Width = 65
        tabla2.Columns[4].Text = "Cargo"
        tabla2.Columns[4].Width = 70
        tabla2.Columns[5].Text = "Banco"
        tabla2.Columns[5].Width = 70
        tabla2.Columns[6].Text = "Cuenta"
        tabla2.Columns[6].Width = 85
        tabla2.Columns[7].Text = "Suel.Fijo"
        tabla2.Columns[7].Width = 65
        tabla2.Columns[8].Text = "Suel.Hora"
        tabla2.Columns[8].Width = 65
     
        tabla2.Columns[9].Text = "Sexo"
        tabla2.Columns[9].Width = 40
        tabla2.Columns[10].Text = "Comunidad"
        tabla2.Columns[10].Width = 50
        tabla2.Columns[11].Text = "Academico"
        tabla2.Columns[11].Width = 50
        tabla2.Columns[12].Text = "Clasific."
        tabla2.Columns[12].Width = 50
        tabla2.Columns[13].Text = "telefono1 y 2"
        tabla2.Columns[13].Width = 50
        tabla2.Columns[14].Text = "fecha"
        tabla2.Columns[14].Width = 50
        tabla2.Columns[15].Text = "contrato"
        tabla2.Columns[15].Width = 50
        tabla2.Columns[16].Text = "pad"
        tabla2.Columns[16].Width = 1
    End

Si cuando generas el csv en la hoja de cálculo le dices que utilice las cabeceras como primera línea te ahorrarás todo esto. Si tu hoja de cálculo no tiene las cabeceras adecuadas y quieres cambiarlas no es necesario este código. Para eso está la propiedad Titles del gridview. y los anchos de columna, si no te valen con ancho automático sólo tienes que introducirlos en la propiedad Widths. Con lo que se quedaría así:
Cita:
GAMBAS
  1. micsv = File.load(mipath&/nombreficherocsv)
  2. micsv=replace(micsv,"\t",";")
  3. file.save(mipath&/nombreficherocsv,micsv)
  4. miExtgrid.Source=micsv
  5. miExtgrid.Titles=["Nombre","Apellidos","Cédula","Código","Cargo","Banco","Cuenta",...etc, etc.]
  6. miExtgrid.Widths=[110,110,90,65,70,70,85...etc.etc]


De todas formas la generación de CSV no es tarea del control. El sólo los carga no los procesa. Eso queda para la Hoja de Cálculo... o para el programa del vídeo, donde puedes añadir todas las opciones y tejemanejes que quieras.

Un saludo.

(24-07-2021, 20:00)jguardon escribió: [ -> ]Hola, Shordi

Dos cuestiones:

Primera, veo que en el último campo de cada registro se crea un separador ; aún no siendo necesario porque ya tienes el salto de línea al siguiente registro. Esto podría provocar que se creen campos vacíos al final de cada registro dependiendo del programa que importe el fichero. Totalmente cierto. Se me pasó el detalle. Lo corrijo y actualizo el control adjunto al vídeo.

Segunda, si utilizamos el evento Data para cargar datos en el grid, podría ocurrir que sólo se exportasen los datos visibles en éste, ya que el evento sólo carga datos cuando deben visualizarse y no antes. Lo digo porque si la base de la creación del CSV es la iteración por las celdas del grid, no parece una buena idea.

Normalmente uno exporta y convierte los datos provenientes de la consulta, ya sea SQL o de otro tipo de fuente de datos, que es más fiable (y posiblemente más rápido) que "leer" las celdas una por una.
No se miran las celdas. los valores se extraen a través de la propiedad Value del array Source del control. Eso hace que sea más rápido que recurrir a la base de datos (al menos si la base de datos no es local de la máquina). La fiabilidad del tipo de datos devuelto (de caracter o numérico) es 100% si se cargó desde una base de datos y, digamos, al 90 por cien si se hizo desde un CSV. El código es éste tomado de la función Values_read() del control:

GAMBAS
  1.      col = New Collection
  2.             For i = 0 To Me.Columns.Count - 1
  3.                 Select Case $anFieldtypes[i]
  4.                     Case gb.String                
  5.                         v = $avSource[anSelection[n]][i] 'donde $avSource es el array bidimensional fuente del Control.
  6.                     Case gb.Integer
  7.                         v = Val($avSource[anSelection[n]][i])
  8.                     Case gb.Float
  9.                         v = Val($avSource[anSelection[n]][i])
  10.                 End Select
  11.                 col.Add(v, $asFieldNames[i])
  12.             Next




En cualquier caso, puede ser de mucha utilidad para determinadas tareas pequeñas, gracias por enseñarnos tu trabajo, shordi.

Saludos

y gracias por tu interés.


Saludos
Muy bien. gracias.
De todas formas eso a lo que apuntas también lo tengo hecho por ahí. Me refiero al mecanismo de impresión en html y pdf a partir de un gridview, con CSS por medio y demás. Dame unos días para que lo adecúe a los nuevos controles (ahora están imbricados en el software de mi antigua empresa) y los presentaré en sociedad.
(24-07-2021, 21:25)Shordi escribió: [ -> ]No se miran las celdas. los valores se extraen a través de la propiedad Value del array Source del control.

Pues se me pasó ese detalle. Efectivamente esa es la manera más rápida y fiable, pero por un momento pensé que estabas tomando los valores desde las celdas. Fantástico! Tongue

Saludos