Python Forum
Tkinter email client - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: General (https://python-forum.io/forum-1.html)
+--- Forum: News and Discussions (https://python-forum.io/forum-31.html)
+--- Thread: Tkinter email client (/thread-28199.html)



Tkinter email client - menator01 - Jul-09-2020

Just wanted to share the current learning project.
Although far from being finished, I the program will get and display email subjects.
Refreshes every 5 minutes.
I welcome all feedback and thoughts.

#! /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 = ''

# 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:

    # Grab the inbox. This could be any box or a list of boxes
    def inbox(self, mail):
        status, self.messages = mail.select('inbox')

    # Grab the ids of the emails and return as a list
    def get_ids(self, mail):
        type, data = mail.search(None, 'ALL')
        mail_ids = data[0]
        id_list = mail_ids.split()
        return id_list

    # Grab the subject headers and ids. Combine for getting the mail body/content
    def get_headers(self, mail, id_list):

        # Set some range vars for grabbing mail
        first = int(id_list[0])
        last = int(id_list[-1])

        # Create some empty list
        newids = []
        mylist = []
        headers = []

        # Covert id to int
        for id in id_list:
            newids.append(int(id))

        # Fetch mails and append to a list
        for i in range(last, 0, -1):
            typ, data = mail.fetch(str(i), '(RFC822)')
            mylist.append(data)

        # Loop through the list and get subject headers
        for header in mylist:
            for response in header:
                if isinstance(response, tuple):
                    msg = email.message_from_bytes(response[1])
                    headers.append(msg['subject'])

        # Combine subjects and their ids and return
        new_headers = dict(zip(headers, newids))
        return new_headers

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')

        # 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='new', pady=2, ipady=2)

        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['highlightthickness'] = 1
        self.message_frame['highlightbackground'] = 'grey65'
        self.message_frame.grid(column=1, row=1, sticky='new', pady=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.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.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.update()

    def update(self):
        try:
            self.subjectframe.destroy()
            # 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='new', pady=2, ipady=2)

            i = 0
            mail = Connect()
            mail = mail.connect(user, passwd, server)
            inbox = GetInbox()
            messages = inbox.inbox(mail)
            ids = inbox.get_ids(mail)
            headers = inbox.get_headers(mail, ids)
            for subject, id in headers.items():
                self.subs = tk.Label(self.subjectframe, padx=5, cursor='hand2', anchor='n')
                self.subs['text'] = f'{subject} -> {id}'
                self.subs['font'] = 'sans 10 normal'
                self.subs.grid(column=0, row=i, sticky='nw')
                self.subs.bind('<Button-1>', partial(self.get_content, mail, id))
                i += 1
            mail.close()
            mail.logout()
            self.subjectframe.after(10000, self.update)
        except Exception as error:
            print(error)

    # Get the mail body/content
    def get_content(self, mail, id, event):

        self.message_frame.destroy()
        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['highlightthickness'] = 1
        self.message_frame['highlightbackground'] = 'grey65'
        self.message_frame.grid(column=1, row=1, sticky='new', pady=2)

        mail = Connect()
        mail = mail.connect(user, passwd, server)
        inbox = GetInbox()
        messages = inbox.inbox(mail)

        type, data = mail.fetch(str(id), '(RFC822)')
        print(data)
        for message in data:
            if isinstance(message, tuple):
                msg = email.message_from_bytes(message[1])
                if msg.is_multipart():
                    for line in msg.walk():
                        content_type = line.get_content_type()
                        body = line.get_payload()

                        if content_type == 'text/plain':
                            body = msg.get_payload()
                else:
                    content_type = msg.get_content_type()
                    if content_type == 'text/plain':
                        body = msg.get_payload()
                    else:
                        body = msg.get_payload()

        self.content = tk.Label(self.message_frame, anchor='w', wraplength=420)
        self.content['text'] = self.cleaner(body).strip()
        self.content.grid(column=0, row=0, sticky='nw')
        mail.close()
        mail.logout()

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

def main():
    root = tk.Tk()
    root.title('Tkinter Email Client')
    root['pady'] = 5
    root['padx'] = 10
    root['borderwidth'] = 2
    root.geometry(f'{int(root.winfo_screenwidth()/2)}x{int(root.winfo_screenheight()/2)}')
    Mail(root)
    root.mainloop()

if __name__ == '__main__':
    main()



RE: Tkinter email client - menator01 - Jul-10-2020

I've ran into a few problems while learning on this project. The one that has got me stuck, After displaying all subjects, and after a few updates, the ids change on which I'm using to fetch the body content. Is there any way to use th message-id to retrieve the body content.
Current using fetch(id, '(RFC822)'). Is there another way of fetching current mail body?
Any help/thoughts is much appreciated.