martes, 28 de julio de 2015

24 - Python PyQt (Interfaz gráfica) - SMTP Email (Hotmail, Gmail, Yahoo, ...)




En este capítulo del tutorial de Python 3 más PyQt5 crearemos una sencilla aplicación que nos permita enviar correos con servidores que dispongan del protocolo SMTP (protocolo para transferencia simple de correo) tales como Hotmail, Gmail, Yahoo, ...

El programa permitirá al usuario configurar sus preferencias tales como el servidor STMP, puerto, credenciales, uso opcional del protocolo starttls, adjuntar multiples archivos (QFileDialog), agregar múltiples destinatarios, etc.

El programa mostrará en una etiqueta QLabel el proceso de envío del correo así como cualquier excepción que pudiera ocurrir.

Configuración para servidores SMTP clásicos:

Hotmail:
– SMTP - smtp.live.com (TLS enabled, port 25)


GMail by Google:
– SMTP - smtp.gmail.com (TLS enabled, port 587)


Yahoo! Mail:
– SMTP - smtp.mail.yahoo.com (port 25)


Lycos Mail:
– SMTP - smtp.mail.lycos.com (port 25)


AOL Mail Settings:
– SMTP - smtp.aol.com (port 25)




smtp.pyw


import sys, smtplib, email
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QMessageBox, QPushButton, QLabel, QLineEdit, QFileDialog, QTextEdit, QCheckBox


class FileDialog(QFileDialog):
 def __init__(self):
  QFileDialog.__init__(self)
  self.setFileMode(QFileDialog.ExistingFiles) #Selección múltiple
  self.files = [] #Lista que guardará las rutas de los archivos
  self.filesSelected.connect(self.getFiles) #Activar la señal al seleccionar archivos
 def getFiles(self):
  self.files = self.selectedFiles() #Agregar las rutas de archivos a la lista

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.setWindowTitle("Enviar email SMTP")
  self.resize(450, 600)
  #Crear objetos en el cuadro de diálogo
  self.layout = QGridLayout()
  self.setLayout(self.layout)
  self.label_host = QLabel("Host SMTP:")
  self.txt_host = QLineEdit()
  self.chk_tls = QCheckBox("STARTTLS")
  self.chk_tls.setChecked(True)
  self.label_port = QLabel("Puerto:")
  self.txt_port = QLineEdit()
  self.label_user = QLabel("Usuario:")
  self.txt_user = QLineEdit()
  self.label_pass = QLabel("Password:")
  self.txt_pass = QLineEdit()
  self.txt_pass.setEchoMode(QLineEdit.Password)
  self.label_from = QLabel("Remitente:")
  self.txt_from = QLineEdit()
  self.label_to = QLabel("Destinatario/s:")
  self.txt_to = QLineEdit()
  self.label_subject = QLabel("Asunto:")
  self.txt_subject = QLineEdit()
  self.label_dialog = QLabel("Adjunto:")
  self.btn_dialog = QPushButton("Seleccionar ...")
  self.label_body = QLabel("Mensaje: ")
  self.txt_body = QTextEdit()
  self.label_state = QLabel("")
  self.btn_send = QPushButton("Enviar")
  #Crear objeto de la clase FileDialog()
  self.fileDialog = FileDialog()
  
  #Agregar objetos al layout grid
  self.layout.addWidget(self.label_host)
  self.layout.addWidget(self.txt_host)
  self.layout.addWidget(self.chk_tls)
  self.layout.addWidget(self.label_port)
  self.layout.addWidget(self.txt_port)
  self.layout.addWidget(self.label_user)
  self.layout.addWidget(self.txt_user)
  self.layout.addWidget(self.label_pass)
  self.layout.addWidget(self.txt_pass)
  self.layout.addWidget(self.label_from)
  self.layout.addWidget(self.txt_from)
  self.layout.addWidget(self.label_to)
  self.layout.addWidget(self.txt_to)
  self.layout.addWidget(self.label_subject)
  self.layout.addWidget(self.txt_subject)
  self.layout.addWidget(self.label_dialog)
  self.layout.addWidget(self.btn_dialog)
  self.layout.addWidget(self.label_body)
  self.layout.addWidget(self.txt_body)
  self.layout.addWidget(self.label_state)
  self.layout.addWidget(self.btn_send)
  
  #Señal para abrir el cuadro de diálogo de selección de archivos
  self.btn_dialog.clicked.connect(self.openDialog)
  #Señal para proceder a enviar los correos
  self.btn_send.clicked.connect(self.sendMail)
  
 #Método encargado de abrir el cuadro de diálogo de selección de archivos
 def openDialog(self):
  self.fileDialog.open()
  
 #Método encargado de procesar el envío de correos
 def sendMail(self):
  To = self.txt_to.text().split(", ") #Lista con los destinatarios
  #Capturar cualquier posible excepción y mostrarlo en el label label_state
  try:
   #Recorrer los destinatarios
   for destinatario in To:
    #Guardar los valores del formulario
    Host = self.txt_host.text()
    Port = self.txt_port.text()
    User = self.txt_user.text()
    Pass = self.txt_pass.text()
    From = self.txt_from.text()
    Subject = self.txt_subject.text()
    Body = self.txt_body.toPlainText()
    
    #Objeto para procesar el envío de correo
    smtp = smtplib.SMTP(Host, Port)
    #Si el servidor utilizar el protocolo starttls activarlo
    if self.chk_tls.isChecked(): smtp.starttls()
    #Credenciales
    smtp.login(User, Pass)
    #Iniciar la cabecera
    header = MIMEMultipart()
    header['From'] = From
    header['To'] = destinatario
    header["Subject"] = Subject
    msg = MIMEText(Body, 'html') #Content-type:text/html
    header.attach(msg)
    #Lista para agregrar en la cabecera los archivos a enviar
    parts = []
    for file in self.fileDialog.files:
     parts.append(MIMEBase('application', "octet-stream"))
    
    #Nos permite indexar la ruta de los archivos que se encuentran
    #en el atributo files del objeto fileDialog
    x = 0
    #Recorrer archivo a archivo
    for part in parts:
     #Lectura del archivo
     part.set_payload(open(self.fileDialog.files[x], "rb").read())
     #Codificar el archivo en base64
     encoders.encode_base64(part)
     #Extracción del nombre del archivo
     explode = self.fileDialog.files[x].split("/")
     filename = explode[len(explode)-1]
     #Añadir archivo a la cabecera
     part.add_header('Content-Disposition', 'attachment; filename="'+filename+'"')
     header.attach(part)
     x = x + 1
    #Enviar el correo al destinatario
    smtp.sendmail(From, destinatario, header.as_string())
    #Terminar la conexión con el servidor
    smtp.quit()
    #Mostrar el proceso
    self.label_state.setText("Enviando a " + destinatario + " ... ")
    
  except Exception as e: 
   #Capturar cualquier posible excepción y mostrarla
   self.label_state.setText(str(e))
    
  parts[:] = [] #Limpiar la lista de archivos
  self.fileDialog.files[:] = [] #Limpiar la lista de archivos
  self.label_state.setText("Envío finalizado")
     
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


1 comentario:

Francisco Alba dijo...

Muchas gracias por este tutorial, me ha sido de mucha utilidad.