以下是一个使用 wxPython 4.2 开发的简单邮件客户端程序,支持编写邮件、添加附件和发送邮件功能:
编写 eMailClient_wx.py 如下
import os
import wx
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
class EmailClient(wx.Frame):
def __init__(self):
super().__init__(None, title="邮件客户端", size=(1000, 600))
# 初始化附件列表
self.attachments = []
# 创建主面板
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
# 服务器设置
server_box = wx.StaticBox(panel, label="SMTP服务器设置")
server_sizer = wx.StaticBoxSizer(server_box, wx.VERTICAL)
server_grid = wx.FlexGridSizer(cols=2, vgap=10, hgap=10)
server_grid.Add(wx.StaticText(panel, label="SMTP服务器:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.smtp_server = wx.TextCtrl(panel, value="smtp.qq.com")
server_grid.Add(self.smtp_server, 1, wx.EXPAND)
server_grid.Add(wx.StaticText(panel, label="端口:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.port = wx.TextCtrl(panel, value="465")
server_grid.Add(self.port, 1, wx.EXPAND)
server_grid.Add(wx.StaticText(panel, label="邮箱账号:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.email = wx.TextCtrl(panel)
server_grid.Add(self.email, 1, wx.EXPAND)
server_grid.Add(wx.StaticText(panel, label="密码:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.password = wx.TextCtrl(panel, style=wx.TE_PASSWORD)
server_grid.Add(self.password, 1, wx.EXPAND)
server_sizer.Add(server_grid, 1, wx.EXPAND|wx.ALL, 10)
# 邮件内容
mail_box = wx.StaticBox(panel, label="邮件内容")
mail_sizer = wx.StaticBoxSizer(mail_box, wx.VERTICAL)
mail_grid = wx.FlexGridSizer(cols=2, vgap=10, hgap=10)
mail_grid.AddGrowableCol(1, 1)
mail_grid.Add(wx.StaticText(panel, label="收件人:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.to_email = wx.TextCtrl(panel)
mail_grid.Add(self.to_email, 1, wx.EXPAND)
mail_grid.Add(wx.StaticText(panel, label="主题:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.subject = wx.TextCtrl(panel)
mail_grid.Add(self.subject, 1, wx.EXPAND)
mail_grid.Add(wx.StaticText(panel, label="附件:"), 0, wx.ALIGN_CENTER_VERTICAL)
attachment_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.attachment_list = wx.ListBox(panel, size=(-1, 60))
attachment_sizer.Add(self.attachment_list, 1, wx.EXPAND)
btn_add = wx.Button(panel, label="添加附件")
btn_add.Bind(wx.EVT_BUTTON, self.on_add_attachment)
attachment_sizer.Add(btn_add, 0, wx.LEFT, 5)
btn_remove = wx.Button(panel, label="移除")
btn_remove.Bind(wx.EVT_BUTTON, self.on_remove_attachment)
attachment_sizer.Add(btn_remove, 0, wx.LEFT, 5)
mail_grid.Add(attachment_sizer, 1, wx.EXPAND)
mail_grid.Add(wx.StaticText(panel, label="正文:"), 0, wx.ALIGN_TOP)
self.body = wx.TextCtrl(panel, style=wx.TE_MULTILINE, size=(-1, 200))
mail_grid.Add(self.body, 1, wx.EXPAND)
mail_sizer.Add(mail_grid, 1, wx.EXPAND|wx.ALL, 10)
# 发送按钮
send_btn = wx.Button(panel, label="发送邮件")
send_btn.Bind(wx.EVT_BUTTON, self.on_send)
# 添加所有组件到主布局
vbox.Add(server_sizer, 0, wx.EXPAND|wx.ALL, 10)
vbox.Add(mail_sizer, 1, wx.EXPAND|wx.ALL, 10)
vbox.Add(send_btn, 0, wx.ALIGN_CENTER|wx.ALL, 10)
panel.SetSizer(vbox)
# 状态栏
self.CreateStatusBar()
self.SetStatusText("准备就绪")
self.Show(True)
def on_add_attachment(self, event):
"""添加附件"""
with wx.FileDialog(self, "选择附件", wildcard="所有文件 (*.*)|*.*",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) as dialog:
if dialog.ShowModal() == wx.ID_CANCEL:
return
paths = dialog.GetPaths()
for path in paths:
if path not in self.attachments:
self.attachments.append(path)
self.attachment_list.Append(os.path.basename(path))
def on_remove_attachment(self, event):
"""移除选中的附件"""
selection = self.attachment_list.GetSelection()
if selection != wx.NOT_FOUND:
self.attachments.pop(selection)
self.attachment_list.Delete(selection)
def on_send(self, event):
"""发送邮件"""
# 获取输入值
smtp_server = self.smtp_server.GetValue()
port = self.port.GetValue()
email = self.email.GetValue()
password = self.password.GetValue()
to_email = self.to_email.GetValue()
subject = self.subject.GetValue()
body = self.body.GetValue()
# 验证输入
if not all([smtp_server, port, email, password, to_email]):
wx.MessageBox("请填写所有必填字段", "错误", wx.OK | wx.ICON_ERROR)
return
try:
# 创建邮件
msg = MIMEMultipart()
msg['From'] = email
msg['To'] = to_email
msg['Subject'] = subject
# 添加正文
msg.attach(MIMEText(body, 'plain'))
# 添加附件
for file_path in self.attachments:
with open(file_path, 'rb') as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename={os.path.basename(file_path)}'
)
msg.attach(part)
# 连接SMTP服务器并发送
self.SetStatusText("正在连接服务器...")
with smtplib.SMTP(smtp_server, int(port)) as server:
server.starttls()
self.SetStatusText("登录中...")
server.login(email, password)
self.SetStatusText("发送邮件中...")
server.sendmail(email, to_email.split(','), msg.as_string())
# 清空表单
self.attachments = []
self.attachment_list.Clear()
self.to_email.Clear()
self.subject.Clear()
self.body.Clear()
self.SetStatusText("邮件发送成功!")
wx.MessageBox("邮件已成功发送", "成功", wx.OK | wx.ICON_INFORMATION)
except Exception as e:
self.SetStatusText(f"发送失败: {str(e)}")
wx.MessageBox(f"发送邮件时出错:\n{str(e)}", "错误", wx.OK | wx.ICON_ERROR)
if __name__ == "__main__":
app = wx.App()
EmailClient()
app.MainLoop()
使用说明:
-
SMTP服务器设置:
- 输入您的SMTP服务器地址(如:smtp.gmail.com)
- 端口号(如:587 用于TLS)
- 邮箱账号和密码
-
邮件内容:
- 填写收件人邮箱(多个邮箱用逗号分隔)
- 邮件主题
- 使用"添加附件"按钮添加文件
- 在正文区域编写邮件内容
-
发送邮件:
- 点击"发送邮件"按钮发送
注意事项:
- 此程序需要使用支持SMTP的邮箱服务,并确保已开启SMTP服务
- 对于Gmail等邮箱,可能需要启用"安全性较低的应用的访问权限"
- 附件大小受邮箱服务商限制
- 密码以明文形式存储,请谨慎使用
依赖库安装:
pip install wxPython==4.2.0
此程序实现了基本的邮件发送功能,包括:
- SMTP服务器配置
- 邮件内容编辑
- 多附件支持
- 邮件发送状态反馈
- 基本的错误处理
您可以根据需要进一步扩展功能,如添加HTML格式支持、收件箱查看功能、邮件保存草稿等。
优化调整布局
下面是根据您的要求调整布局后的邮件客户端程序,将SMTP服务器和端口放在一行,邮箱账号和密码放在一行:
编写 eMailClient_wx4.py 如下
import os
import wx
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
class EmailClient(wx.Frame):
def __init__(self):
super().__init__(None, title="邮件客户端", size=(1000, 600))
# 初始化附件列表
self.attachments = []
# 创建主面板
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
# 服务器设置
server_box = wx.StaticBox(panel, label="SMTP服务器设置")
server_sizer = wx.StaticBoxSizer(server_box, wx.VERTICAL)
# 第一行:SMTP服务器和端口
server_row1 = wx.BoxSizer(wx.HORIZONTAL)
server_row1.Add(wx.StaticText(panel, label="SMTP服务器:"), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
self.smtp_server = wx.TextCtrl(panel, value="smtp.qq.com")
server_row1.Add(self.smtp_server, 1, wx.EXPAND|wx.RIGHT, 10)
server_row1.Add(wx.StaticText(panel, label="端口:"), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
self.port = wx.TextCtrl(panel, value="465", size=(60, -1))
server_row1.Add(self.port, 0, wx.EXPAND)
# 第二行:邮箱账号和密码
server_row2 = wx.BoxSizer(wx.HORIZONTAL)
server_row2.Add(wx.StaticText(panel, label="邮箱账号:"), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
self.email = wx.TextCtrl(panel)
server_row2.Add(self.email, 1, wx.EXPAND|wx.RIGHT, 10)
server_row2.Add(wx.StaticText(panel, label="密码:"), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
self.password = wx.TextCtrl(panel, style=wx.TE_PASSWORD)
server_row2.Add(self.password, 1, wx.EXPAND)
# 将两行添加到服务器sizer
server_sizer.Add(server_row1, 0, wx.EXPAND|wx.ALL, 10)
server_sizer.Add(server_row2, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, 10)
# 邮件内容
mail_box = wx.StaticBox(panel, label="邮件内容")
mail_sizer = wx.StaticBoxSizer(mail_box, wx.VERTICAL)
mail_grid = wx.FlexGridSizer(cols=2, vgap=10, hgap=10)
mail_grid.AddGrowableCol(1, 1)
mail_grid.Add(wx.StaticText(panel, label="收件人:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.to_email = wx.TextCtrl(panel)
mail_grid.Add(self.to_email, 1, wx.EXPAND)
mail_grid.Add(wx.StaticText(panel, label="主题:"), 0, wx.ALIGN_CENTER_VERTICAL)
self.subject = wx.TextCtrl(panel)
mail_grid.Add(self.subject, 1, wx.EXPAND)
mail_grid.Add(wx.StaticText(panel, label="附件:"), 0, wx.ALIGN_CENTER_VERTICAL)
attachment_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.attachment_list = wx.ListBox(panel, size=(-1, 60))
attachment_sizer.Add(self.attachment_list, 1, wx.EXPAND)
btn_add = wx.Button(panel, label="添加附件")
btn_add.Bind(wx.EVT_BUTTON, self.on_add_attachment)
attachment_sizer.Add(btn_add, 0, wx.LEFT, 5)
btn_remove = wx.Button(panel, label="移除")
btn_remove.Bind(wx.EVT_BUTTON, self.on_remove_attachment)
attachment_sizer.Add(btn_remove, 0, wx.LEFT, 5)
mail_grid.Add(attachment_sizer, 1, wx.EXPAND)
mail_grid.Add(wx.StaticText(panel, label="正文:"), 0, wx.ALIGN_TOP)
self.body = wx.TextCtrl(panel, style=wx.TE_MULTILINE, size=(-1, 200))
mail_grid.Add(self.body, 1, wx.EXPAND)
mail_sizer.Add(mail_grid, 1, wx.EXPAND|wx.ALL, 10)
# 发送按钮
send_btn = wx.Button(panel, label="发送邮件")
send_btn.Bind(wx.EVT_BUTTON, self.on_send)
# 添加所有组件到主布局
vbox.Add(server_sizer, 0, wx.EXPAND|wx.ALL, 10)
vbox.Add(mail_sizer, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, 10)
vbox.Add(send_btn, 0, wx.ALIGN_CENTER|wx.ALL, 10)
panel.SetSizer(vbox)
# 状态栏
self.CreateStatusBar()
self.SetStatusText("准备就绪")
self.Show(True)
def on_add_attachment(self, event):
"""添加附件"""
with wx.FileDialog(self, "选择附件", wildcard="所有文件 (*.*)|*.*",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) as dialog:
if dialog.ShowModal() == wx.ID_CANCEL:
return
paths = dialog.GetPaths()
for path in paths:
if path not in self.attachments:
self.attachments.append(path)
self.attachment_list.Append(os.path.basename(path))
def on_remove_attachment(self, event):
"""移除选中的附件"""
selection = self.attachment_list.GetSelection()
if selection != wx.NOT_FOUND:
self.attachments.pop(selection)
self.attachment_list.Delete(selection)
def on_send(self, event):
"""发送邮件"""
# 获取输入值
smtp_server = self.smtp_server.GetValue()
port = self.port.GetValue()
email = self.email.GetValue()
password = self.password.GetValue()
to_email = self.to_email.GetValue()
subject = self.subject.GetValue()
body = self.body.GetValue()
# 验证输入
if not all([smtp_server, port, email, password, to_email]):
wx.MessageBox("请填写所有必填字段", "错误", wx.OK | wx.ICON_ERROR)
return
try:
# 创建邮件
msg = MIMEMultipart()
msg['From'] = email
msg['To'] = to_email
msg['Subject'] = subject
# 添加正文
msg.attach(MIMEText(body, 'plain'))
# 添加附件
for file_path in self.attachments:
with open(file_path, 'rb') as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename={os.path.basename(file_path)}'
)
msg.attach(part)
# 连接SMTP服务器并发送
self.SetStatusText("正在连接服务器...")
# 根据端口选择连接方式
if port == "465":
# 使用SSL加密
server = smtplib.SMTP_SSL(smtp_server, int(port))
else:
# 普通连接,可能使用STARTTLS
server = smtplib.SMTP(smtp_server, int(port))
if port == "587":
server.starttls()
self.SetStatusText("登录中...")
server.login(email, password)
self.SetStatusText("发送邮件中...")
server.sendmail(email, to_email.split(','), msg.as_string())
server.quit()
# 清空表单
self.attachments = []
self.attachment_list.Clear()
self.to_email.Clear()
self.subject.Clear()
self.body.Clear()
self.SetStatusText("邮件发送成功!")
wx.MessageBox("邮件已成功发送", "成功", wx.OK | wx.ICON_INFORMATION)
except Exception as e:
self.SetStatusText(f"发送失败: {str(e)}")
wx.MessageBox(f"发送邮件时出错:\n{str(e)}", "错误", wx.OK | wx.ICON_ERROR)
if __name__ == "__main__":
app = wx.App()
EmailClient()
app.MainLoop()
主要改进:
-
布局调整:
- SMTP服务器和端口放在同一行
- 邮箱账号和密码放在同一行
- 使用水平布局器(wx.BoxSizer)替代原来的网格布局器(wx.FlexGridSizer)
- 优化了控件间的间距和对齐方式
-
SMTP连接改进:
- 根据端口自动选择连接方式
- 465端口使用SSL加密连接
- 587端口使用STARTTLS
- 其他端口使用普通连接
-
界面优化:
- 端口输入框宽度调整为60像素
- 控件间添加了适当的间距
- 输入框使用比例分配空间
- 状态栏显示更详细的发送过程信息
使用说明:
-
SMTP服务器设置:
- 第一行:SMTP服务器地址和端口
- 第二行:邮箱账号和密码
- 端口默认设置为465(SSL加密)
-
邮件内容:
- 收件人:可输入多个邮箱,用逗号分隔
- 主题:邮件标题
- 附件:可添加多个附件
- 正文:邮件内容编辑区
-
发送邮件:
- 点击"发送邮件"按钮
- 发送过程会在状态栏显示进度
- 发送成功后会清空邮件内容
注意事项:
- 对于Gmail等邮箱,可能需要开启"低安全性应用访问"
- 465端口通常使用SSL加密,587端口使用STARTTLS
- 附件大小受邮箱服务商限制
- 密码以明文形式存储,请谨慎使用
这个版本的布局更加紧凑合理,SMTP服务器设置部分按照您的要求进行了调整,同时保持了原有的功能完整性。