Python手搓郵件發(fā)送客戶端
1. 簡介
這款郵件發(fā)送客戶端是一款桌面應(yīng)用程序,旨在提供一個簡潔且高效的郵件發(fā)送界面,支持發(fā)送郵件、附件、定時發(fā)送以及個性化郵件正文。該應(yīng)用支持通過SMTP協(xié)議發(fā)送電子郵件,提供豐富的配置選項,并支持附件管理。它還允許用戶設(shè)置郵件簽名,支持拖拽文件添加附件,能夠根據(jù)用戶設(shè)定的時間進行郵件的定時發(fā)送。界面設(shè)計采用了淺藍色主題,用戶體驗友好。
2.主要功能
2.1.郵件發(fā)送功能
- 輸入郵件賬號、授權(quán)碼、SMTP服務(wù)器、端口等信息。
- 填寫郵件收件人、抄送人、郵件標題和正文。
- 支持富文本格式郵件正文(支持HTML和圖片)。
- 可以添加附件,并在郵件中附加多個文件。
- 用戶點擊發(fā)送郵件按鈕時,send_email 函數(shù)會讀取用戶輸入的郵件信息(賬號、密碼、SMTP信息等),創(chuàng)建一個 MIME
- 格式的郵件對象。郵件正文以 HTML 格式發(fā)送,并添加附件(如果有)。
- 通過 smtplib.SMTP_SSL 連接到SMTP服務(wù)器,并使用提供的郵件賬號和授權(quán)碼登錄,最后將郵件發(fā)送出去。
2.2.個性簽名功能
允許用戶設(shè)置郵件的個性簽名,支持HTML和圖片:通過 signature_input 支持富文本,用戶可以在其中插入HTML標簽(包括 標簽)。在發(fā)送郵件時,如果個性簽名包含圖片,這些圖片會被作為附件嵌入到郵件中。
2.3.定時發(fā)送功能
用戶可以設(shè)置定時發(fā)送郵件的時間。schedule_email_send
函數(shù)會根據(jù)當前時間和設(shè)定的時間差,利用定時器(QTimer)在指定時間調(diào)用 send_email 函數(shù)發(fā)送郵件。
為了支持定時發(fā)送郵件功能,需要在現(xiàn)有代碼中添加一個定時器,允許用戶設(shè)定發(fā)送時間,并在設(shè)定的時間自動發(fā)送郵件。我們可以通過 QTimer 來實現(xiàn)定時功能。
定時發(fā)送部分:
新增了 QDateTimeEdit 輸入框,允許用戶選擇定時發(fā)送的日期和時間。
新增了一個 schedule_email_send() 函數(shù),用來計算當前時間與目標發(fā)送時間的差距,并通過
QTimer.singleShot() 在設(shè)定的時間觸發(fā)郵件發(fā)送。
定時發(fā)送郵件 按鈕將調(diào)用 schedule_email_send() 函數(shù)。
相關(guān)定時器的實現(xiàn):
使用 QTimer.singleShot() 來定時觸發(fā)郵件發(fā)送。該函數(shù)接收時間間隔(以毫秒為單位)和要執(zhí)行的回調(diào)函數(shù)(即send_email())。
2. 4.附件管理
支持通過文件選擇框或者拖拽文件添加附件。
顯示附件名稱和文件大小。
支持清除附件功能。
一鍵多選附件:
附件添加:通過 add_attachment
函數(shù),用戶可以通過文件對話框選擇一個或多個文件,添加到附件列表,并在表格中顯示文件名和文件大小。
附件清除:通過 clear_attachments 函數(shù),用戶可以清空附件列表。
附件表格:attachment_table 用于顯示附件信息,每個附件占一行,顯示文件名和大小。
使用filedialog.askopenfilenames(),允許用戶選擇多個文件。
選擇的每個文件的名稱、路徑和文件大小被添加到附件列表中。
使用tkinterdnd2庫實現(xiàn)拖拽文件到程序中。 當文件被拖拽到窗口時,會顯示附件的名稱和文件大小。
2.5.配置加載功能
應(yīng)用啟動時,load_config 函數(shù)會檢查當前路徑是否存在 config.ini文件,如果存在,將從文件中讀取郵件賬號、授權(quán)碼、SMTP服務(wù)器及端口,并自動填充輸入框,且設(shè)置為只讀。
配置文件格式 (config.ini): config.ini文件應(yīng)包含如下內(nèi)容:
sender_email:郵件賬號。
sender_password:授權(quán)碼。
smtp_server:SMTP服務(wù)器地址。
smtp_port:SMTP服務(wù)器端口號。
[Email] sender_email = your_email@example.com sender_password = your_authorization_code [SMTP] smtp_server = smtp.example.com smtp_port = 465
自動加載當前路徑配置文件(config.ini),填充發(fā)件人的郵件賬號、授權(quán)碼等字段信息,并設(shè)置為只讀。
2.6.主題設(shè)置和UI
使用 QVBoxLayout 和 QHBoxLayout 布局管理器,組織各個控件的排版。
使用 QPalette 設(shè)置淺藍色背景、按鈕背景和輸入框背景等顏色,并定義按鈕文本為白色,使用 Segoe UI 字體。
2.7.錯誤處理與消息框
在遇到錯誤(如SMTP連接失敗、配置文件讀取失敗等)時,使用 QMessageBox 彈出提示框顯示錯誤信息。
2.8 日志保存功能
在當前路徑生成 email_send.log日志文件,保存相關(guān)郵件發(fā)送信息等事件。
3. 運行效果
普通界面:

UI美化界面:

發(fā)送成功界面:

郵箱接收界面:

日志生成頁面:

資源列表頁面:

4.BUG修復(fù)之路
顯示發(fā)送郵件失敗,軟件報錯:getaddrinfo faied
后面檢查發(fā)現(xiàn)是郵件服務(wù)器填寫時候多敲了一個空格導致的報錯,后面在發(fā)送郵件時,在send_email函數(shù)中,使用.strip()方法去除輸入框內(nèi)容的首尾空格和TAB字符。
郵件正文通過body_text.get(“1.0”, tk.END).strip()獲取內(nèi)容時,也會清除多余的空格和TAB字符。
附件發(fā)送比如.xlsx發(fā)出去變成未命名.bin文件
當發(fā)送 .xlsx 等 Office 類型附件時,附件變成 .bin 文件的原因,通常是因為郵件的 MIME類型沒有正確指定,導致郵件客戶端無法識別文件類型并將其處理為二進制流。因此,需要確保在附件添加時指定正確的 MIME 類型和內(nèi)容類型。
對于 .xlsx 文件,我們需要確保 MIME 類型設(shè)置為application/vnd.openxmlformats-officedocument.spreadsheetml.sheet。同樣,其他Office 文件(如 .docx、.pptx)也有各自的 MIME 類型。
修改代碼:正確設(shè)置 MIME 類型
我們可以修改附件的 MIME 類型來確保文件以原格式發(fā)送。以下是如何修復(fù)這個問題:
針對 Office 文件設(shè)置正確的 MIME 類型
根據(jù)附件的擴展名自動設(shè)置 MIME 類型
主要修改:
使用 mimetypes 識別文件類型:通過 mimetypes.guess_type() 函數(shù)識別文件的 MIME
類型。如果文件類型是已知的(例如 .xlsx),就會設(shè)置正確的 MIME 類型。
create_attachment 函數(shù):這個函數(shù)根據(jù)文件類型創(chuàng)建附件并設(shè)置適當?shù)?MIME 類型。如果是 .xlsx 文件,則設(shè)置application/vnd.openxmlformats-officedocument.spreadsheetml.sheet MIME 類型。
5.部分相關(guān)源碼分享
# 讀取config.ini文件并填充輸入框
def load_config():
config_file = "config.ini"
if os.path.exists(config_file):
config = configparser.ConfigParser()
config.read(config_file)
try:
sender_email = config.get("Email", "sender_email")
sender_password = config.get("Email", "sender_password")
smtp_server = config.get("SMTP", "smtp_server")
smtp_port = config.get("SMTP", "smtp_port")
# 填充輸入框并設(shè)置為只讀
sender_email_input.setText(sender_email)
sender_password_input.setText(sender_password)
smtp_server_input.setText(smtp_server)
smtp_port_input.setText(smtp_port)
# 設(shè)置輸入框為只讀
sender_email_input.setReadOnly(True)
sender_password_input.setReadOnly(True)
smtp_server_input.setReadOnly(True)
smtp_port_input.setReadOnly(True)
except Exception as e:
QMessageBox.critical(window, "錯誤", f"讀取配置文件失敗: {e}")
else:
# 如果沒有config.ini文件,不做任何操作
pass
# 發(fā)送郵件的函數(shù)
def send_email():
sender_email = sender_email_input.text().strip()
sender_password = sender_password_input.text().strip()
smtp_server = smtp_server_input.text().strip()
smtp_port = smtp_port_input.text().strip()
recipient_email = recipient_input.text().strip()
cc_email = cc_input.text().strip().replace(";", ",") # 替換中文分隔符
subject = subject_input.text().strip()
body = body_input.toPlainText().strip()
signature = signature_input.toHtml().strip() # 獲取個性簽名HTML內(nèi)容
# 將個性簽名添加到郵件正文
if signature:
body += f"<br><br>{signature}"
# 檢查SMTP端口是否有效
if not smtp_port.isdigit():
QMessageBox.critical(window, "錯誤", "請輸入有效的SMTP端口號!")
return
# 創(chuàng)建郵件
msg = MIMEMultipart("related") # 允許郵件中包含HTML內(nèi)容和附件
msg['From'] = sender_email
msg['To'] = recipient_email
if cc_email:
msg['Cc'] = cc_email # 抄送多個郵箱
msg['Subject'] = subject
# 郵件正文
msg_html = MIMEText(body, 'html', 'utf-8') # 使用HTML格式發(fā)送郵件正文
msg.attach(msg_html)
# 添加附件
for file in attachments:
file_path = file['path']
file_name = file['name']
mime_type, _ = mimetypes.guess_type(file_path) # 根據(jù)文件擴展名自動猜測 MIME 類型
if mime_type is None:
mime_type = 'application/octet-stream' # 默認 MIME 類型
main_type, sub_type = mime_type.split('/')
part = MIMEBase(main_type, sub_type)
with open(file_path, 'rb') as f:
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename="{file_name}"')
msg.attach(part)
try:
# 設(shè)置SMTP服務(wù)器
with smtplib.SMTP_SSL(smtp_server, int(smtp_port)) as server:
server.login(sender_email, sender_password)
# 發(fā)送郵件
server.sendmail(sender_email, [recipient_email] + (cc_email.split(',') if cc_email else []), msg.as_string())
QMessageBox.information(window, "成功", "郵件發(fā)送成功!")
#clear_fields() # 發(fā)送郵件后清除輸入框和附件
except Exception as e:
QMessageBox.critical(window, "錯誤", f"發(fā)送郵件失敗: {e}")
# 添加附件的函數(shù)
def add_attachment():
file_paths, _ = QFileDialog.getOpenFileNames(window, "選擇附件", "", "All Files (*);;Text Files (*.txt);;Image Files (*.png *.jpg *.bmp);;Excel Files (*.xlsx);;PDF Files (*.pdf)")
if file_paths:
for file_path in file_paths:
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path)
attachments.append({"name": file_name, "path": file_path, "size": file_size})
# 將附件信息添加到表格中
row_position = attachment_table.rowCount()
attachment_table.insertRow(row_position)
attachment_table.setItem(row_position, 0, QTableWidgetItem(file_name))
attachment_table.setItem(row_position, 1, QTableWidgetItem(f"{file_size // 1024} KB"))
# 清除附件的函數(shù)
def clear_attachments():
attachments.clear()
attachment_table.setRowCount(0)
# 清除所有輸入框的內(nèi)容和附件
def clear_fields():
sender_email_input.clear()
sender_password_input.clear()
smtp_server_input.clear()
smtp_port_input.clear()
recipient_input.clear()
cc_input.clear()
subject_input.clear()
body_input.clear()
signature_input.clear() # 清除個性簽名輸入框內(nèi)容
clear_attachments() # 清除附件列表
# 設(shè)置定時發(fā)送的函數(shù)
def schedule_email_send():
send_time = send_time_input.dateTime()
current_time = QDateTime.currentDateTime()
if send_time <= current_time:
QMessageBox.critical(window, "錯誤", "請選擇未來的時間!")
return
time_diff = current_time.msecsTo(send_time) # 計算定時發(fā)送的時間差
timer.singleShot(time_diff, send_email) # 定時調(diào)用發(fā)送郵件函數(shù)
QMessageBox.information(window, "成功", f"郵件將在 {send_time.toString()} 發(fā)送。")
# 創(chuàng)建主窗口
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("郵件發(fā)送客戶端")
window.setWindowIcon(QIcon("icon.png")) # 可以設(shè)置一個圖標
window.setGeometry(100, 100, 400, 700) # 增加窗口高度,以適應(yīng)新添加的個性簽名文本框
# 創(chuàng)建淺藍色主題
def set_light_blue_theme():
palette = QPalette()
palette.setColor(QPalette.Background, QColor(173, 216, 230)) # 淺藍色背景
palette.setColor(QPalette.WindowText, QColor(0, 0, 0)) # 黑色文本
palette.setColor(QPalette.Button, QColor(73, 170, 160)) # 按鈕背景色為 #49AAA0
palette.setColor(QPalette.ButtonText, QColor(255, 255, 255)) # 按鈕文本為白色
palette.setColor(QPalette.Base, QColor(240, 248, 255)) # 淺藍色輸入框背景
palette.setColor(QPalette.Text, QColor(0, 0, 0)) # 黑色文本
window.setPalette(palette)
set_light_blue_theme()
# 設(shè)置字體為 Segoe UI
font = QFont("Segoe UI", 10)
window.setFont(font)
6.總結(jié)
總之這個郵件客戶端不僅提供基礎(chǔ)的郵件發(fā)送和附件管理功能,還集成了定時發(fā)送、個性簽名等高級功能,兼具簡潔的用戶界面和較強的功能擴展性。界面采用簡潔明了的淺藍色主題,結(jié)合了現(xiàn)代UI設(shè)計理念和豐富的郵件配置選項,適合用戶進行郵件發(fā)送和管理
以上就是Python手搓郵件發(fā)送客戶端的詳細內(nèi)容,更多關(guān)于Python郵件發(fā)送的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python最大連續(xù)區(qū)間和動態(tài)規(guī)劃
這篇文章主要介紹了Python最大連續(xù)區(qū)間和動態(tài)規(guī)劃,文章圍繞Python最大連續(xù)區(qū)間和動態(tài)規(guī)劃的相關(guān)資料展開內(nèi)容,需要的小伙伴可以參考一下2022-01-01
詳解Python實現(xiàn)多進程異步事件驅(qū)動引擎
本篇文章主要介紹了詳解Python實現(xiàn)多進程異步事件驅(qū)動引擎,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Python?datacompy?找出兩個DataFrames不同的地方
本文主要介紹了Python?datacompy?找出兩個DataFrames不同的地方,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧<BR>2022-05-05
使用Python對Syslog信息進行分析并繪圖的實現(xiàn)
這篇文章主要介紹了使用Python對Syslog信息進行分析并繪圖的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04

