Generar un PDF a partir de un sencillo formulario en PyQt

Generar PDF en Python
Generar PDF en Python

La idea de esta entrada, como su titulo ya describe, es crear en QtDesigner(PyQt) un
sencillo formulario gráfico que generará un PDF con los datos ingresados por el usuario.

Algunos ejemplos de uso: Ingreso de un alumno a un curso, ingreso de un nuevo funcionario a una empresa, etc.

La idea es llenar el formulario, y una ves que se presione el botón PDF, se abra automáticamente el archivo Formulario.pdf con los datos que acabamos de llenar.
Si se analiza con profundidad el código, se podrían mejorar muchas cosas, como por ejemplo añadir excepciones de error y alguna cosa mas. Pero la idea principal de la entrada es ver de forma sencilla como crear un Formulario en python y generar un PDF para imprimir o guardar.

Estas pruebas fueron
realizadas en un sistema Linux (Ubuntu)

Fecha: 14/04/2015

Dejo también acá,
algunas entradas sobre PyQt por si no estas familiarizado con el
tema:
La librería que utilizaremos para trabajar con PDF en python será: reportlab
Aquí dejo el link con la guía de instalación tanto para Windows, Mac OS y Linux: https://bitbucket.org/rptlab/reportlab

Diseño en QtDesigner

Nombre que utilicé para los objetos en QtDesigener

1 botón : btn_pdf
6 etiquetas (no cambio el objetName porque en este caso no las utilizo en el código), pero si les agrego el texto (Nombre, Apellido, Correo, Curso, Pais y Observaciones)
6 textEdit :
  1. Para el Nombre = textNombre
  2. Para el Apellido = textApellido
  3. Para el Correo = textCorreo
  4. Para el Curso = textCurso
  5. Para el País = textPais
  6. Para las Observaciones = textObs

Código del Programa

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Formulario que Genera PDF
# www.pythondiario.com

import sys
import os
from reportlab.pdfgen import canvas
from PyQt4 import QtCore, QtGui, uic
 
# Cargar nuestro archivo .ui
form_class = uic.loadUiType("formularioPDF.ui")[0]
 
class MyWindowClass(QtGui.QMainWindow, form_class):
 def __init__(self, parent=None):
  QtGui.QMainWindow.__init__(self, parent)
  self.setupUi(self)
  
  self.btn_pdf.clicked.connect(self.generarPDF)

  
 # Evento del boton btn_pdf
 def generarPDF(self):
  
  # Guardo en las variables los datos de los textEdit Ingresados por el Usuario
  self.nombre = str(self.textNombre.toPlainText())
  self.apellido = str(self.textApellido.toPlainText())
  self.correo = str(self.textCorreo.toPlainText())
  self.curso = str(self.textCurso.toPlainText())
  self.pais = str(self.textPais.toPlainText())
  self.obs = str(self.textObs.toPlainText())
  
    
  # Ruta donde quiero crear el PDF
  c = canvas.Canvas("/home/diego123/Escritorio/Formulario.pdf")
  c.drawString(100,750,"Este formulario fue creado en www.pythondiario.com")
  c.drawString(100,700,("Nombre: "+ self.nombre))
  c.drawString(100,680,("Apellido: "+ self.apellido))
  c.drawString(100,660,("Correo: "+ self.correo))
  c.drawString(100,640,("Curso: "+ self.curso))
  c.drawString(100,620,("Pais: "+ self.pais))
  c.drawString(100,600,("Observaciones: "+ self.obs))
  c.save()
    
  # PARA WINDOWS: os.system("start AcroRD32 ruta_y_archivo.pdf &")
  os.system("evince /home/diego123/Escritorio/Formulario.pdf &") 
  
 
if __name__ == "__main__":
 app = QtGui.QApplication(sys.argv)
 MyWindow = MyWindowClass(None)
 MyWindow.show()
 app.exec_()

Análisis del código

Importamos los módulos necesarios para trabajar:

import sys
import os
from reportlab.pdfgen import canvas
from PyQt4 import QtCore, QtGui, uic

El módulo os será necesario para abrir el archivo.pdf una ves sea creado. También importamos el módulo reportlab para crear el archivo.pdf. Los otros módulos son necesarios para la aplicación gráfica.

Cargamos el archivo.ui de QtDesigner:

# Cargar nuestro archivo .ui
form_class = uic.loadUiType("formularioPDF.ui")[0]

Creamos la clase MyWindowClass con su inicializador

class MyWindowClass(QtGui.QMainWindow, form_class):
 def __init__(self, parent=None):
  QtGui.QMainWindow.__init__(self, parent)
  self.setupUi(self)
  
  self.btn_pdf.clicked.connect(self.generarPDF)

Como podemos ver, creamos un evento (btn_pdf.clicked.connect) que funcionara cada ves que presionemos el botón PDF (hace lo que está en la función generarPDF())

Función generarPDF()

# Evento del boton btn_pdf
 def generarPDF(self):
  
  # Guardo en las variables los datos de los textEdit Ingresados por el Usuario
  self.nombre = str(self.textNombre.toPlainText())
  self.apellido = str(self.textApellido.toPlainText())
  self.correo = str(self.textCorreo.toPlainText())
  self.curso = str(self.textCurso.toPlainText())
  self.pais = str(self.textPais.toPlainText())
  self.obs = str(self.textObs.toPlainText())
  
    
  # Ruta donde quiero crear el PDF
  c = canvas.Canvas("/home/diego123/Escritorio/Formulario.pdf")
  c.drawString(100,750,"Este formulario fue creado en www.pythondiario.com")
  c.drawString(100,700,("Nombre: "+ self.nombre))
  c.drawString(100,680,("Apellido: "+ self.apellido))
  c.drawString(100,660,("Correo: "+ self.correo))
  c.drawString(100,640,("Curso: "+ self.curso))
  c.drawString(100,620,("Pais: "+ self.pais))
  c.drawString(100,600,("Observaciones: "+ self.obs))
  c.save()
    
  # PARA WINDOWS: os.system("start AcroRD32 ruta_y_archivo.pdf &")
  os.system("evince /home/diego123/Escritorio/Formulario.pdf &") 

Esta función se encarga de obtener los datos ingresados por el usuario y generar el PDF a partir de estos datos.
En las variables: self.nombre, self.apellido .... se guarda cada dato ingresado, para eso utilizamos el método toPlainText().
Luego creamos el archivo.pdf con la instrucción:

c = canvas.Canvas("/home/diego123/Escritorio/Formulario.pdf")

y más abajo agregamos los datos con la función drawString(coordenadas + texto).
c.save() se encarga de guardar el archivo, y la última linea:

# PARA WINDOWS: os.system("start AcroRD32 ruta_y_archivo.pdf &")
os.system("evince /home/diego123/Escritorio/Formulario.pdf &")

se encarga de abrir atuomáticamente el archvo.pdf . En el comentario dejo también la instrucción para abrirlo en Windows.

Finalizamos el programa con el bloque de código ya conocido:

if __name__ == "__main__":
 app = QtGui.QApplication(sys.argv)
 MyWindow = MyWindowClass(None)
 MyWindow.show()
 app.exec_()

Espero que esta entrada sea de guia para sus futuras aplicaciones. Dejar sus comentarios ante cualquier duda o sugerencia.

Saludos, Diego.

  1. Unknown dice:

    Muchas Gracias Amigos... Justo lo que buscaba

    1. PythonDiario dice:

      Me alegro te sea de ayuda 😉
      Saludos

  2. SalvaMX dice:

    Muchas gracias por la información, funciona de maravilla.
    ¿Podrías hacer alguna guía de como crear un ejecutable .exe para esta aplicación si no es mucha molestia?

    1. PythonDiario dice:

      Hola Salvador, me has dado la idea para una nueva entrada. Por el momento no estoy con mucho tiempo, pero ni bien pueda publicaré algo al respecto. Saludos

    2. SalvaMX dice:

      Despues de investigar un poco ya pude generar un ejecutable de este ejercicio con cx_Freeze, gracias

    3. PythonDiario dice:

      Estaría bueno poder crear alguna entrada con esa información. Saludos

  3. Leonardo Calcagno dice:

    Excelentes tutoriales.
    Recien empiezo con esto y se entiende todo perfectamente.
    Gracias x compartirlos.

    1. PythonDiario dice:

      Excelente Leonardo, me alegro te sean de ayuda los tutoriales. Gracias por visitar el blog 😉

  4. Unknown dice:

    Hola, muchas gracias por tus aportes, la verdad que gracias a ellos aprendo mucho.
    Necesito ayuda con este error:

    File "formularioPDF.py", line 27, in generarPDF
    self.nombre = str(self.textNombre.toPlainText())
    AttributeError: 'QLineEdit' object has no attribute 'toPlainText'

    estuve buscando y no encuentro una solucion. Muchas gracias, saludos.

    1. alexcbn dice:

      Es muy Facil Esa pregunta utilizaste un QLineEdit y debe ser un TextEdit, se parecen pero en el QtDisegner no son lo mismo por que tienen metodos diferentes. por eso no reconoce el metodo ToplainText, cambialo y listo

  5. Unknown dice:

    Hola Nico el error es que tenes que usar en el Diseño TextEdit y no LineEdit

  6. Unknown dice:

    Excelente aporte, yo quiero crear un pdf mostrando los datos de una tabla de una consulta en postgresql, me funcionaria tu ejemplo? o este es muy básico? por casualidad tienes algún material? Gracias 🙂

    1. PythonDiario dice:

      Hola. Gracias por visitar el blog. Necesitas utilizar entorno gráfico o sólo generar el pdf? No tengo material al respecto pero pienso que no debe ser complicado cargar los datos de esa consulta y generar el pdf. Saludos

    2. Unknown dice:

      Gracias por responder 😀 Tengo un formulario y me muestra la tabla con los datos de la consulta cargados y un botón pdf al darle click quiero que me muestre los datos tal cual como en la tabla. que podría hacer??

    3. PythonDiario dice:

      Podrías hacer lo mismo que hice yo: cada campo del formulario pasarlo con la función drawString, puedes guardarlo en una variable para que te quede más cómodo. Saludos

    4. Roberto dice:

      Buenas Maritrini, ya es un poco antigua su pregunta, pero me la he encontrado en internet y tengo la misma duda. Yo soy principiante en Python.
      Quisiera saber si siempre pudo realizar la consulta de hace, de accesar una base de datos y mostrar los campos por medio del reportlab.

      Me interesa mucho saber sobre este tema ya que debo hacer una gran cantidad de reportes financieros, con cortes control inclusive.

      Mucho agradecería la ayuda.

      Roberto
      Costa Rica
      rmatarria@gmail.com

  7. Unknown dice:

    Que excelente sitio, ojala tuviera mas tiempo para seguirla.

    1. PythonDiario dice:

      Hola, muchas gracias por tu visita y comentarios ;). Saludos

  8. salva dice:

    Hola, seguí los pasos y se crea todo lo que debe generarse, pero al ejecutar la aplicación con el ejecutable generado me salta el siguiente error: 'ImportError: No module named PyQt4'. ¿Existe algún problema con pyqt4 a la hora de generar ejecutable? Muchas gracias de antemano.

    1. PythonDiario dice:

      Hola Salva, gracias por visitar el blog. Fijate en el ejemplo dos de esta entrada: Generar un archivo exe con python. Saludos

    2. salva dice:

      leí todas las entradas, el único problema es que yo no utilice QtDesigner para mi interfaz, la hice a mano con QWidget en vez de MainWindow, ¿puede ser que ese sea el probema? He estado intentando acoplar mi interfaz a una clase QtGui.QMainWindow, pero me salen todos mis widget en el mismo punto de la ventana principal, ya que estaba utilizando un grid.

    3. PythonDiario dice:

      Puede venir por ese lado si. Yo hace un tiempito estoy por fuera de PyQt y no se si pueda ayudarte. Vas a tener que seguir metiendo mano, quizá alguien más pueda responderte. Saludos

    4. salva dice:

      Muchas gracias de todas formas, he ido descubriendo que podía ser. Y me he dado cuenta que podría ser un problema de faltas de librerias en windows a la hora de hacer el ejecutable. He instalado el paquete de pandas, numpy, pyqt, fpdf y al menos he conseguido que ejecutara en windows el .py. Aún así cuando se ejecuta la app sigue dando fallos el pandas. Saludos

  9. crostow dice:

    Hola diego una pregunta estoy generando un pdf dinamicamente nada mas que tengo un problema ya que al momento de que es dinamico en ocasiones hay datos que sobrepasan los que caben en 1 hoja tendras alguna opcion para que cuando se llene la primera escriba en una segunda hoja

    1. PythonDiario dice:

      Hola, gracias por visitar el blog. Te dejo el enlace de la documentación oficial: https://www.reportlab.com/docs/reportlab-userguide.pdf . Saludos

  10. escritorinspirado dice:

    Hola, muy interesante este ejemplo. Tengo una pregunta que no tiene nada que ver con esto de pdf sino con otro programa de Python/Tkinter y como no encuentro una sección "general" para hacer preguntas, pues la hago aquí. De antemano perdón si no debí hacerlo aquí.
    Llevo unos meses traveseando con Python y Tkinter. Quiero hacer una aplicación para realizar mapas de direcciones muy básico, que arrastrando formas en un canvas el usuario vaya construyendo el mapa. El problema es que no encuentro la manera de hacer drag&drop sobre una imagen .gif en el canvas pero que NO sea precargada al inicio, sino que el usuario pueda escoger cualquier imagen de un menú y esa imagen la pueda arrastrar con el mouse y ubicarla donde desee en el canvas. Ya tengo hecha toda la GUI, puedo cargar las imágenes .gif que desee en el canvas, las veces que desee y puedo moverlas a diferentes intervalos con el teclado, pero sería más lógico, fácil y rápido hacerlo con el mouse. He buscado por todas partes y sólo encuentro ejemplos que funcionan con una única image cargada desde el inicio junto con la aplicación, pero no que se puedan cargar varias luego ¿Es posible lograr lo que quiero hacer en Python o estoy perdiendo mi tiempo? Todavía no hago el paso a wxPython ni PyQt ni nada de eso, pero ya sólo me falta esto en Tkinter para tener lista mi aplicación y no me quiero dar por vencido. Tal vez mi email: hackerdeus@terra.com Saludos.

    1. PythonDiario dice:

      Hola Alex, gracias por visitar el blog. Hace tiempo no toco nada de Tkinter, quizá algún colega pueda responderte. Saludo!!!

  11. [LET] Solidus_Hebi dice:

    HOLA ME PODRIAS AYUDAR ME SALE EL SIGUIENTE ERROR .

    Traceback (most recent call last):
    File "C:UsersMusashiDocumentsPDF.py", line 5, in
    from PyQt4 import QtCore, QtGui, uic
    ImportError: No module named 'PyQt4'

    DESDE YA MUCHAS GRACIAS..!! SALUDOS

    1. PythonDiario dice:

      Hola. Gracias por visitar el blog. Te falta instalar el módulo PyQt4
      Saludos

  12. Anónimo dice:

    Hola, esta grandioso este tutorial, me podrías ayudar ya que al intentar probarlo me arroja el siguiente error:

    Traceback (most recent call last):
    File "pruebform.py", line 44, in generarPDF
    c.save()
    File "C:WinPython-32bit-3.3.5.0python-3.3.5libsite-packagesreportlabpdfg
    encanvas.py", line 1209, in save
    self._doc.SaveToFile(self._filename, self)
    File "C:WinPython-32bit-3.3.5.0python-3.3.5libsite-packagesreportlabpdfb
    asepdfdoc.py", line 217, in SaveToFile
    f = open(filename, "wb")
    TypeError: embedded NUL character

    Mi sistema operativo es windows

  13. neodevilcry dice:

    hola me podria decir alguien como hecho andar el ejemplo soy muy nobato aun en esto de python

    1. PythonDiario dice:

      Hola neo, gracias por visitar el blog!!! Que parte no entiendes? has intentado seguir los pasos del tutorial?
      Saludos

    2. neodevilcry dice:

      pues en concreto esto es lo que me sale, agradecieria la ayuda por que aun estoy muy verde

      Traceback (most recent call last):
      File "pdf.py", line 13, in
      form_class = uic.loadUiType("formularioPDF.ui")[0]
      File "/usr/lib/python2.7/dist-packages/PyQt4/uic/__init__.py", line 208, in loadUiType
      winfo = compiler.UICompiler().compileUi(uifile, code_string, from_imports, resource_suffix)
      File "/usr/lib/python2.7/dist-packages/PyQt4/uic/Compiler/compiler.py", line 140, in compileUi
      w = self.parse(input_stream, resource_suffix)
      File "/usr/lib/python2.7/dist-packages/PyQt4/uic/uiparser.py", line 974, in parse
      document = parse(filename)
      File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1182, in parse
      tree.parse(source, parser)
      File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 647, in parse
      source = open(source, "rb")
      IOError: [Errno 2] No such file or directory: 'formularioPDF.ui'

    3. PythonDiario dice:

      Al final del error dice que no está econtrando el archivo "formularioPDF.ui". Te aconsejaría que comienzes con el primer tutorial que hice de PyQt para que entiendas mejor: Primera aplicacion con Qt Designer y Python
      Saludos

  14. Anónimo dice:

    Se pude crear una pagina html con python, es decir que llames al archivo.py desde un formulario html, com se hace en php...? Gracias, Saludos.

  15. Roberto dice:

    Buenas Diego, donde podría obtener un buen ejemplo de acceso a base de datos Postgresql y mostrar el resultado de la lectura de una tabla. Mediante reportlab

    Gracias
    Roberto
    Costa Rica
    rmatarria@gmail.com

  16. Unknown dice:

    Muy útil el ejemplo!!!
    Te consulto, si quiero elegir la ruta y el nombre donde crear el pdf, como lo hago?
    Gracias!!
    Cielo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Te has suscrito correctamente al boletín

Se produjo un error al intentar enviar tu solicitud. Inténtalo de nuevo.

Mi Diario Python will use the information you provide on this form to be in touch with you and to provide updates and marketing.