May-17-2024, 02:59 AM
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:
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.