你是不是有这么一种需求:在使用邮箱(qq邮箱或163邮箱)时,许多时候都需要我们重新登录(或者隔一段时间),那么如果忘记密码,重新设置将是一件很痛苦的事,加之,我们对邮箱使用很频繁,能不能有种产品能使我们不用登入账户,同时桌面应用程序那样而不用去登入网页版邮箱网站,从而满足我们的发送邮件的基本功能实现呢?答案是有的!
在开源IT时代,我们有很多方法和工具来实现我们的需求,作为极客,我们可以发现SMTP协议背后的一种特殊的发送邮件过程:基于邮箱服务商服务器(IMAP,SMTP,POS3等)来绑定对应程序,从而达到免登录,二次程序开发实现邮件发送功能(简单的理解下就可以了,可以自行百度)。
python是具有强大第三方库的一门超级语言,我们可以选择简单且易上手的第三方库!经过笔者比对发现python的yagmail库是个不错的选择,基本配置简单且不复杂!
另外,我们的需求开发出的是产品级,那么就不能再运行时再打开代码,我们要设计出有UI交互界面的程序,由于python自带tkinter库,我们可以用来GUI开发比较方便简单。我们还可以添加程序窗口图标,添加自己喜爱的图片作为背景!
为了使程序成品能给更多用户使用,同时保证各自邮箱的安全性,我们在配置个人邮箱隐私信息时,使用的是本地手动写入配置文件ini,这样使程序成品有可移植性,并且,可以随时修改自己的邮箱配置信息!
知识储备 | 备注 |
---|---|
面向对象编程:类和对象 | 使用类,能使程序二次开发和维护更好,并且程序代码模块化和结构明了,利于阅读和开发 |
GUI开发tkinter | python自带的tkinterGUI编写,虽然总体UI界面不比主流美观,但是开发简单,且外观逻辑上能满足我们的需求 |
pyinstaller | python常见且流行的打包程序库,需要下载安装 |
yagmail库 | 非常好用简单的第三方库,需要下载安装 |
configparser 库 | 用来管理配置文件的库,需要下载安装 |
PIL库 | 通用名为Pillow 库,需要下载安装 |
datetime库 | 用来获取时间 ,需要下载安装 |
calendar库 | 用来生成日历形式的库 |
tkinter.filedialog 模块 | tk库里的文件选择框模块 |
其他媒体文件 | 用于背景和图标等,看自己选择,可自由决定 |
邮箱网站取得密钥和其他信息 | 如果没有这些配置,邮箱程序将无法运行,务必设置好先 |
邮箱网站配置方法如下:
python-基于yagmail库开发自动邮件发送程序
该MAIL.py文件主要放置MAIL类,储存主要的账户配置!
import yagmail#邮箱发送程序的核心库
import configparser#用来管理配置文件的库
import os
class MAIL:#邮箱MAIL类,储存主要的账户配置
def __init__(self):
# yagmail库基本参数设置
self.config = configparser.ConfigParser()
self.config.read('MailConfig.ini')#读取配置文件,程序打开前需保证该文件存在打开,程序后按相应功能可重新生成初始化配置文件
user = self.config['DEFAULT']['user'] # 对应自己的邮箱账号
password =self.config['DEFAULT']['password'] # 对应自己邮箱申请的服务密码,博客有介绍步骤的链接,不是自己邮箱的登录密码
port = self.config['DEFAULT']['port'] # 邮箱服务器端口
host = self.config['DEFAULT']['host'] # 对应自己邮箱服务的服务器地址
self.mail = yagmail.SMTP(user, password, host, port) # yagmail库初始化实例
#为了保证程序代码的安全性和移植性,在程序同目录下,打开配置文件(手动或程序功能上打开),填写好配置信息即可
def creatconfig(self):#恢复成默认的配置文件的方法
self.config['DEFAULT'] = {
'user': 'user',
'password': 'password',
'port': 'port',
'host': 'hosts'
}
with open('MailConfig.ini', 'w+') as configfile:#向配置文件写入默认配置,相当恢复出厂设置!
self.config.write(configfile)
def openconfig(self):#程序上自动打开配置文件的方法
os.startfile('MailConfig.ini')
该MAILGUI.py文件(是主程序)主要放置MAILGUI类,MAILGUI类主要是GUI编写的类和导入并使用前面的MAIL类的方法。
from tkinter import * #tkinter编写的GUI程序
from PIL import Image,ImageTk #由于我们设置窗口图片背景,所以要引入pil库,对应的通用库名为Pillow 库
from MAIL import MAIL #需要导入MAIL类
import datetime #用来获取时间
import calendar #用来生成日历形式的库
import tkinter.filedialog #tk库里的文件选择框模块
class MAILGUI(MAIL):#继承自MAIL类
def __init__(self):
super().__init__()
# 主窗口设置
self.root = Tk() # 程序主窗体
self.root.title('基于SMTP协议自动发送的邮箱程序') # 设置窗口标题
self.root.geometry('800x600+300+100') # 设置窗口大小和在系统显示下的位置
self.root.iconbitmap('mail.ico') # 设置窗体图标
self.lists=[''] #由于后面以第一个列表元素作为显示,所以初始元素先设置为空字符串
self.filename = '' #初始化附件地址变量
photo1=Image.open('background1.png')#同代码文件目录下的图片文件,用于日历背景
self.background_image1 = ImageTk.PhotoImage(photo1.resize((500, 400)))
# GUI组件
# 收件人提示标签
to_label = Label(self.root, text='收件人:', font=('黑体', 13)).place(x=10, y=20)
# 收件人输入框
self.to_text = Text(self.root, width=26, height=1.55, font=('黑体', 13))
self.to_text.place(x=90, y=20)
# 邮件主题标签
title_label = Label(self.root, text='主题:', font=('黑体', 13)).place(x=10, y=80)
# 邮件主题输入框
self.title_text = Text(self.root, width=26, height=1.55, font=('黑体', 13))
self.title_text.place(x=90, y=80)
# 邮件内容提示标签
contents_label = Label(self.root, text='内容:', font=('黑体', 13)).place(x=10, y=140)
# 邮件内容输入框
self.content_text = Text(self.root, width=75, height=12, font=('黑体', 13))
self.content_text.place(x=90, y=140)
#邮件附件按钮
attachments_button = Button(self.root, text='附件地址', font=('黑体', 13), fg='white', bg='green', command=self.Attachments,width=10, height=2, relief=RAISED)
attachments_button.place(x=30, y=370)
#邮件附件提示标签
self.attachments_label=Label(self.root, text='暂无选择文件', font=('黑体', 9),width=80, height=3, fg='white', bg='green')
self.attachments_label.place(x=180, y=370)
#邮件发送确认按钮
check_button = Button(self.root, text='确认发送', font=('黑体', 13), fg='white', bg='green', command=self.Sendto, width=10,height=2, relief=RAISED).place(x=30, y=450)
#程序状态标签
tip_label = Label(self.root, text='程序状态:', font=('黑体', 13)).place(x=10, y=530)
# 其文本内容对应着实际操作程序的操作提示的标签
self.tipout_label = Label(self.root, text='暂无操作!', font=('黑体', 13), bg='green', fg='white')
self.tipout_label.place(x=120, y=530)
# 用于时间显示标签
self.time_label = Label(self.root, text='', font=('黑体', 16), fg='green')
self.time_label.place(x=340, y=85)
self.menubar = Menu(self.root) # 主菜单
self.menuout = Menu(self.root, tearoff=0) # 弹出菜单,清楚功能实现的汇集
self.root['menu'] = self.menubar # 设置主菜单
# 菜单项加入:
self.menubar.add_command(label="初始化配置文件 ", command=self.creatconfig)#初始化配置文件的菜单选项功能
self.menubar.add_command(label=" 打开配置文件 ", command=self.openconfig)#打开配置文件的菜单选项功能
self.menubar.add_command(label=" 草稿 ", command=self.textbook)
self.menubar.add_command(label=" 日历 ", command=self.show_datetime)
self.root.bind("" , self.pops) # 为右键点击事件,用于触发弹出菜单
self.menuout.add_command(label='清空收件人窗口', command=self.Clear_to)
self.menuout.add_command(label='清空主题窗口', command=self.Clear_title)
self.menuout.add_command(label='清空内容窗口', command=self.Clear_content)
self.menuout.add_command(label='清空附件地址', command=self.Clear_attachments)
self.menuout.add_command(label='清空所有输入窗口', command=self.message)
self.root.mainloop()
# 附件控件控制函数
def Attachments(self):
self.filename = tkinter.filedialog.askopenfilename() # 在弹出框内选择文件,并获得字符串型的文件地址
if self.filename != '': # 用filename是否为空字符串来判断,如果没选择的,就用初始量
self.tipout_label['text'] = '您选择了附件文件!'
self.attachments_label['text'] = '您选择的文件是' + self.filename
else:
self.tipout_label['text'] = '您没有选择任何文件!'
self.attachments_label['text'] = '您还没选择任何文件!'
# 主函数:处理邮件发送
def Sendto(self):
to = self.to_text.get('1.0', 'end') # 获取相关内容
title = self.title_text.get('1.0', 'end')
content = self.content_text.get('1.0', 'end')
if self.filename == '': # 根据filename值来选择发送时是否包含附件
try: # 检查邮件发送是否成功很重要;
self.mail.send(to, title, content)
self.tipout_label['text'] = "发送成功!"
except:
self.message()
elif self.filename != '':
try:
self.mail.send(to, title, content, self.filename)
self.tipout_label['text'] = "发送成功!"
except:
self.message()
def show_datetime(self): # 利用calendar库显示日历的窗口
winnew = Toplevel(self.root) # 顶层窗体
winnew.title('日历窗口')
winnew.geometry('500x400+700+50')
winnew.iconbitmap('mail.ico') # 设置窗口图标,统一使用同一张
self.tipout_label['text'] = '你点开了日历窗口!' # 操作变化提示,基本每个模块被使用时都会含有
date_time = datetime.datetime.today()
year = date_time.year # 获取当前年份
month = date_time.month # 获取当前月份
calendar.setfirstweekday(firstweekday=6) # 设置日历的初始天(第一天)
dates = calendar.month(year, month) # 获得日历的字符串类型
alldate = Label(winnew, text='', bg='green', fg='white', width=1000, height=600, font=('黑体', 20),
image=self.background_image1, compound=CENTER)
alldate.configure(text=dates)
alldate.pack()
def textbook(self):# 草稿操作的窗口
text_win = Toplevel(self.root)
text_win.title('草稿窗口')
text_win.geometry('740x520+500+50')
text_win.iconbitmap('mail.ico')
self.tipout_label['text'] = '你点开了草稿窗口!'
self.draft_paper = Text(text_win, width=100, height=30) # 该实例为草稿箱输入和文本显示框功能
self.draft_paper.insert('1.0', self.lists[0]) # 插入所保存的内容,相当记忆重现,实现了保存功能逻辑
save_button = Button(text_win, text='保存', command=self.saves, relief=RAISED, fg='white', bg='green', font=('黑体', 14),
width=10, height=2).place(x=19, y=430)
clear_button = Button(text_win, text='清空所有内容', command=self.clears, relief=RAISED, fg='white', bg='green',
font=('黑体', 14), width=16, height=2).place(x=150, y=430)
self.draft_paper.place(x=19, y=18)
def saves(self):#保存了草稿箱里的内容的方法
self.lists[0] = self.draft_paper.get(1.0, 'end') # 点击保存按钮,触发获取文本框的内容,可在编写过程任一时刻保存,避免丢失
self.tipout_label['text'] = "你保存了草稿箱里的内容!"
def clears(self):#清空草稿箱里的内容的方法
self.draft_paper.delete('1.0', 'end')
self.tipout_label['text'] = "你清空了草稿箱里的内容!"
# 用于其他模块下错误信息提示的使用,使用时调用即可
def message(self):
self.tipout_label['text'] = "输入有误或已全清空!请重新输入所有内容!"
self.to_text.delete('1.0', 'end') # 文本框控件中第一个字符的位置是 1.0,可以用数字 1.0 或字符串"1.0"来表示
self.title_text.delete('1.0', 'end')
self.content_text.delete('1.0', 'end')
self.filename=''
self.tipout_label['text']='你清空了所有输入内容!包括收件人,主题,内容,附件地址的所有输入!'
self.attachments_label['text']='您还没选择任何文件!'
def pops(self,event): # 右键响应函数,弹出菜单
self.menuout.post(event.x_root, event.y_root) # 这两个变量可让在在窗口任一触发部位弹出
def Clear_to(self): # 清空收件人输入框函数
self.to_text.delete('1.0', 'end')
self.tipout_label['text'] = '你清空了收件人窗口!'
def Clear_title(self): # 清空邮件主题输入框的方法
self.title_text.delete('1.0', 'end')
self.tipout_label['text'] = '你清空了主题窗口!'
def Clear_content(self): # 清空邮件内容输入框的方法
self.content_text.delete('1.0', 'end')
self.tipout_label['text'] = '你清空了内容窗口!'
def Clear_attachments(self):
self.filename=''
self.tipout_label['text'] = '你清除了附件地址!'
self.attachments_label['text'] = '您还没选择任何文件!'
if __name__ == '__main__':
MAILGUI()
pyinstaller打包多个py文件和去除cmd黑框
报错:ModuleNotFoundError: No module named 'PIL’解决方法
python 获取当前年份和月份
Python中tkinter.filedialog
Python-Tkinter图形化界面设计(详细教程 )
用tkinter.pack设计复杂界面布局
python-基于yagmail库开发自动邮件发送程序
(1)找到自己代码文件的目录,复制下来
(2)打开cmd窗口,把复制的目录复制到cmd窗口,并在前加cd,如下例所示:
(3)确认已经下载安装好pyinstaller,输入如下指令:
pyinstaller -F <程序主文件>.py
(4)在生成的文件中,只保留<主程序>.spec文件,其余都删除(当然我们原来的py代码文件也在该目录下,肯定不用删除,保留就行)!如下:
(5)打开后缀为.spec文件,在Analysis中的第一行的列表,再添加我们主程序依赖和要导入的模块MAIL.py文件的字符串形式,如下图:
(6)再重新打开cmd,进入文件目录,输入以下指令:
该程序不用携带小黑框,所以要在命令后加上–noconsole语句
pyinstaller -F <主程序名>.spec --noconsole
(7)提示打包成功后,我们可以删除生成的_pycache_和build文件,只保留dist文件夹,因为这里面放置了生成exe程序,另外程序窗体要用到mail.ico文件,默认打包是不会把它移到dist文件夹的,我们要复制到dist文件下!
(8)修改配置文件,如下:
控件 | 功能描述 | 注意事项 |
---|---|---|
初始化配置文件顶层菜单选项 | 初始化配置文件,相当恢复原厂设置,一般用于要修改配置 | |
打开配置文件顶层菜单选项 | 快速自动打开配置文件 | 如果打开修改配置文件的话,保存配置文件后,需重新打开程序 ,方可正常运行 |
草稿顶层菜单选项 | 打开草稿窗口 | |
日历顶层菜单选项 | 打开日历窗口 | |
收件人输入框 | 输入收件人的信息 | |
邮件主题输入框 | 输入邮件主题 | |
邮件内容输入框 | 输入邮件内容 | |
邮件附件按钮 | 选择邮件附件,若不点击,则默认无附件 | 点击附件选择窗口后,若无选择文件,则默认无附件 |
邮件发送确认按钮 | 输入所要要发送的信息,点击可发送 | 根据系统运行环境和网络环境会,发送时会有不同程度的卡顿,但会正常运行下去的,不必奇怪 |
其他显示标签 | 显示程序操作及相关提示等 | |
草稿保存按钮 | 保存草稿窗口所输入的内容 | 保存范围在不关闭主程序窗口内,都有记忆功能 |
草稿清空按钮 | 清空草稿内容 | |
配置文件 | 若删除,可在程序运行状态下,点击初始化配置文件按钮,即可生成 |
为了更好地学习和分享该部分的内容,以及提供成品程序给大家直接观摩,特意放资源链接在此,供各位读者下载!
邮件发送GUI程序下载
最后,由于开发经验尚浅和程序其中的一些瑕疵,希望大家理解!
对于文本内容有何问题,欢迎批评指正!!!