Python Forum
Tkinter basic email client
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tkinter basic email client
#1
This is my first attempt at an email client. It does not handle all email. It's setup for plain text. Will handle some multipart but I have it setup to remove some html tags. Current setting will poll mail server once a minute. Right clicking on the subject will delete. As all my scripts this was done on a linux system so I do not know how it will look on a windows system.
As always I welcome all comments. I tested on my own personal mail server so, I do not know if it works on servers like gmail, yahoo, etc...

#! /usr/bin/env python3
# Do the imports
import imaplib
import email
import tkinter as tk
from functools import partial
import re

# Set mail server connection vars
user = ''
passwd = ''
server = ''

def cleaner(tags):
    cleanr = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
    cleantext = re.sub(cleanr, '', tags)
    return cleantext

class Mail:
    def __init__(self, parent):
        # Configure the root window
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Get screen width & height
        self.width = self.parent.winfo_screenwidth()
        self.height = self.parent.winfo_screenheight()

        # Setup the mainframe container.
        self.mainframe = tk.Frame(self.parent)
        self.mainframe['width'] = int(self.width/2)
        self.mainframe['height'] = int(self.height/2)
        self.mainframe.grid(column=0, row=0, sticky='new')
        self.mainframe.grid_columnconfigure(0, weight=3)
        self.mainframe.grid_rowconfigure(0, weight=3)

        # Setup header frame to place our widget header
        self.headerframe = tk.Frame(self.mainframe)
        self.headerframe['width'] = int((self.width/2)-1)
        self.headerframe['relief'] = 'ridge'
        self.headerframe['borderwidth'] = 2
        self.headerframe.grid(column=0, row=0, sticky='new')
        self.headerframe.grid_columnconfigure(0, weight=3)

        # Setup the mini header frame. This will hold the labels
        # for the inbox and messages labels
        self.mini_headerframe = tk.Frame(self.mainframe)
        self.mini_headerframe.grid(column=0, row=1, sticky='new', pady=3)
        self.mini_headerframe.grid_columnconfigure(0, weight=1)
        self.mini_headerframe.grid_columnconfigure(1, weight=10)

        # Setup the subject frame
        self.subjectframe = tk.Frame(self.mini_headerframe)
        self.subjectframe['relief'] = 'solid'
        self.subjectframe['borderwidth'] = 1
        self.subjectframe['border'] = 0
        self.subjectframe['highlightthickness'] = 1
        self.subjectframe['highlightbackground'] = 'grey65'
        self.subjectframe.grid(column=0, row=1, sticky='news', pady=2, ipady=2)

        # Message frame
        self.message_frame = tk.Frame(self.mini_headerframe)
        self.message_frame['relief'] = 'solid'
        self.message_frame['borderwidth'] = 1
        self.message_frame['border'] = 0
        self.message_frame['bg'] = 'white'
        self.message_frame['highlightthickness'] = 1
        self.message_frame['highlightbackground'] = 'grey65'
        self.message_frame.grid(column=1, row=1, sticky='news', pady=2, ipady=2)

        #Setup the inbox label
        self.inbox_label = tk.Label(self.mini_headerframe)
        self.inbox_label['text'] = 'Inbox'
        self.inbox_label['font'] = 'sans 10 bold'
        self.inbox_label['relief'] = 'solid'
        self.inbox_label['borderwidth'] = 1
        self.inbox_label['border'] = 0
        self.inbox_label['highlightthickness'] = 1
        self.inbox_label['highlightbackground'] = 'grey65'
        self.inbox_label['width'] = 50
        self.inbox_label.grid(column=0, row=0, sticky='new')

        # Setup the messages label
        self.msg_label = tk.Label(self.mini_headerframe)
        self.msg_label['text'] = 'Messages'
        self.msg_label['font'] = 'sans 10 bold'
        self.msg_label['relief'] = 'solid'
        self.msg_label['borderwidth'] = 1
        self.msg_label['border'] = 0
        self.msg_label['highlightthickness'] = 1
        self.msg_label['highlightbackground'] = 'grey65'
        self.msg_label['width'] = 80
        self.msg_label.grid(column=1, row=0, sticky='new')

        # Setup the header label
        self.header_label = tk.Label(self.headerframe, padx=5, pady=2)
        self.header_label['text'] = 'Tkinter Email Client'
        self.header_label['font'] = 'sans 16 bold'
        self.header_label['fg'] = 'indigo'
        self.header_label.grid(column=0, row=0, sticky='new')
        self.header_label.grid_columnconfigure(0, weight=3)

        self.scrollbar = tk.Scrollbar(self.message_frame)
        self.message = tk.Text(self.message_frame, wrap='word', padx=5, pady=3)
        self.message['width'] = 87
        self.message['height'] = 26
        self.message['state'] = 'disabled'
        self.scrollbar.config(command=self.message.yview)
        self.message.config(yscrollcommand=self.scrollbar.set)
        self.message.insert(tk.END, ' ')
        self.scrollbar.grid(column=0, row=0, sticky='ns')
        self.message.grid(column=1, row=0, sticky='news')


        self.subjects()



    def subjects(self):
        canvas = tk.Canvas(self.subjectframe)
        canvas['height'] = 450
        canvas['bg'] = 'white'
        canvas['width'] = int(self.width/4)
        scrollbar = tk.Scrollbar(self.subjectframe, orient='vertical', command=canvas.yview)
        scrollable_frame = tk.Frame(canvas)
        scrollable_frame.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox('all')))
        canvas.create_window((0, 0), window=scrollable_frame, anchor='nw')
        canvas.configure(yscrollcommand=scrollbar.set)
        scrollbar.grid(column=0, row=0, sticky='ns')
        canvas.grid(column=1, row=0, sticky='news')

        mail = Connect()
        mail = mail.connect(user, passwd, server)

        mailbox = GetInbox()
        uids = mailbox.get_uids(mail)

        headers = GrabHeader()
        header = headers.get_headers(mail, uids)
        i = 0
        for heads in header:
            for head, id in heads.items():
                button = tk.Button(scrollable_frame, cursor='hand2', anchor='w', wraplength=430, justify='left')
                button['text'] = head
                button['relief'] = 'flat'
                button['bg'] = 'white'
                button['fg'] = 'blue'
                button['activeforeground'] = 'red'
                button['command'] = partial(self.messagebox, id=id)
                button['width'] = int(self.width/4)
                button.grid(column=0, row=i, sticky='ew')
                button.bind('<Button-3>', partial(self.delete_message, id))
                i += 1
        mail.close()
        mail.logout()
        self.subjectframe.after(60000, self.subjects)

    def messagebox(self, id):
        mail = Connect()
        mail = mail.connect(user, passwd, server)

        mailbox = GetInbox()
        uids = mailbox.get_uids(mail)
        body = GetBody()
        msg = body.get_body(mail, str(id))

        msg = cleaner(msg)

        self.scrollbar = tk.Scrollbar(self.message_frame)
        self.message = tk.Text(self.message_frame, wrap='word', padx=5, pady=3)
        self.message['width'] = 87
        self.message['height'] = 26
        self.scrollbar.config(command=self.message.yview)
        self.message.config(yscrollcommand=self.scrollbar.set)
        self.message['state'] = 'normal'
        self.message.insert(tk.END,msg)
        self.message['state'] = 'disabled'
        self.scrollbar.grid(column=0, row=0, sticky='ns')
        self.message.grid(column=1, row=0, sticky='news')

        mail.close()
        mail.logout()

    def delete_message(self, id, event):
        mail = Connect()
        mail = mail.connect(user, passwd, server)
        mail.select('inbox')
        mail.uid('STORE', str(id), '+FLAGS', '\\Deleted')
        mail.expunge()
        mail.close()
        mail.logout()
        self.subjectframe.after(0, self.subjects)
        self.message['state'] = 'normal'
        self.message.delete(1.0, tk.END)
        self.message['state'] = 'disabled'


# Create the connection class
class Connect:
    # Connect to the server and return
    def connect(self, user, passwd, server):
        mail = imaplib.IMAP4_SSL(server)
        mail.login(user, passwd)
        return mail


# Class for doin operations on the inbox
class GetInbox:
    def get_uids(self, mail):
        mail.select('inbox')
        result, data = mail.uid('search', None, 'ALL')
        return data

# Class for grabbing message headers and uid
class GrabHeader:
    def get_headers(self, mail, uids):
        subjects = []
        for numbers in range(len(uids[0].split())):
            uid = uids[0].split()[numbers]
            result, data = mail.uid('fetch', uid, '(RFC822)')
            raw_data = data[0][1]
            raw_data_string = raw_data.decode('utf-8')
            message = email.message_from_string(raw_data_string)
            subject = str(email.header.make_header(email.header.decode_header(message['subject'])))
            subjects.append({subject:int(uid)})
        return subjects


# Class for retrieving message body
class GetBody:
    def get_body(self, mail, uid):
        result, data = mail.uid('fetch', uid, '(RFC822)')
        raw_data = data[0][1]
        raw_data_string = raw_data.decode('utf-8')
        message = email.message_from_string(raw_data_string)
        for part in message.walk():
            if part.get_content_type() == 'text/plain':
                body = part.get_payload(decode=True)
                return body.decode()
            else:
                continue


def main():
    root = tk.Tk()
    root.title('Tkinter Email Client')
    root['pady'] = 5
    root['padx'] = 10
    root['borderwidth'] = 2
    root.resizable(False, False)
    Mail(root)
    root.mainloop()

if __name__ == '__main__':
    main()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Messages In This Thread
Tkinter basic email client - by menator01 - Jul-10-2020, 09:26 AM
RE: Tkinter basic email client - by ndc85430 - Jul-12-2020, 04:55 AM
RE: Tkinter basic email client - by menator01 - Jul-12-2020, 11:23 AM
RE: Tkinter basic email client - by ndc85430 - Jul-12-2020, 12:00 PM
RE: Tkinter basic email client - by ndc85430 - Jul-23-2020, 08:16 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Basic Music Player with tkinter menator01 4 4,998 Jul-31-2021, 04:27 AM
Last Post: ndc85430
  S3Zilla... an S3 File Transfer Client developed with Python3/Tkinter rootVIII 0 1,968 Apr-21-2019, 05:59 AM
Last Post: rootVIII

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020