欢迎使用CSDN-markdown编辑器

软件课设:邮件管理系统


一 开发环境

操作系统 解释器 依赖模块 图形开发组件
Windows python>=3.4.4 pyqt5 QtDesigner

为获得最佳用户体验,请使用Windows8,python3.4.4 运行并测试

脚本执行方法

在Src目录下,运行 python3 pxmail.py.

可执行文件运行方法

直接运行Bin目录下已打包好的可执行文件(pxmail.exe)


二 基本特点

  1. 采用python3编写,编码风格良好。
  2. 结构清晰,代码耦合度低,可扩展性强,其中:正则引擎,GUI,邮件解析器等,皆为通用组件。
  3. 源代码注释详尽
  4. 实现效率高,核心模块方法都是线性级别。
  5. 跨平台,可在Linux、Windows、OSX运行

三 实现功能

基本功能

题目要求的基本功能全部实现:

  1. 有较良好的图形界面
  2. 支持邮件的收发
  3. 支持邮件的转发、回复
  4. 支持邮件管理:浏览、查找、删除
  5. 支持QQ、sina、163、hust邮箱
  6. 支持html以及文本邮件的接收显示

拓展功能

  1. 支持多附件邮件接收和发送
  2. 支持邮件发信人、主题、内容检索
  3. 按日期、发信人、主题邮件排序,支持升序、降序
  4. 邮件搜索采用正则表达引擎
  5. 实现了邮件内容语法高亮,提高用户体验
  6. 支持发送图片,截屏
  7. 通讯簿导入,导出,与Foxmail兼容
  8. 记住密码功能,并保存本地,用户名会加密
  9. 炫酷黑色皮肤,并可以切换皮肤
  10. 多线程接收邮件,基本可以达到秒收

四 项目架构

(一). 源码目录结构

   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
    GUI主要接口,实现了登陆、浏览、发送等界面
class AccountDialog(QtWidgets.QMainWindow)      #登陆界面
class MainWindow(QtWidgets.QMainWindow):        #邮件浏览主界面  
class Contact(QWidget):                         #联系人界面         
class ComposeWindow(QtWidgets.QMainWindow)      #发送邮件界面
class ReceiveDialog(QtWidgets.QDialog)          #接收邮件对话框
class SendDialog(QtWidgets.QDialog)             #发送邮件对话框

主界面MainWindow

重要接口:
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()                      #切换皮肤

登陆界面AccountDialog

重要接口:
def hideManualSet()                         #隐藏手动设置
def showManualSet()                         #展开手动设置
def txtuserEdited()                     #文本编辑完成自动显示服务器文本
def ontextChanged()                         #文本框自动补全
def onLogin()                               #登陆邮箱
def save()                                  #保存密码到本地
def trans()                                 #改变透明度,淡入淡出

发送界面ComposeWindow

重要接口:
def onAttachment()                          #添加附件
def ontextChanged()                         #文本框自动补全
def onSend()                                #发送邮件
def InitRichText()                          #初始化富文本编辑器
def addPerson()                             #添加联系人
def fileSave()                              #保存邮件到草稿箱
def insertImage()                           #插入图片
def onScreenCut()                           #截屏
def createAttachmentsMenuItems()            #创建附件菜单

联系人界面Contact

重要接口:
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)                #自定义附件数据结构

邮件缓存MailCache

重要接口:
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.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)发送邮件模块

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)接收邮件模块

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;
}

五 程序逻辑

接收流程

Created with Raphaël 2.1.0 登陆邮箱 刚收过邮件? 显示在前端 POP3协议收取邮件 yes no
  1. 先从登陆界面进入主界面
  2. 调用onFolderSelected(self, folder)选择当前显示目录
  3. 启动receiveThread()接收线程
  4. 调用MailCache()创建当前用户的缓存邮件夹,记下当前时间戳
  5. 若当前时间遇上一次时间戳在一个周期内,则直接显示在前端,否则就先采用POP3协议从邮件服务器收取邮件到本地
  6. 调用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.修复了删除联系人会导致界面崩溃的BUG

2016/12/20 本次更新
1.增加了记住密码机制
2.解决了频繁刷新界面会崩溃的BUG
3.解决了发送附件文件格式的问题
4.支持发送图片
5.支持接收图片
6.可以在发邮件里面添加联系人
7.发送邮件的对话框美化,加了进度条
8.可以删除邮件,并从服务器上也一并删除

2016/12/28 本次更新
1.多线程并行接收邮件,基本可以达到秒收
2.可以发送多个邮件,并可以编辑管理,删除
3.实现了多个附件的接收
4.回复和转发也支持转发附件了..

2016/12/31 本次更新
1.字体黑转白,由于部分html邮件是黑色字体,增加了可以切换皮肤的功能
2.支持邮件夹管理,添加,删除

七 运行截图

欢迎使用CSDN-markdown编辑器_第1张图片

欢迎使用CSDN-markdown编辑器_第2张图片

欢迎使用CSDN-markdown编辑器_第3张图片

欢迎使用CSDN-markdown编辑器_第4张图片

欢迎使用CSDN-markdown编辑器_第5张图片

你可能感兴趣的:(软件工程)