1+ from tkinter import *
2+ from tkinter import ttk
3+ from emails import DailyEmail
4+ from scheduler import Scheduler
5+
6+ class GUI :
7+ # The GUI should enable the admin to...
8+ # - configure which content sources to include in email
9+ # - add recipients
10+ # - remove recipients
11+ # - schedule daily time to send email
12+ # - configure sender credentials
13+
14+
15+ def __init__ (self , root ):
16+ # build the GUI window
17+ self .__root = root
18+ self .__root .title ('Daily Digest' )
19+ title_label = ttk .Label (self .__root , text = ' \U0001F4DC START YOUR DAY \U0001F4DC ' ,
20+ font = 'Algerian 32 bold' , justify = CENTER )
21+ title_label .pack (padx = 5 , pady = 5 )
22+
23+ self .__style = ttk .Style ()
24+ self .__style .configure ('TButton' , font = ('Arial' , 12 , 'bold' ))
25+ self .__style .configure ('Header.TLabel' , font = ('Arial' , 18 , 'bold' ))
26+
27+ # GUI listbox for recipients
28+ recipients_frame = ttk .Frame (self .__root )
29+ recipients_frame .pack (padx = 5 , pady = 5 )
30+ self .__add_recipient_var = StringVar ()
31+ self .__recipient_list_var = Variable ()
32+ self .__build_gui_recipients (recipients_frame ,
33+ self .__add_recipient_var ,
34+ self .__recipient_list_var )
35+
36+ # GUI elements to schedule delivery time
37+ schedule_frame = ttk .Frame (self .__root )
38+ schedule_frame .pack (padx = 5 , pady = 5 )
39+ self .__hour_var = StringVar ()
40+ self .__minute_var = StringVar ()
41+ self .__build_gui_schedule (schedule_frame ,
42+ self .__hour_var ,
43+ self .__minute_var )
44+
45+ # GUI checkboxes of content to include in email
46+ contents_frame = ttk .Frame (self .__root )
47+ contents_frame .pack (padx = 5 , pady = 5 )
48+ self .__quote_var = IntVar ()
49+ self .__weather_var = IntVar ()
50+ self .__wikipedia_var = IntVar ()
51+ self .__build_gui_contents (contents_frame ,
52+ self .__quote_var ,
53+ self .__weather_var ,
54+ self .__wikipedia_var )
55+
56+ # GUI fields for sender email/password credentials
57+ sender_frame = ttk .Frame (self .__root )
58+ sender_frame .pack (padx = 5 , pady = 5 )
59+ self .__sender_email_var = StringVar ()
60+ self .__sender_password_var = StringVar ()
61+ self .__build_gui_sender (sender_frame ,
62+ self .__sender_email_var ,
63+ self .__sender_password_var )
64+
65+ # GUI field for controls
66+ controls_frame = ttk .Frame (self .__root )
67+ controls_frame .pack (padx = 5 , pady = 5 )
68+ self .__build_gui_controls (controls_frame )
69+
70+ # set initial values for variables
71+ self .__email = DailyEmail ()
72+
73+ self .__add_recipient_var .set ('' )
74+ self .__recipient_list_var .set (self .__email .recipient_list )
75+
76+ self .__hour_var .set ('07' ) # defaul send time
77+ self .__minute_var .set ('30' )
78+
79+ self .__quote_var .set (self .__email .content ['quote' ]['include' ])
80+ self .__weather_var .set (self .__email .content ['weather' ]['include' ])
81+ self .__wikipedia_var .set (self .__email .content ['wikipedia' ]['include' ])
82+
83+ self .__sender_email_var .set (self .__email .sender_credentials ['email' ])
84+ self .__sender_password_var .set (self .__email .sender_credentials ['password' ])
85+
86+ # initialize scheduler
87+ self .__scheduler = Scheduler ()
88+ self .__scheduler .start ()
89+ self .__root .protocol ("WM_DELETE_WINDOW" , self .__shutdown ) # shuts down the scheduler
90+
91+ """
92+ Build GUI elements to add/remove recipients
93+ """
94+ def __build_gui_recipients (self , master , add_recipient_var , recipient_list_var ):
95+ # create GUI widgets
96+ header = ttk .Label (master , text = 'Recipients:' , style = 'Header.TLabel' )
97+ spacer_frame = ttk .Frame (master ) # used as GUI spacer
98+
99+ recipients_entry = ttk .Entry (master , width = 40 , textvariable = add_recipient_var )
100+ recipients_scrollbar = ttk .Scrollbar (master , orient = VERTICAL )
101+ recipients_scrollbar .grid (row = 4 , column = 1 , sticky = N + S + W + E )
102+ recipient_listbox = Listbox (master , listvariable = recipient_list_var ,
103+ selectmode = 'multiple' , width = 40 , height = 5 )
104+ recipient_listbox .configure (yscrollcommand = recipients_scrollbar .set )
105+ recipients_scrollbar .config (command = recipient_listbox .yview )
106+
107+ add_button = ttk .Button (master , text = 'Add Recipient' , command = self .__add_recipient )
108+ remove_button = ttk .Button (master , text = 'Remove Selected' ,
109+ command = lambda : self .__remove_selected_recipients (recipient_listbox .curselection ()))
110+
111+ # place GUI widgets using grid geometry manager
112+ header .grid (row = 0 , column = 0 )
113+ recipients_entry .grid (row = 1 , column = 0 )
114+ add_button .grid (row = 2 , column = 0 )
115+ spacer_frame .grid (row = 3 , column = 0 , pady = 5 )
116+ recipient_listbox .grid (row = 4 , column = 0 )
117+ remove_button .grid (row = 5 , column = 0 )
118+
119+ """
120+ Build GUI elements to schedule send time
121+ """
122+ def __build_gui_schedule (self , master , hour_var , minute_var ):
123+ # create GUI widgets
124+ header = ttk .Label (master , text = 'Scheduled Time (24hr):' , style = 'Header.TLabel' )
125+ hour_spinbox = ttk .Spinbox (master , from_ = 0 , to = 23 , textvariable = hour_var ,
126+ wrap = True , width = 3 , justify = CENTER , font = 'Arial 12' )
127+ minute_spinbox = ttk .Spinbox (master , from_ = 0 , to = 59 , textvariable = minute_var ,
128+ wrap = True , width = 3 , justify = CENTER , font = 'Arial 12' )
129+
130+ # place GUI widgets using grid geometry manager
131+ header .grid (row = 0 , column = 0 , columnspan = 2 )
132+ hour_spinbox .grid (row = 1 , column = 0 , sticky = E , padx = 2 , pady = 5 )
133+ minute_spinbox .grid (row = 1 , column = 1 , sticky = W , padx = 2 , pady = 5 )
134+
135+ """
136+ Build GUI elements to select content to include
137+ """
138+ def __build_gui_contents (self , master , quote_var , weather_var , wikipedia_var ):
139+ # create GUI widgets
140+ header = ttk .Label (master , text = 'Digest Contents:' , style = 'Header.TLabel' )
141+ quote_checkbox = Checkbutton (master , text = 'Motivational Quote' ,
142+ onvalue = True , offvalue = False ,
143+ variable = quote_var )
144+ weather_checkbox = Checkbutton (master , text = 'Weather Forecast' ,
145+ onvalue = True , offvalue = False ,
146+ variable = weather_var )
147+ wikipedia_checkbox = Checkbutton (master , text = 'Wikipedia Article' ,
148+ onvalue = True , offvalue = False ,
149+ variable = wikipedia_var )
150+
151+ # place GUI widgets using grid geometry manager
152+ header .grid (row = 0 , column = 0 , columnspan = 2 )
153+ quote_checkbox .grid (row = 1 , column = 0 , sticky = W )
154+ weather_checkbox .grid (row = 2 , column = 0 , sticky = W )
155+ wikipedia_checkbox .grid (row = 2 , column = 1 , sticky = W )
156+
157+ """
158+ Build GUI elements to configure sender credentials
159+ """
160+ def __build_gui_sender (self , master , sender_email_var , sender_password_var ):
161+ # create GUI widgets
162+ header = ttk .Label (master , text = 'Sender Credentials:' , style = 'Header.TLabel' )
163+ email_label = ttk .Label (master , text = "Email:" )
164+ email_entry = ttk .Entry (master , width = 40 ,
165+ textvariable = sender_email_var )
166+ password_label = ttk .Label (master , text = 'Password:' )
167+ password_entry = ttk .Entry (master , width = 40 , show = '*' ,
168+ textvariable = sender_password_var )
169+
170+ # place GUI widgets using grid geometry manager
171+ header .grid (row = 0 , column = 0 , columnspan = 2 )
172+ email_label .grid (row = 1 , column = 0 , pady = 2 , sticky = E )
173+ email_entry .grid (row = 1 , column = 1 , pady = 2 , sticky = W )
174+ password_label .grid (row = 2 , column = 0 , pady = 2 , sticky = E )
175+ password_entry .grid (row = 2 , column = 1 , pady = 2 , sticky = W )
176+
177+ """
178+ Build GUI elements to update settings & manually send digest email
179+ """
180+ def __build_gui_controls (self , master ):
181+ # create GUI widgets
182+ update_button = ttk .Button (master , text = 'Update Settings' , command = self .__update_settings )
183+ send_button = ttk .Button (master , text = 'Manual Send' , command = self .__manual_send )
184+
185+ # place GUI widgets using grid geometry manager
186+ update_button .grid (row = 0 , column = 0 , padx = 5 , pady = 5 )
187+ send_button .grid (row = 0 , column = 1 , padx = 5 , pady = 5 )
188+
189+ """
190+ Callback function to add recipient
191+ """
192+ def __add_recipient (self ):
193+ new_recipient = self .__add_recipient_var .get ()
194+ if new_recipient != '' :
195+ recipient_list = self .__recipient_list_var .get ()
196+ if recipient_list != '' :
197+ self .__recipient_list_var .set (recipient_list + (new_recipient ,))
198+ else :
199+ self .__recipient_list_var .set ([new_recipient ])
200+ self .__add_recipient_var .set ('' ) # clear entry field
201+
202+ """
203+ Callback function to remove selected recipient(s)
204+ """
205+ def __remove_selected_recipients (self , selection ):
206+ recipient_list = list (self .__recipient_list_var .get ())
207+ for index in reversed (selection ):
208+ recipient_list .pop (index )
209+ self .__recipient_list_var .set (recipient_list )
210+
211+ """
212+ Callback function to update settings
213+ """
214+ def __update_settings (self ):
215+ print ('Updating settings...' )
216+ self .__email .recipient_list = list (self .__recipient_list_var .get ())
217+
218+ self .__email .content ['quote' ]['include' ] = self .__quote_var .get ()
219+ self .__email .content ['weather' ]['include' ] = self .__weather_var .get ()
220+ self .__email .content ['wikipedia' ]['include' ] = self .__wikipedia_var .get ()
221+
222+ self .__email .sender_credentials = {'email' : self .__sender_email_var .get (),
223+ 'password' : self .__sender_password_var .get ()}
224+
225+ self .__scheduler .schedule_task (int (self .__hour_var .get ()),
226+ int (self .__minute_var .get ()),
227+ self .__email .send_email )
228+
229+ """
230+ Callback function to manually send digest email
231+ """
232+ def __manual_send (self ):
233+ # note: settings are not updated before manual send
234+ print ('Manually sending email digest...' )
235+ self .__email .send_email ()
236+
237+ """
238+ Shutdown the scheduler before closing the GUI window
239+ """
240+ def __shutdown (self ):
241+ print ('Shutting down the scheduler...' )
242+ self .__scheduler .stop ()
243+ self .__scheduler .join ()
244+ self .__root .destroy () # close the GUI
245+
246+ if __name__ == '__main__' :
247+ root = Tk ()
248+ app = GUI (root )
249+ root .mainloop ()
250+
0 commit comments