Python Forum
Unexpected output when trying to attach files in a mail application
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Unexpected output when trying to attach files in a mail application
#1
Dear forum experts:

First of all, excuse my little or no English. Who greets you, I take advantage of my request for help to introduce myself to you as an enthusiastic, but new Python programmer who, even though I already have some experience in programming, many things fail me and when that happens, help will always be welcome and By asking you learn.

I move on to the matter. It happens that I found a Python program that, using the PyQt5 and Tkinter libraries to a lesser extent, sends emails in an effective way and, above all, elegant in the design of the window that said application presents. A fact that attracted me, but like any good programmer, always looking for it to adapt to objectivity, I saw the gaps in it and tried to correct such omissions, such as the fact that it does not include attachments and the fact that the email can be sent in bulk, with the help of an Excel or CSV workbook.

I don't think, I know that what I currently see as a problem can be done, but no matter how much I have tried, I can't find how. I looked for examples on various pages on the internet and I have not found one that suits what I am trying to do. I'm going to the attachments part, and although I see more progress, it still continues to fail because when I apply this option in the tests I have carried out, it closes and does not present the error so I can correct it. As analyzed, I am sure that the problem is something related to the information that should preferably be presented in the comboBox, that is, the files that I am trying to attach to the email.

I present the code immediately for your evaluation and valuable help:

import re
import sys

import tkinter as tk
from tkinter import messagebox, filedialog, ttk

from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import os

from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QDialog, QComboBox, QVBoxLayout, QLabel
from PyQt5.QtCore import QSize, Qt
from PyQt5 import QtWidgets
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import *
from PyQt5.QtGui import * 

from pysender.outlook import OutlookClient

class FormButton(QPushButton):
    def __init__(self, text):
        super(FormButton, self).__init__()
        self.setText(text)
        self.setMinimumWidth(5)

class MiComboBox(QWidget):
    def __init__(self):
        super().__init__()

        self.comboBox = QComboBox(self)
        #self.comboBox.setGeometry(50, 50, 400, 35)
        self.comboBox.addItems()

        self.btn = QPushButton('Click', self)
        #self.btn.setGeometry(170, 120, 120, 35)
        self.btn.clicked.connect(self.getComboValue)

    def getComboValue(self):
        print((self.comboBox.currentText(), self.comboBox.currentIndex()))

class FormLabel(QLabel):
    def __init__(self, text):
        super(FormLabel, self).__init__()
        self.setText(text)
        # Configuración de fuente y tamaño
        self.setFont(QFont('Arial', 11))
        self.setMinimumWidth(60)


class AboutDialog(QMessageBox):
    def __init__(self):
        super(AboutDialog, self).__init__()
        self.setWindowTitle("Acerca de")
        # Configuración de fuente y tamaño
        self.setFont(QFont('Arial', 11))

        self.setIcon(QMessageBox.Information)
        self.setText("PySender version 1.0.0")
        self.setInformativeText("Envío masivo de correo electrónico desde Outlook. "
                                "Asegúrese de tener Outlook instalado en su sistema. "
                                "¿Dudas?, comunicarse con ")

        self.setStandardButtons(QMessageBox.Close)


class ErrorDialog(QMessageBox):
    def __init__(self, title, message):
        super(ErrorDialog, self).__init__()
        self.setWindowTitle("Error")

        self.setIcon(QMessageBox.Warning)
        self.setText(title)
        self.setInformativeText(message)

        self.setStandardButtons(QMessageBox.Ok)


class MessageForm(QWidget):
    def __init__(self):
        super(MessageForm, self).__init__()
        # Configuración de fuente y tamaño
        self.setFont(QFont('Montserrat', 11))

        self.from_field = QLineEdit()
        self.from_field.setDisabled(True)
        self.to_field = QLineEdit() #QTextEdit()
        self.cc_field = QLineEdit()     ##
        self.bcc_field = QLineEdit()    ##
        self.subject_field = QLineEdit()
        self.message_field = QTextEdit()
        self.Attach_field = QLineEdit()       ##
        self.Attach_field.setDisabled(True)   ##

        self.init_widgets()

    def init_widgets(self):
        address_form = QGridLayout()
        address_form.setSpacing(10)
        address_form.addWidget(FormLabel("De:"), 1, 0)
        address_form.addWidget(self.from_field, 1, 1)
        address_form.addWidget(FormButton("Para:"), 2, 0)        
        address_form.addWidget(self.to_field, 2, 1)
        address_form.addWidget(FormLabel("CC:"), 3, 0)  ##
        address_form.addWidget(self.cc_field, 3, 1)     ##
        address_form.addWidget(FormLabel("BCC:"), 4, 0) ##
        address_form.addWidget(self.bcc_field, 4, 1)    ##

        attachment_form = QGridLayout()
        attachment_form.setSpacing(10)
        #toolbar = self.addToolBar("Tools")
        window = QWidget()
        
        ## **
        # Crear una etiqueta para mostrar la ruta del archivo adjunto
        ##attachment_label = tk.Label(window, text="Ningún archivo seleccionado", anchor="w")
        ##style = ttk.Style()
        ##style.configure("Custom.TButton", padding=(0,0,0,0), font=('Arial', 12),anchor='center')
        ##style.map("Custom.TButton",
                  ##foreground=[('pressed', 'black'), ('active', 'black'), ('!disabled', 'black')],
                  ##background=[('pressed', '!disabled', 'white'), ('active', 'white')])
        ## **
        
        self.browse_button = QtWidgets.QPushButton("📎")
        self.browse_button.clicked.connect(self.browse_file)
        attachment_form.addWidget(self.browse_button, 1, 0)
        
        self.file_combobox = QtWidgets.QComboBox(self)              ##
        self.file_combobox.setObjectName("comboBox")                ##
        # Haciéndolo si o no editable (True o False)
        self.file_combobox.setEditable(True)                        ##
        self.file_combobox.activated.connect(self.elegirLista)
        attachment_form.addWidget(self.file_combobox, 1, 1, 1, 1)   ##

        self.remove_button = QtWidgets.QPushButton("❌")             ##
        self.remove_button.clicked.connect(self.remove_file)        ##
        attachment_form.addWidget(self.remove_button, 2, 0)         ##
        
        attachment_form.addWidget(self.Attach_field, 2, 1, 1, 1)    ##

        message_form = QGridLayout()
        message_form.setSpacing(10)
        message_form.addWidget(FormLabel("Asunto:"), 1, 0)
        message_form.addWidget(self.subject_field, 1, 1)
        message_form.addWidget(FormLabel("Cuerpo de mensaje:"), 2, 0)
        message_form.addWidget(self.message_field, 2, 1)    #, 2, 1)

        address_group = QGroupBox("Dirección")
        address_group.setLayout(address_form)

        attachment_group = QGroupBox("Adjuntar")
        attachment_group.setLayout(attachment_form)

        message_group = QGroupBox("Mensaje")
        message_group.setLayout(message_form)

        main_layout = QBoxLayout(QBoxLayout.Down)
        main_layout.addWidget(address_group)
        main_layout.addWidget(attachment_group)
        main_layout.addWidget(message_group)

        self.setLayout(main_layout)

## *******************************************INICIO ARCHIVOS ADJUNTOS*******************************************
    #Definir la función con la que se trabajará el ComboBox
    def elegirLista(self):
        #print((self.comboBox.currentText(), self.comboBox.currentIndex()))
        print((self.file_combobox.currentText(), self.file_combobox.currentIndex()))


    def clear_text_input(self):
        text_input.delete('1.0', tk.END)
        e1.delete(0, tk.END)
        subj.delete(0, tk.END)
        self.attachment_label.config(text="Ningún archivo seleccionado")
        self.file_combobox["values"] = []
        self.file_combobox.set("")

    def browse_file(self):
        file_paths = filedialog.askopenfilenames(filetypes=[("Todos los archivos", "*.*")])
        if len(file_paths) == 0 and self.attachment_label["text"] == "Ningún archivo seleccionado":
            self.attachment_label.config(text="Ningún archivo seleccionado")
            self.file_combobox["values"] = []
        else:
            attachment_path = self.attachment_label["text"]
            if attachment_path!='Ningún archivo seleccionado':
                file_paths = [file_path for file_path in file_paths if file_path not in attachment_path.split(", ")]
                file_paths = (attachment_path.split(", ")) + file_paths
            self.file_combobox["values"] = [file_name.split('/')[-1] for file_name in file_paths]
            self.attachment_label.config(text=", ".join(file_paths))
            self.file_combobox.current(len(file_paths)-1)

    def remove_file(self):
        selected_item = self.file_combobox.get()
        if len(selected_item)!=0:
            attachment_path = self.attachment_label["text"]
            attachment_path = attachment_path.split(", ")
            attachment_path = [path for path in attachment_path if path.split('/')[-1] != selected_item]
            self.attachment_label.config(text=", ".join(attachment_path))
            self.file_combobox["values"] = [file_name.split('/')[-1] for file_name in attachment_path]
            if(len(attachment_path)==0):
                self.file_combobox.set("")
                self.attachment_label.config(text="Ningún archivo seleccionado")
            else:
                self.file_combobox.current(0)
        else:
            messagebox.showerror("Error", "Ningún archivo seleccionado")
## *******************************************FINAL ARCHIVOS ADJUNTOS*******************************************

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.message_form = MessageForm()
        # Configuración de fuente y tamaño
        self.setFont(QFont('Arial', 11))

        self.init_window()
        self.init_menubar()

        self.statusBar()
        self.show()

    def init_window(self):
        self.setWindowTitle("ENVÍO DE EMAIL MASIVO")
        self.setCentralWidget(self.message_form)
        self.resize(800, 600)

    def init_menubar(self):
        # Configuración de fuente y tamaño
        self.setFont(QFont('Arial', 11))

        quit_action = QAction("&Quitar", self)
        quit_action.setIcon(self.style().standardIcon(QStyle.SP_TitleBarCloseButton))
        quit_action.setShortcut("Ctrl+Q")
        quit_action.setStatusTip("Cerrar la aplicación")
        quit_action.triggered.connect(self.quit_application)

        send_action = QAction("&Enviar", self)
        send_action.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
        send_action.setShortcut("Ctrl+Enter")
        send_action.setStatusTip("Enviar mensaje a todos los destinatarios")
        send_action.triggered.connect(self.send_message)

        reset_action = QAction("&Restablecer", self)
        reset_action.setIcon(self.style().standardIcon(QStyle.SP_DialogResetButton))
        reset_action.setShortcut("Ctrl+Del")
        reset_action.setStatusTip("Restablecer todos los datos ingresados")
        reset_action.triggered.connect(self.reset_input)

        about_action = QAction("&Acerca de", self)
        about_action.setStatusTip("Mostrar información de la aplicación")
        about_action.triggered.connect(self.show_info)

        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu("&Archivo")
        file_menu.addAction(quit_action)

        action_menu = menu_bar.addMenu("&Acción")
        action_menu.addActions([send_action, reset_action])

        about_menu = menu_bar.addMenu("&Ayuda")
        about_menu.addAction(about_action)

        tool_bar = self.addToolBar("Main")
        tool_bar.setIconSize(QSize(18, 18))
        ##tool_bar.setIconSize(QSize(24, 24))
        tool_bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        tool_bar.addActions([send_action, reset_action])

    def send_message(self):
        #address_to = self.message_form.to_field.toPlainText()
        address_to = self.message_form.to_field.text()
        address_cc = self.message_form.cc_field.text()  ##
        address_bcc = self.message_form.bcc_field.text()  ##
        subject = self.message_form.subject_field.text()
        body_plain = self.message_form.message_field.toPlainText()
        body_html = self.message_form.message_field.toHtml()

        if not address_to:
            dialog = ErrorDialog("Datos faltantes", "Por favor complete el campo \"Para\" con las direcciones de Correo electrónico de los destinatarios.")
            dialog.exec_()
            return
        else:
            address_to = re.sub(r"\s+", ";", address_to)

        if not subject:
            dialog = ErrorDialog("Datos faltantes", "Por favor complete el campo \"Asunto\" con el asunto del correo electrónico.")
            dialog.exec_()
            return

        if not body_plain or not body_html:
            dialog = ErrorDialog("Datos faltantes", "Por favor complete el campo \"Cuerpo del mensaje\" con algún contenido.")
            dialog.exec_()
            return

### **
        # Adjuntar el archivo
        attachment_path = self.attachment_label["text"]
        if attachment_path!='Ningún archivo seleccionado':
            for path in attachment_path.split(", "):
                with open(path, "rb") as attachment_file:
                    attachment = MIMEApplication(attachment_file.read())

                attachment.add_header(
                    "Content-Disposition",
                    "attachment",
                    filename=os.path.basename(path)
                )
                message.attach(attachment)

### **
        outlook_client = OutlookClient()
        outlook_client.send_email(address_to, address_cc, address_bcc, subject, body_plain, body_html)

        clear_text_input()

    def reset_input(self):
        self.message_form.to_field.setText("")
        self.message_form.cc_field.setText("")
        self.message_form.bcc_field.setText("")
        self.message_form.subject_field.setText("")
        self.message_form.message_field.setText("")

    @staticmethod
    def show_info(self):
        dialog = AboutDialog()
        dialog.exec_()

    @staticmethod
    def quit_application():
        sys.exit(0)
Thanks in advance. Excellent night.

Larz60+ write May-17-2024, 03:28 AM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
tags added this time. Please use BBCode tags on future posts.

Attached Files

.py   main.py (Size: 224 bytes / Downloads: 0)
.py   outlook.py (Size: 531 bytes / Downloads: 0)
.py   ui.py (Size: 12.86 KB / Downloads: 0)
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020