操作系统 | 解释器 | 依赖模块 | 图形开发组件 |
---|---|---|---|
Windows | python>=3.4.4 | pyqt5 | QtDesigner |
为获得最佳用户体验,请使用Windows8,python3.4.4 运行并测试
在Src目录下,运行 python3 pxmail.py.
直接运行Bin目录下已打包好的可执行文件(pxmail.exe)
题目要求的基本功能全部实现:
Src
├── pxmail.py
├── parameter.py
├── mail.py
├── gui.py
├── backend.py
├── syntax_pars.py
└── ui
├── accountdialog.ui
├── composewindow.ui
├── Contact.ui
├── mainwindow.ui
├── receivingDialog.ui
├── searchDialog.ui
├── sendDialog.ui
└── ui.qss
简单说明:
pxmail.py
入口主程序gui.py
UI相关界面程序parameter.py
包含了一些定义的全局变量mail.py
邮件后端接收以及解析backend.py
实现登陆框字体渐变的方法setup.py
软件打包的脚本ui/
界面的实现ui素材文件ui.qss
黑色皮肤的样式文件gui.py
class AccountDialog(QtWidgets.QMainWindow) #登陆界面
class MainWindow(QtWidgets.QMainWindow): #邮件浏览主界面
class Contact(QWidget): #联系人界面
class ComposeWindow(QtWidgets.QMainWindow) #发送邮件界面
class ReceiveDialog(QtWidgets.QDialog) #接收邮件对话框
class SendDialog(QtWidgets.QDialog) #发送邮件对话框
重要接口:
def OnActivated() #排序选项Combobox
def mailDisplay() #邮件按排序显示目录
def save_binary_file() #附件保存为二进制文件
def openFile() #打开附件
def onComposeMail() #打开发送邮件界面
def onRefresh() #刷新邮件列表
def onContactList() #显示联系人列表
def InitSearchEdit() #初始化搜索框
def onReply() #回复邮件
def onForward() #转发邮件
def onDelete() #删除邮件
def onFolderSelected() #显示邮件夹目录
def onMailSelected() #显示邮件内容
def makeFolder() #新建邮件夹
def deleteFolder() #删除邮件夹
def folderMenu() #文件夹菜单
def changeBackground() #切换皮肤
重要接口:
def hideManualSet() #隐藏手动设置
def showManualSet() #展开手动设置
def txtuserEdited() #文本编辑完成自动显示服务器文本
def ontextChanged() #文本框自动补全
def onLogin() #登陆邮箱
def save() #保存密码到本地
def trans() #改变透明度,淡入淡出
重要接口:
def onAttachment() #添加附件
def ontextChanged() #文本框自动补全
def onSend() #发送邮件
def InitRichText() #初始化富文本编辑器
def addPerson() #添加联系人
def fileSave() #保存邮件到草稿箱
def insertImage() #插入图片
def onScreenCut() #截屏
def createAttachmentsMenuItems() #创建附件菜单
重要接口:
def exportCsv() #导出通讯录
def importCsv() #导入通讯录
def SetupCsv() #初始化通讯录
def PersonDisplay() #显示联系人列表
def onPeopleSelected() #显示联系人内容
def onEdit() #编辑联系人
def onCreatperson() #新建联系人
def onDeleteperson() #删除联系人
def onComposeMail() #发送邮件
mail.py
class loadingThread(QtCore.QThread) #登陆线程
class sendingThread(QtCore.QThread) #邮件发送线程
class receiveThread(QtCore.QThread) #邮件接收线程
class MyThread(Thread) #多线程并行接收邮件
class MailCache() #邮件缓存、记录时间戳
def CleanDir(Dir) #清除目录下文件
def guess_charset(msg) #获得字符编码方法
def decode_str(s) #字符编码转换方法
def get_info(msg, indent = 0) #邮件解码,获取内容
class Attachment(object) #自定义附件数据结构
重要接口:
def _is_stale(self, folder) #检测上一次时间戳
def _renew_state(self, folder) #刷新时间戳
def _load_state(self) #读取时间戳
def _commit_state(self) #写入时间戳
backend.py
class Trans(QThread) #淡入淡出提示文字
class In(QThread): #淡入提示文字
1.1登陆服务器
读取用户输入到文本框的用户名密码,调用poplib类并连接到服务器
def onLogin(self):
gl.username=self.txtuser.text().strip()
gl.password=self.txtpassword.text()
if gl.popssl:
pop_backend = poplib.POP3_SSL(gl.pophost,gl.popport) #SSL加密登陆
else:
pop_backend = poplib.POP3(gl.pophost,gl.popport)
pop_backend.user(gl.username)
pop_backend.pass_(gl.password)
resp, gl.mails_number, octets = pop_backend.list()
1.2记住密码机制
初始化登陆的时候从本地配置文件config.ini加载用户名密码,密码做了简单的加密操作,需将密文转换成明文
def Initlogin():
self.config=configparser.ConfigParser()
self.config.read('config.ini')
gl.smtpport = self.config.get('mail', 'smtpport')
gl.popport = self.config.get('mail', 'popport')
secret_user = self.config.get('mail', 'user')
secret_passwd = self.config.get('mail', 'passwd')
for i in range(0,len(secret_user)):
gl.username += chr(ord(secret_user[i]) ^ 7)
for i in range(0,len(secret_passwd)):
gl.password += chr(ord(secret_passwd[i]) ^ 5)
1.3文本框自动补全
设置一个下拉框,读取用户输入的当前用户名,假如没出现@,就自动列出qq、sina等邮箱进行自动补全,方便用户使用
def ontextChanged(self):
completer = QtWidgets.QCompleter() #文本框自动补全
self.txtuser.setCompleter(completer)
self.model=QtCore.QStringListModel()
completer.setModel(self.model)
getstring=self.txtuser.text()
if not "@" in getstring:
self.model.setStringList([getstring+"@qq.com", getstring+"@sina.com",getstring+"@sina.cn",
getstring+ "@163.com",getstring+"@126.com", getstring+"@hust.edu.cn"])
2.1 发送邮件
定义一个MIME对象,并填充该数据结构,数据为用户输入的收件人,正文,附件以及图片等等,然后利用smtplib库连接到SMTP服务器,使用用户名密码登陆该邮箱并将以上MIME邮件发送出去
def onSend(self):
gl.message = MIMEMultipart('related')
gl.message['Subject'] = self.txtsubject.text()
gl.message['from'] = gl.username
gl.message['date']=time.strftime('%a, %d %b %Y %H:%M:%S %z')
gl.message.attach(MIMEText(self.textEdit.document().toHtml(), 'html', 'utf-8'))
for filename in self.fileName:
#构造附件
basename = os.path.basename(filename)
msg_attach = MIMEBase('application', 'octet-stream', filename = basename)
msg_attach.set_payload(open(filename, 'rb').read())
encoders.encode_base64(msg_attach)
msg_attach.add_header('Content-Disposition', 'attachment', filename = basename)
gl.message.attach(msg_attach)
gl.receivers=self.txtreceiver.text().split(';')
smtp_backend = smtplib.SMTP(gl.smtphost, 25)
smtp_backend.login(gl.username,gl.password)
smtp_backend.sendmail(gl.username, gl.receivers, gl.message.as_string())
2.2添加附件
弹出文件对话框,选择相应文件,创建一个按钮对象并显示到前端用于编辑操作
def onAttachment(self,ReplyFile=None):
if ReplyFile:
fileName=ReplyFile
else:
fileName, filetype = QFileDialog.getOpenFileName(self,
"选取文件",
"C:/",
"All Files (*);;Text Files (*.txt)") #设置文件扩展名过滤,注意用双分号间隔
self.fileName.append(fileName)
button = QtWidgets.QPushButton( None)
button.setMenu(self.attachmentContextMenu)
button.setToolTip(fileName)
button.setText(os.path.basename(fileName))
button.attachment = None
button.setMaximumSize(200,40)
button.setFocusPolicy(Qt.NoFocus)
self.layout.addWidget(button)
2.3截屏
调用Qt的API接口,保存当前屏幕到本地,并插入到邮件编辑框
def onScreenCut(self):
format = 'png'
screen = QApplication.primaryScreen()
if screen is not None:
self.originalPixmap = screen.grabWindow(0)
else:
self.originalPixmap = QtGui.QPixmap()
self.originalPixmap.save('utitled.png', format)
filename='utitled.png'
image = QtGui.QImage(filename)
cursor = self.textEdit.textCursor()
cursor.insertImage(image,filename)
2.4富文本编辑器
+ 此处涉及函数过多,只列出相应的方法
# 当前文字格式改变
def onCurrentCharFormatChanged(self, format)
#光标位置改变
def onCursorPositionChanged(self)
#字体改变
def fontChanged(self, font)
#字体加粗
def onTextBold(self)
#斜体显示
def onTextItalic(self)
#下划线
def onTextUnderline(self)
#字体颜色
def onTextColor(self)
#文字字体
def onTextFamily(self,family)
#文字大小
def onTextSize(self,pointSize)
#文字对齐
def onTextAlign(self,button)
#改变字体
def fontChanged(self, font)
#改变颜色
def colorChanged(self, color)
#改变对齐
def alignmentChanged(self, alignment)
#剪贴板变换
def clipboardDataChanged(self)
#选中词汇合并格式
def mergeFormatOnWordOrSelection(self, format)
3.1 POP3接收邮件
利用poplib库连接到POP3服务器,使用用户名密码登陆该邮箱并将指定邮件下载到本地,先利用chardet判断邮件文字编码方式进行解析,然后调用Parser库进行base64解码获取邮件内容
def run(self):
ipop_backend = poplib.POP3(gl.pophost,gl.popport)
ipop_backend.user(gl.username)
ipop_backend.pass_(gl.password)
MailIndex = len(gl.mails_number)-counter
resp, lines, octets = ipop_backend.retr(MailIndex)
msg_byte = b'\r\n'.join(lines)
msg_content=msg_byte.decode(chardet.detect(msg_byte)['encoding'])
msg = Parser().parsestr(msg_content)
mails.append((len(gl.mails_number)-MailIndex+1,msg))
多线程并行调用:
for i in range(len(gl.mails_number)):
my_thread = MyThread()
my_thread.start()
threads.append(my_thread)
for thread in threads: #回收线程
thread.join()
3.2 搜索邮件
根据用户输入的关键词,利用python自带的in方法匹配字符串,并添加到显示列表
def txtsearchEdited(self):
gl.string=self.searchEdit.text().replace(' ','').split('|')[0]
gl.March_ID=[] #匹配符合条件的邮件
for email in gl.emails:
info=get_info(email["message"])
if (gl.string in info["subject"]) or (gl.string in info["content"]) or (gl.string in info["addr"]):
gl.March_ID.append(email)
self.data=info["subject"]+info["content"]+info["addr"]
#匹配得到关键字高亮显示
info["content"]=""+info["content"]+""
3.3 切换皮肤
通过加载相应的界面样式文件来实现当前皮肤颜色的变化,样式文件通过qss语言实现
def changeBackground(self):
if self.background:
with open("ui/white.qss","r") as fh: #加载qss文件
self.setStyleSheet(fh.read())
self.background=False
else :
with open("ui/ui.qss","r") as fh: #加载qss文件
self.setStyleSheet(fh.read())
self.background=True
4.1 导入通讯录
+ 打开相应的csv文件,提取通讯录数据,保存到本地并显示到前端
def importCsv(self):
filePath,filetype = QFileDialog.getOpenFileName(self,"导入通讯录",
'.', "CSV (*.csv)",)
if filePath:
self.contact_table=[]
with open(filePath, 'r') as csvfile:
csv_reader = csv.DictReader(csvfile)
for row in csv_reader:
self.contact_table.append(row)
self.WriteCsv()
self.SetupCsv()
self.PersonDisplay()
4.2导出通讯录
+ 导出本地通讯录数据,为了与foxmail兼容,我们将数据保存为csv文件格式,这样可以使foxmail正确读取到
def exportCsv(self):
filename, filetype = QFileDialog.getSaveFileName(self,"导出通讯录",
'.',"CSV (*.csv)" )
if not filename:
return
qFile = QtCore.QFile(filename)
if not qFile.open(QtCore.QFile.WriteOnly | QtCore.QFile.ReadWrite):
QtWidgets.QMessageBox.warning(self, APPNAME,
"无法创建文件: %s\n%s." % (filename, qFile.errorString()))
return
with open(filename,"w",newline="") as datacsv:
csvwriter = csv.writer(datacsv,dialect = ("excel"))
#csv文件插入一行数据,把下面列表中的每一项放入一个单元格(可以用循环插入多行)
csvwriter.writerow(["姓名","电子邮件地址","性别","生日","手机","QQ",
"家庭住址","公司","部门","职位","公司地址"])
for person in self.contact_table:
csvwriter.writerow([person["姓名"],person["电子邮件地址"],person["性别"],person["生日"],person["手机"],person["QQ"],person["家庭住址"],person["公司"],person["部门"],person["职位"],person["公司地址"]])
4.3 新建联系人
+ 定义一个person数据结构,并添加到contact_table联系人列表里面
def onCreatperson(self):
person={"姓名":'未命名',
"电子邮件地址":'',
"性别":'',
"生日":'',
"手机":'',
"QQ":'',
"家庭住址":'',
"公司":'',
"部门":'',
"职位":'',
"公司地址":''}
self.contact_table.append(person)
self.PersonDisplay()
4.4 删除联系人
+ 将contact_table联系人列表里面删除相应的成员,然后刷新前端显示列表,并将此时的数据成员写到本地配置文件里
def onDeleteperson(self):
self.contact_table.pop(self.index)
self.PersonDisplay()
self.widgetShow.hide()
self.widgetEdit.hide()
self.WriteCsv()
主要在ui/ui.qss文件里面,用qss标记语言实现,文件较庞大,这里只展示部分
QWidget #主界面黑色效果
{
color: #eff0f1;
background-color: #31363b;
selection-background-color:#3daee9;
selection-color: #eff0f1;
background-clip: border;
border-image: none;
border: 0px transparent black;
outline: 0;
}
QComboBox:hover,QPushButton:hoverQLineEdit:hover,QTextEdit:hover,QTreeView:hover #文本框划过会有蓝色边框效果
{
border: 1px solid #3daee9;
color: #eff0f1;
}
onFolderSelected(self, folder)
选择当前显示目录receiveThread()
接收线程MailCache()
创建当前用户的缓存邮件夹,记下当前时间戳mailDisplay(self)
输出结果2016/10/7 本次更新:
1.实现基本的SMTP和POP3协议收发邮件
2.实现了UI界面的整体框架2016/10/23 本次更新:
1.解决了收取邮件时画面卡顿的问题
2.支持qq、sina、163、126、hust邮箱
3.增加了登陆失败提示
4.增加了回车快捷键2016/11/3 本次更新:
1.增加了显示收信人、日期、主题
2.增加了回复、转发功能
3.增加了搜索功能
4.初步尝试对界面做了小幅改动,之后还会修改2016/11/10 本次更新:
1.对邮件协议里的日期做了格式化处理
2.增加了对邮件按日期、收信人、主题排序
3.支持带附件邮件的接收,保存到本地,暂未想好如何显示到前端
4.搜索支持关键词背景高亮
5.实现了邮件内容语法高亮,提高用户体验2016/11/12 本次更新:
1.增加了附件显示,能以系统默认程序打开和另存为2016/12/4 本次更新:
1.支持多用户接受邮件,收件人以分号分隔
2.美化了登陆界面,增加了文字淡入淡出效果,提高了用户体验
3.修复了部分HTML邮件显示异常的BUG
4.添加了富文本编辑器
5.写邮件时可以保存邮件至草稿箱
6.支持html邮件发送
7.修复了QQ邮箱不能发送邮件的问题2016/12/16 本次更新:
1.完成了联系人模块
2.支持联系人导入导出csv文件,并与foxmail兼容2016/12/17 本次更新:
1.修复了删除联系人会导致界面崩溃的BUG2016/12/20 本次更新:
1.增加了记住密码机制
2.解决了频繁刷新界面会崩溃的BUG
3.解决了发送附件文件格式的问题
4.支持发送图片
5.支持接收图片
6.可以在发邮件里面添加联系人
7.发送邮件的对话框美化,加了进度条
8.可以删除邮件,并从服务器上也一并删除2016/12/28 本次更新:
1.多线程并行接收邮件,基本可以达到秒收
2.可以发送多个邮件,并可以编辑管理,删除
3.实现了多个附件的接收
4.回复和转发也支持转发附件了..2016/12/31 本次更新:
1.字体黑转白,由于部分html邮件是黑色字体,增加了可以切换皮肤的功能
2.支持邮件夹管理,添加,删除