瀏覽代碼

feat: 添加GUI程序

SongZihuan 2 年之前
父節點
當前提交
246bf20a64
共有 1 個文件被更改,包括 535 次插入0 次删除
  1. 535 0
      sender-tk.py

+ 535 - 0
sender-tk.py

@@ -0,0 +1,535 @@
+import tkinter as tk
+import tkinter.messagebox as msg
+import tkinter.filedialog as fd
+import tkinter.scrolledtext as st
+from typing import Optional
+import smtplib
+import json
+import os.path
+
+from sender.email import Email
+from sender.smtp import Sender
+
+# 邮件输入框
+TEXT_WIDTH = 40
+TEXT_HEIGHT = 20
+
+# 顶层窗口按钮
+BUTTON_WIDTH = 40
+BUTTON_WIDTH_L = 20
+BUTTON_HEIGHT = 1
+
+# 邮件信息显示框
+EMAIL_INFO_BUTTON_WIDTH = 20
+EMAIL_INFO_BUTTON_HEIGHT = 1
+EMAIL_INFO_WIDTH = 100
+EMAIL_INFO_HEIGHT = 40
+
+# 登录框
+LOGIN_LABEL_WIDTH = 10
+LOGIN_LABEL_HEIGHT = 1
+LOGIN_ENTRY_WIDTH = 35
+LOGIN_BTN_WIDTH = 20
+LOGIN_RBTN_WIDTH = 6
+
+# 邮件框
+CREATE_LABEL_WIDTH = 15
+CREATE_LABEL_HEIGHT = 1
+CREATE_ENTRY_WIDTH = 35
+
+# 收件人添加
+ADD_RC_LABEL_WIDTH = 10
+ADD_RC_LABEL_HEIGHT = 1
+ADD_RC_ENTRY_WIDTH = 25
+ADD_RC_BUTTON_WIDTH = 10
+ADD_RC_BUTTON_HEIGHT = 1
+ADD_RC_LISTBOX_WIDTH = 35
+ADD_RC_LISTBOX_HEIGHT = 10
+
+
+class SenderGUI:
+    def __init__(self):
+        self.win = tk.Tk()
+        self.sender: Optional[Sender] = None
+        self.email: Optional[Email] = None
+
+        self.win.iconbitmap('HuanMail.ico')
+        self.win.title("HuanMail (SMTP)")
+        self.win.resizable(False, False)
+
+        self.login_frame = tk.Frame(self.win)
+        self.email_create_frame = tk.Frame(self.win)
+        self.add_to_frame = tk.Frame(self.win)
+        self.text_frame = tk.Frame(self.win)
+        self.file_frame = tk.Frame(self.win)
+        self.config_frame = tk.Frame(self.win)
+        self.control_frame = tk.Frame(self.win)
+
+        for i in (self.login_frame, self.email_create_frame, self.add_to_frame, self.text_frame,
+                  self.file_frame, self.config_frame, self.control_frame):
+            i.pack(side="top", padx=5)
+
+        button_arg = dict(width=BUTTON_WIDTH_L, height=BUTTON_HEIGHT, border=2)
+        button_big_arg = dict(width=BUTTON_WIDTH, height=BUTTON_HEIGHT, border=2)
+
+        self.login = tk.Button(self.login_frame, text="Login", command=self.show_login, **button_arg)
+        self.user_show = tk.Button(self.login_frame, text="Show User", command=self.show_user, **button_arg)
+
+        self.create = tk.Button(self.email_create_frame, text="Create", command=self.show_create, **button_arg)
+        self.email_show = tk.Button(self.email_create_frame, text="Show Email", command=self.show_email, **button_arg)
+
+        self.add_to = tk.Button(self.add_to_frame, text="Add Recipient", command=self.show_add_rc, **button_big_arg)
+
+        self.text = st.ScrolledText(self.text_frame, width=TEXT_WIDTH, height=TEXT_HEIGHT)
+        self.text_control_frame = tk.Frame(self.text_frame)
+        self.add_as_html = tk.Button(self.text_control_frame, text="Add as HTML",
+                                     command=self.add_as_html, **button_arg)
+        self.add_as_text = tk.Button(self.text_control_frame, text="Add as TEXT",
+                                     command=self.add_as_text, **button_arg)
+        self.text.pack(side="top")
+        self.text_control_frame.pack(side="top")
+
+        self.load = tk.Button(self.config_frame, text="Load", command=self.load_config, **button_arg)
+        self.save = tk.Button(self.config_frame, text="Save", command=self.save_config, **button_arg)
+
+        self.send = tk.Button(self.control_frame, text="Send", command=self.send, **button_arg)
+        self.quit = tk.Button(self.control_frame, text="Quit", command=self.quit, **button_arg)
+
+        for i in (self.login, self.user_show, self.create, self.email_show, self.add_as_html,
+                  self.add_as_text, self.load, self.save, self.send, self.quit):
+            i.pack(side="left", pady=2.5, ipadx=2)
+        self.add_to.pack(side="left", pady=2.5, ipadx=8)
+
+        self.add_html = tk.Button(self.file_frame, text="Add HTML From File", command=self.add_html, **button_big_arg)
+        self.add_text = tk.Button(self.file_frame, text="Add TEXT From File", command=self.add_text, **button_big_arg)
+        self.add_file = tk.Button(self.file_frame, text="Add File", command=self.add_file, **button_big_arg)
+        for i in (self.add_html, self.add_text, self.add_file):
+            i.pack(side="top", pady=2.5, ipadx=8)
+
+    def show_login(self):
+        win = tk.Toplevel(self.win)
+        win.title("User Login")
+        win.resizable(False, False)
+
+        label_arg = dict(width=LOGIN_LABEL_WIDTH, height=LOGIN_LABEL_HEIGHT)
+        entry_arg = dict(width=LOGIN_ENTRY_WIDTH)
+
+        host_frame = tk.Frame(win)
+        host_label = tk.Label(host_frame, text="Host: ", **label_arg)
+        host = tk.Entry(host_frame, **entry_arg)
+        host_label.pack(side="left")
+        host.pack(side="left")
+        host_frame.pack(side="top", pady=2, padx=5)
+
+        port_frame = tk.Frame(win)
+        port_label = tk.Label(port_frame, text="Port: ", **label_arg)
+        port = tk.Entry(port_frame, **entry_arg)
+        port_label.pack(side="left")
+        port.pack(side="left")
+        port_frame.pack(side="top", pady=2, padx=5)
+
+        user_frame = tk.Frame(win)
+        user_label = tk.Label(user_frame, text="User: ", **label_arg)
+        user = tk.Entry(user_frame, **entry_arg)
+        user_label.pack(side="left")
+        user.pack(side="left")
+        user_frame.pack(side="top", pady=2, padx=5)
+
+        passwd_frame = tk.Frame(win)
+        passwd_label = tk.Label(passwd_frame, text="Passwd: ", **label_arg)
+        passwd = tk.Entry(passwd_frame, **entry_arg)
+        passwd_label.pack(side="left")
+        passwd.pack(side="left")
+        passwd_frame.pack(side="top", pady=2, padx=5)
+
+        ssl_frame = tk.Frame(win)
+        ssl_label = tk.Label(ssl_frame, text="SSL: ", **label_arg)
+        ssl = tk.IntVar(ssl_frame)
+        ssl.set(1)
+        ssl_btn = (tk.Radiobutton(ssl_frame, variable=ssl, value=1, text="No", width=LOGIN_RBTN_WIDTH),
+                   tk.Radiobutton(ssl_frame, variable=ssl, value=2, text="SSL", width=LOGIN_RBTN_WIDTH),
+                   tk.Radiobutton(ssl_frame, variable=ssl, value=3, text="StartSSL", width=LOGIN_RBTN_WIDTH))
+        ssl_label.pack(side="left")
+        for i in ssl_btn:
+            i.pack(side="left")
+        ssl_frame.pack(side="top", pady=2, padx=5)
+
+        login_btn = tk.Button(win, text="Login", width=LOGIN_BTN_WIDTH,
+                              command=lambda: self.login_user(host.get(), port.get(), user.get(), passwd.get(),
+                                                              ssl.get(), win=win))
+        login_btn.pack(side="top", pady=2)
+
+    def login_user(self, host, port, user, passwd, ssl: int, win=None):
+        try:
+            port = int(port)
+        except (TypeError, ValueError):
+            msg.showerror("Login error", "Port must be int.")
+            return
+
+        if ssl == 0:
+            ssl = False
+            start_ssl = False
+        elif ssl == 1:
+            ssl = True
+            start_ssl = False
+        else:
+            ssl = False
+            start_ssl = True
+
+        try:
+            self.sender = Sender(user, passwd, host=host, port=port, debug=False, ssl=ssl, start_ssl=start_ssl)
+        except smtplib.SMTPAuthenticationError:
+            msg.showinfo("Login error", "Sorry, SMTP Authentication error. Please check your user and password.")
+        except smtplib.SMTPException:
+            msg.showinfo("Login error", "Sorry, SMTP error.")
+        except Exception:
+            msg.showinfo("Login error", "Sorry, Unknown error.")
+        else:
+            if win:
+                win.destroy()
+            msg.showinfo("Login", "Success!")
+
+    def show_user(self):
+        if self.sender:
+            msg.showinfo("User",
+                         f"""Host: {self.sender.host}:{self.sender.port}
+User: {self.sender.user}
+Passwd: {self.sender.passwd}""")
+        else:
+            msg.showwarning("Not login", "You should login first.")
+
+    def show_create(self):
+        win = tk.Toplevel(self.win)
+        win.title("Email Create")
+        win.resizable(False, False)
+
+        label_arg = dict(width=CREATE_LABEL_WIDTH, height=CREATE_LABEL_HEIGHT)
+        entry_arg = dict(width=CREATE_ENTRY_WIDTH)
+
+        from_name_frame = tk.Frame(win)
+        from_name_label = tk.Label(from_name_frame, text="From Name: ", **label_arg)
+        from_name = tk.Entry(from_name_frame, **entry_arg)
+        from_name_label.pack(side="left")
+        from_name.pack(side="left")
+        from_name_frame.pack(side="top", pady=2, padx=5)
+
+        from_address_frame = tk.Frame(win)
+        from_address_label = tk.Label(from_address_frame, text="From Address: ", **label_arg)
+        from_address = tk.Entry(from_address_frame, **entry_arg)
+        from_address_label.pack(side="left")
+        from_address.pack(side="left")
+        from_address_frame.pack(side="top", pady=2, padx=5)
+
+        title_frame = tk.Frame(win)
+        title_label = tk.Label(title_frame, text="Title: ", **label_arg)
+        title = tk.Entry(title_frame, **entry_arg)
+        title_label.pack(side="left")
+        title.pack(side="left")
+        title_frame.pack(side="top", pady=2, padx=5)
+
+        create_btn = tk.Button(win, text="Create", width=LOGIN_BTN_WIDTH,
+                               command=lambda: self.create_email(from_name.get(), from_address.get(),
+                                                                 title.get(), win=win))
+        create_btn.pack(side="top", pady=2)
+
+    def create_email(self, from_name, from_address, title, win=None):
+        self.email = Email((from_name, from_address), title)
+        if win:
+            win.destroy()
+        msg.showinfo("Create", "Success!")
+
+    def show_email(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+
+        win = tk.Toplevel(self.win)
+        win.title("Email info")
+        win.resizable(False, False)
+
+        arg = dict(width=EMAIL_INFO_WIDTH)
+        from_addr = tk.Label(win, text=f"From: {self.email.from_addr[0]} <{self.email.from_addr[1]}>", **arg)
+        title = tk.Label(win, text=f"Title: {self.email.subject}", **arg)
+        info = st.ScrolledText(win, height=EMAIL_INFO_HEIGHT, state=tk.DISABLED, **arg)
+        btn_frame = tk.Frame(win)
+
+        for i in (from_addr, title, info, btn_frame):
+            i.pack(side="top", pady=2.5, ipadx=8, padx=2)
+
+        button_arg = dict(width=EMAIL_INFO_BUTTON_WIDTH, height=EMAIL_INFO_BUTTON_HEIGHT, border=2)
+        text = tk.Button(btn_frame, text="TEXT", command=lambda: self.show_email_text(info), **button_arg)
+        html = tk.Button(btn_frame, text="HTML", command=lambda: self.show_email_html(info), **button_arg)
+        file = tk.Button(btn_frame, text="FILE", command=lambda: self.show_email_file(info), **button_arg)
+
+        for i in (text, html, file):
+            i.pack(side="left", ipadx=0.5)
+
+        self.show_email_text(info)
+
+    def show_email_text(self, text: st.ScrolledText):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+
+        text.config(state=tk.NORMAL)
+        text.delete("0.0", tk.END)
+
+        if len(self.email.text) == 0:
+            text.insert(tk.END, "Not any TEXT ...")
+        else:
+            for i in self.email.text:
+                text.insert(tk.END, i + "\n")
+                text.insert(tk.END, "-" * 5 + "\n")
+        text.config(state=tk.DISABLED)
+
+    def show_email_html(self, text: st.ScrolledText):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+
+        text.config(state=tk.NORMAL)
+        text.delete("0.0", tk.END)
+
+        if len(self.email.html) == 0:
+            text.insert(tk.END, "Not any HTML ...")
+        else:
+            for i in self.email.html:
+                text.insert(tk.END, i + "\n")
+                text.insert(tk.END, "-" * 5 + "\n")
+        text.config(state=tk.DISABLED)
+
+    def show_email_file(self, text: st.ScrolledText):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+
+        text.config(state=tk.NORMAL)
+        text.delete("0.0", tk.END)
+
+        if len(self.email.file) == 0:
+            text.insert(tk.END, "Not any FILE ...")
+        else:
+            for i in self.email.file:
+                text.insert(tk.END, i[0] + "\n")
+        text.config(state=tk.DISABLED)
+
+    def show_add_rc(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+
+        win = tk.Toplevel(self.win)
+        win.title("Add Recipient")
+        win.resizable(False, False)
+
+        label_arg = dict(width=ADD_RC_LABEL_WIDTH, height=ADD_RC_LABEL_HEIGHT)
+        entry_arg = dict(width=ADD_RC_ENTRY_WIDTH)
+
+        name_frame = tk.Frame(win)
+        name_label = tk.Label(name_frame, text="Name: ", **label_arg)
+        name = tk.Entry(name_frame, **entry_arg)
+        name_label.pack(side="left")
+        name.pack(side="left")
+        name_frame.pack(side="top", pady=2, padx=5)
+
+        address_frame = tk.Frame(win)
+        address_label = tk.Label(address_frame, text="Address: ", **label_arg)
+        address = tk.Entry(address_frame, **entry_arg)
+        address_label.pack(side="left")
+        address.pack(side="left")
+        address_frame.pack(side="top", pady=2, padx=5)
+
+        lb = tk.Listbox(win, width=ADD_RC_LISTBOX_WIDTH, height=ADD_RC_LISTBOX_HEIGHT, selectmode=tk.SINGLE)
+        lb.pack(side="top", pady=2.5, padx=2)
+        self.show_rc(lb)
+
+        btn_frame = tk.Frame(win)
+        btn_frame.pack(side="top", pady=2.5, padx=2)
+
+        button_arg = dict(width=ADD_RC_BUTTON_WIDTH, height=ADD_RC_BUTTON_HEIGHT, border=2)
+        rc = tk.Button(btn_frame, text="Add RC",
+                       command=lambda: self.rc_add_to(name.get(), address.get(), lb), **button_arg)
+        cc = tk.Button(btn_frame, text="Add CC",
+                       command=lambda: self.rc_add_cc(name.get(), address.get(), lb), **button_arg)
+        bcc = tk.Button(btn_frame, text="Add BCC",
+                        command=lambda: self.rc_add_bcc(name.get(), address.get(), lb), **button_arg)
+
+        for i in (rc, cc, bcc):
+            i.pack(side="left")
+
+    def rc_add_to(self, name, address, lb):
+        if self.email:
+            if msg.askyesno("Add rc", f"Sure to add {name} <{address}> as rc?"):
+                self.email.add_to_addr(name, address)
+                self.show_rc(lb)
+        else:
+            msg.showwarning("Not email", "You should create email first.")
+
+    def rc_add_cc(self, name, address, lb):
+        if self.email:
+            if msg.askyesno("Add cc", f"Sure to add {name} <{address}> as cc?"):
+                self.email.add_cc_addr(name, address)
+                self.show_rc(lb)
+        else:
+            msg.showwarning("Not email", "You should create email first.")
+
+    def rc_add_bcc(self, name, address, lb):
+        if self.email:
+            if msg.askyesno("Add bcc", f"Sure to add {name} <{address}> as bcc?"):
+                self.email.add_bcc_addr(name, address)
+                self.show_rc(lb)
+        else:
+            msg.showwarning("Not email", "You should create email first.")
+
+    def show_rc(self, lb: tk.Listbox):
+        lb.delete("0", tk.END)
+        if self.email:
+            for i in self.email.to_addr:
+                lb.insert(tk.END, f"TO: {i[0]} <{i[1]}>")
+
+            for i in self.email.cc_addr:
+                lb.insert(tk.END, f"CC: {i[0]} <{i[1]}>")
+
+            for i in self.email.bcc_addr:
+                lb.insert(tk.END, f"BCC: {i[0]} <{i[1]}>")
+        else:
+            msg.showwarning("Not email", "You should create email first.")
+
+    def add_as_text(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+        if msg.askyesno("Sure?", "Sure to add text?"):
+            self.email.add_text(self.text.get("0.0", tk.END))
+            self.text.delete("0.0", tk.END)
+            msg.showinfo("Add", "Success!")
+
+    def add_as_html(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+        if msg.askyesno("Sure?", "Sure to add html?"):
+            self.email.add_html(self.text.get("0.0", tk.END))
+            self.text.delete("0.0", tk.END)
+            msg.showinfo("Add", "Success!")
+
+    def add_html(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+        file = fd.askopenfilename(title="Loading HTML", filetypes=[("HTML", ".html"),
+                                                                   ("TXT", ".txt"),
+                                                                   ("*", "*")])
+        if not file:
+            return
+        self.email.add_html_from_file(file)
+        msg.showinfo("Add", "Success!")
+
+    def add_text(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+        file = fd.askopenfilename(title="Loading TEXT", filetypes=[("TXT", ".txt"),
+                                                                   ("*", "*")])
+        if not file:
+            return
+        self.email.add_text_from_file(file)
+        msg.showinfo("Add", "Success!")
+
+    def add_file(self):
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+        file = fd.askopenfilename(title="Loading FILE", filetypes=[("*", "*")])
+        if not file:
+            return
+        self.email.add_from_file(os.path.split(file)[1], file)
+        msg.showinfo("Add", "Success!")
+
+    def save_config(self):
+        file = None
+        conf = {}
+
+        if self.sender:
+            conf["sender"] = {}
+            conf["sender"]["user"] = self.sender.user
+            conf["sender"]["passwd"] = self.sender.passwd
+            conf["sender"]["host"] = self.sender.host
+            conf["sender"]["port"] = self.sender.port
+            conf["sender"]["debug"] = self.sender.debug
+            conf["sender"]["ssl"] = self.sender.ssl
+            conf["sender"]["start_ssl"] = self.sender.start_ssl
+        try:
+            file = fd.asksaveasfile(mode="w", title="Loading config file", filetypes=[("JSON", ".json")],
+                                    defaultextension=".json")
+            if file is not None:
+                file.write(json.dumps(conf))
+        except IOError:
+            msg.showerror("Load config", "Sorry, IO Error.")
+        except Exception:
+            msg.showerror("Load config", "Sorry, Unknown error.")
+        finally:
+            if file:
+                file.close()
+                msg.showinfo("Saving success", "Success!")
+
+    def load_config(self):
+        file = None
+
+        try:
+            file = fd.askopenfile(mode="r", title="Loading config file", filetypes=[("JSON", ".json")])
+            if file is None:
+                return
+
+            conf = json.load(file)
+            sender = conf.get("sender")
+            if sender:
+                self.sender = Sender(
+                    user=sender["user"],
+                    passwd=sender["passwd"],
+                    host=sender.get("host", "localhost"),
+                    port=sender.get("port", 465),
+                    debug=sender.get("debug", False),
+                    ssl=sender.get("ssl", True),
+                    start_ssl=sender.get("start_ssl", False)
+                )
+        except smtplib.SMTPAuthenticationError:
+            msg.showerror("Load config", "Sorry, SMTP Authentication error. Please check your user and password.")
+        except smtplib.SMTPException:
+            msg.showerror("Load config", "Sorry, SMTP Error.")
+        except KeyError:
+            msg.showerror("Load config", "Sorry, Key Error.")
+        except IOError:
+            msg.showerror("Load config", "Sorry, IO Error.")
+        except Exception:
+            msg.showerror("Load config", "Sorry, Unknown error.")
+        finally:
+            if file:
+                file.close()
+                msg.showinfo("Loading success", "Success!")
+
+    def send(self):
+        if not self.sender:
+            msg.showwarning("Not login", "You should login first.")
+            return
+
+        if not self.email:
+            msg.showwarning("Not email", "You should create email first.")
+            return
+
+        try:
+            self.sender.send(self.email)
+            msg.showinfo("Send email", "Success!")
+        except smtplib.SMTPException:
+            msg.showerror("Send fail", "SMTP error.")
+
+    def mainloop(self):
+        self.win.mainloop()
+
+    def quit(self):
+        self.win.destroy()
+
+
+if __name__ == '__main__':
+    window = SenderGUI()
+    window.mainloop()