import re import threading from tkinter import * from tkinter import messagebox from tkinter import ttk from . import VERSION from .config import get_config from .logic import Account def slugify(text: str): return re.sub(r'\W', '-', text) class CtecFrame: def __init__(self, root: Tk): self.root = root root.title('ctec') mainframe = ttk.Frame(root, padding="3 3 12 12") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) mainframe.columnconfigure(0, weight=1) mainframe.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) config = get_config() self.accounts = [Account(address=section, info=config[section]) for section in config.sections() if '@' in section] self.accounts_tree = ttk.Treeview(mainframe, columns=('unread', 'all')) self.accounts_tree.column('unread', width=50, anchor=E) self.accounts_tree.heading('unread', text='Unread') self.accounts_tree.column('all', width=50, anchor=E) self.accounts_tree.heading('all', text='All') self.accounts_tree.grid(column=0, row=0, sticky=(N, W, E, S)) self.accounts_tree.insert('', 'end', 'all', text='All Accounts') for account in self.accounts: self.accounts_tree.insert('', 'end', account.address, text=account.address, open=True) for folder_name, folder in account.folders(): unread_count = sum(1 for message in folder if 'S' not in message.get_flags()) all_count = sum(1 for message in folder) tree_item = f'{account.address}-{slugify(folder_name)}' parent_tree_item = account.address if '.' in folder_name: parent_folder_name, child_folder_name = folder_name.rsplit('.', 1) parent_tree_item = f'{account.address}-{slugify(parent_folder_name)}' folder_name = child_folder_name self.accounts_tree.insert(parent_tree_item, 'end', tree_item, text=folder_name, values=(unread_count, all_count), open=True) # this argument is here to work around an obnoxious python misfeature # local variables aren't closed over unless the function is returned?? # so setting a default argument value lets us close over the value instead of just using the most recent value in that name def fetch_account_folders(account=account): account.fetch_folders() # TODO do this separately maybe? for folder_name, _ in account.folders(): account.fetch_folder(folder_name) self.update_accounts_tree(updated_account=account) threading.Thread(target=fetch_account_folders).start() # self.messages = # create a menu bar self.make_menu_bar(root) def update_accounts_tree(self, updated_account=None): selected_accounts = [updated_account] if updated_account is None: selected_accounts = self.accounts for account in selected_accounts: for folder_name, folder in account.folders(): unread_count = sum(1 for message in folder if 'S' not in message.get_flags()) all_count = sum(1 for message in folder) tree_item = f'{account.address}-{slugify(folder_name)}' if self.accounts_tree.exists(tree_item): self.accounts_tree.item(tree_item, values=(unread_count, all_count)) else: parent_tree_item = account.address if '.' in folder_name: parent_folder_name, child_folder_name = folder_name.rsplit('.', 1) parent_tree_item = f'{account.address}-{slugify(parent_folder_name)}' folder_name = child_folder_name self.accounts_tree.insert(parent_tree_item, 'end', tree_item, text=folder_name, values=(unread_count, all_count), open=True) def make_menu_bar(self, root): root.option_add('*tearOff', FALSE) menubar = Menu(root) root['menu'] = menubar file_menu = Menu(menubar) menubar.add_cascade(menu=file_menu, label='File', underline=0) file_menu.add_command(label='Exit', command=self.on_exit, underline=1) help_menu = Menu(menubar) menubar.add_cascade(menu=help_menu, label='Help', underline=0) help_menu.add_command(label='About', command=self.on_about, underline=0) def on_exit(self, *args): self.root.destroy() def on_about(self, *args): messagebox.showinfo(message=f"ctec {'.'.join(str(x) for x in VERSION)}") def main(): root = Tk() frame = CtecFrame(root) root.mainloop() main()