def set_current_view(self, new_btn: CustomButton, load: bool = True): if self.current_view_btn: self.current_view_btn.config(font=BasePage.LIGHT_FONT, background='#d1d1d1') new_btn.config(font=BasePage.UNDERLINED_BOLD_FONT, background='#c1c1c1') self.search_entry.delete(0, 'end') self.current_view_btn = new_btn self.buy_btn.config(state='disabled') current_table_view = self.table_views[self.current_view_btn] current_table_view.reset() current_table_view.tkraise() if load: current_table_view.load_all_rows()
def set_current_view(self, new_btn: CustomButton): if self.current_view_btn: self.current_view_btn.config(font=BasePage.LIGHT_FONT, background='#d1d1d1') new_btn.config(font=BasePage.UNDERLINED_BOLD_FONT, background='#c1c1c1') self.current_view_btn = new_btn current_view = self.views[self.current_view_btn] if isinstance(current_view, TableView): self.refund_btn.tkraise() username_key = sanitize(self.master.user.username) current_view.reset() current_view.load_rows_by_name(username_key) else: self.add_funds_btn.tkraise() current_view.tkraise()
class HomePage(BasePage, ABC): """ The home page of the application. Contains the music catalog and allows the user to navigate to their account page. """ def __init__(self, *args, **kwargs): BasePage.__init__(self, *args, **kwargs) self.build_gui() self.table_views[self.songs_btn].update_genre_content() self.entries += [self.search_entry] self.selected_album = None self.current_view_btn = None def reset(self): super().reset() self.account_balance_lbl.config( text=f'balance: ${self.master.user.account_balace/100.0}') self.set_current_view(self.songs_btn) def set_current_view(self, new_btn: CustomButton, load: bool = True): if self.current_view_btn: self.current_view_btn.config(font=BasePage.LIGHT_FONT, background='#d1d1d1') new_btn.config(font=BasePage.UNDERLINED_BOLD_FONT, background='#c1c1c1') self.search_entry.delete(0, 'end') self.current_view_btn = new_btn self.buy_btn.config(state='disabled') current_table_view = self.table_views[self.current_view_btn] current_table_view.reset() current_table_view.tkraise() if load: current_table_view.load_all_rows() def on_log_out(self): self.master.user = None self.master.show_page('start') def on_account(self): self.master.show_page('account') def on_buy(self): album_price, album_id = self.selected_album.album_price, self.selected_album.album_id if self.selected_album and self.master.buy_album( album_id=album_id, album_price=album_price): self.account_balance_lbl.config( text=f'balance: ${self.master.user.account_balace / 100.0}') self.buy_btn.display_message('success', delay=0.5, background='#59c872', final_state='disabled') self.selected_album = None else: self.buy_btn.display_message('error', delay=0.5, final_state='disabled') def on_search(self): user_input = sanitize_extra(self.search_entry.get().strip()) current_table_view = self.table_views[self.current_view_btn] current_table_view.load_rows_by_name(user_input) def on_album_select(self, event): album_data = self.table_views[self.albums_btn].get_selected_album_data( event) if album_data: self.selected_album = Transaction.AlbumData(*album_data) album_price = int(float(self.selected_album.album_price) * 100) if self.buy_btn[ 'text'] == 'buy album' and self.master.user.account_balace >= album_price: self.buy_btn.config(state='normal') else: self.buy_btn.config(state='disabled') def on_album_open(self, event): parent_iid = self.table_views[self.albums_btn].identify_row(event.y) if parent_iid != '': parent_name, parent_id = self.table_views[ self.albums_btn].get_name_and_id(parent_iid) self.set_current_view(self.songs_btn, load=False) self.search_entry.insert(0, f'album: {parent_name}') self.search_by_parent_id(parent_id) def on_artist_open(self, event): parent_iid = self.table_views[self.artists_btn].identify_row(event.y) if parent_iid != '': parent_name, parent_id = self.table_views[ self.artists_btn].get_name_and_id(parent_iid) self.set_current_view(self.albums_btn, load=False) self.search_entry.insert(0, f'artist: {parent_name}') self.search_by_parent_id(parent_id) def search_by_parent_id(self, parent_id: str): current_table_view = self.table_views[self.current_view_btn] current_table_view.load_rows_by_parent_id(parent_id) def search_songs_by_genre_id(self, genre_id: int): current_view = self.table_views[self.songs_btn] current_view.load_rows_by_genre_id(genre_id) def build_gui(self): # top menu self.top_menu_frame = tk.Frame(self) self.top_menu_frame.config(background='#d1d1d1', height='40', width='1400') self.top_menu_frame.grid() # 'log out' button self.log_out_btn = CustomButton(self.top_menu_frame) self.log_out_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.log_out_btn.config(text='log out', command=self.on_log_out) self.log_out_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') # 'buy' button self.buy_btn = CustomButton(self.top_menu_frame) self.buy_btn.config(activebackground='#39a852', background='#59c872', font=BasePage.LIGHT_FONT, relief='flat') self.buy_btn.config(state='disabled', text='buy album', command=self.on_buy) self.buy_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.85714', rely='0.0', width='200', x='0', y='0') # 'search' button self.search_btn = CustomButton(self.top_menu_frame) self.search_btn.config(activebackground='#9a9a9a', background='#c1c1c1', font=BasePage.LIGHT_FONT, relief='flat') self.search_btn.config(text='search', command=self.on_search) self.search_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.142857', rely='0.0', width='200', x='0', y='0') # search entry self.search_entry = tk.Entry(self.top_menu_frame) self.search_entry.config(font=BasePage.LIGHT_FONT, relief='flat') self.search_entry.place(anchor='nw', height='30', relx='0.3', rely='0.125', width='500', x='0', y='0') self.search_entry.bind('<Return>', lambda event: self.on_search()) # 'account balance' label self.account_balance_lbl = ttk.Label(self.top_menu_frame) self.account_balance_lbl.config(background='#d1d1d1', font=BasePage.LIGHT_FONT, foreground='#515151', text='balance: $0') self.account_balance_lbl.place(anchor='nw', height='40', relx='0.68', width='150', x='0', y='0') # bottom menu self.bottom_menu_frame = tk.Frame(self) self.bottom_menu_frame.config(background='#c1c1c1', height='40', width='1400') self.bottom_menu_frame.grid(column='0', row='2') # 'songs' button self.songs_btn = CustomButton(self.bottom_menu_frame) self.songs_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.songs_btn.config( state='normal', text='songs', command=lambda: self.set_current_view(self.songs_btn)) self.songs_btn.place(anchor='nw', height='40', relx='0.142857', width='400', x='0', y='0') # 'albums' button self.albums_btn = CustomButton(self.bottom_menu_frame) self.albums_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.albums_btn.config( text='albums', command=lambda: self.set_current_view(self.albums_btn)) self.albums_btn.place(anchor='nw', height='40', relx='0.42857', width='400', x='0', y='0') # 'artists' button self.artists_btn = CustomButton(self.bottom_menu_frame) self.artists_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.artists_btn.config( text='artists', command=lambda: self.set_current_view(self.artists_btn)) self.artists_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.7142857', width='400', x='0', y='0') # 'account' button self.account_btn = CustomButton(self.bottom_menu_frame) self.account_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.account_btn.config(text='my account', command=self.on_account) self.account_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') # create views self.content_frame = tk.Frame(self) style = ttk.Style() style.configure("Treeview.Heading", font=('Bahnschrift Light', 12)) self.table_views = { self.songs_btn: SongView(master=self.content_frame, db_loader=self.master.db_loader), self.albums_btn: AlbumView(master=self.content_frame, db_loader=self.master.db_loader), self.artists_btn: ArtistView(master=self.content_frame, db_loader=self.master.db_loader) } # add callbacks to views self.table_views[self.albums_btn].bind('<Double 1>', self.on_album_open) self.table_views[self.albums_btn].bind('<Button 1>', self.on_album_select) self.table_views[self.artists_btn].bind('<Double 1>', self.on_artist_open) self.table_views[ self. songs_btn].genre_select_callback = self.search_songs_by_genre_id # place views in frame for view in self.table_views.values(): view.place(anchor='nw', height='591', width='1400', x='0', y='0') self.table_views[self.songs_btn].tkraise() self.content_frame.config(height='591', width='1400') self.content_frame.grid(column='0', row='1')
def build_gui(self): # top menu self.top_menu_frame = tk.Frame(self) self.top_menu_frame.config(background='#d1d1d1', height='40', width='1400') self.top_menu_frame.grid() # 'log out' button self.log_out_btn = CustomButton(self.top_menu_frame) self.log_out_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.log_out_btn.config(text='log out', command=self.on_log_out) self.log_out_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') # 'buy' button self.buy_btn = CustomButton(self.top_menu_frame) self.buy_btn.config(activebackground='#39a852', background='#59c872', font=BasePage.LIGHT_FONT, relief='flat') self.buy_btn.config(state='disabled', text='buy album', command=self.on_buy) self.buy_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.85714', rely='0.0', width='200', x='0', y='0') # 'search' button self.search_btn = CustomButton(self.top_menu_frame) self.search_btn.config(activebackground='#9a9a9a', background='#c1c1c1', font=BasePage.LIGHT_FONT, relief='flat') self.search_btn.config(text='search', command=self.on_search) self.search_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.142857', rely='0.0', width='200', x='0', y='0') # search entry self.search_entry = tk.Entry(self.top_menu_frame) self.search_entry.config(font=BasePage.LIGHT_FONT, relief='flat') self.search_entry.place(anchor='nw', height='30', relx='0.3', rely='0.125', width='500', x='0', y='0') self.search_entry.bind('<Return>', lambda event: self.on_search()) # 'account balance' label self.account_balance_lbl = ttk.Label(self.top_menu_frame) self.account_balance_lbl.config(background='#d1d1d1', font=BasePage.LIGHT_FONT, foreground='#515151', text='balance: $0') self.account_balance_lbl.place(anchor='nw', height='40', relx='0.68', width='150', x='0', y='0') # bottom menu self.bottom_menu_frame = tk.Frame(self) self.bottom_menu_frame.config(background='#c1c1c1', height='40', width='1400') self.bottom_menu_frame.grid(column='0', row='2') # 'songs' button self.songs_btn = CustomButton(self.bottom_menu_frame) self.songs_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.songs_btn.config( state='normal', text='songs', command=lambda: self.set_current_view(self.songs_btn)) self.songs_btn.place(anchor='nw', height='40', relx='0.142857', width='400', x='0', y='0') # 'albums' button self.albums_btn = CustomButton(self.bottom_menu_frame) self.albums_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.albums_btn.config( text='albums', command=lambda: self.set_current_view(self.albums_btn)) self.albums_btn.place(anchor='nw', height='40', relx='0.42857', width='400', x='0', y='0') # 'artists' button self.artists_btn = CustomButton(self.bottom_menu_frame) self.artists_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.artists_btn.config( text='artists', command=lambda: self.set_current_view(self.artists_btn)) self.artists_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.7142857', width='400', x='0', y='0') # 'account' button self.account_btn = CustomButton(self.bottom_menu_frame) self.account_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.account_btn.config(text='my account', command=self.on_account) self.account_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') # create views self.content_frame = tk.Frame(self) style = ttk.Style() style.configure("Treeview.Heading", font=('Bahnschrift Light', 12)) self.table_views = { self.songs_btn: SongView(master=self.content_frame, db_loader=self.master.db_loader), self.albums_btn: AlbumView(master=self.content_frame, db_loader=self.master.db_loader), self.artists_btn: ArtistView(master=self.content_frame, db_loader=self.master.db_loader) } # add callbacks to views self.table_views[self.albums_btn].bind('<Double 1>', self.on_album_open) self.table_views[self.albums_btn].bind('<Button 1>', self.on_album_select) self.table_views[self.artists_btn].bind('<Double 1>', self.on_artist_open) self.table_views[ self. songs_btn].genre_select_callback = self.search_songs_by_genre_id # place views in frame for view in self.table_views.values(): view.place(anchor='nw', height='591', width='1400', x='0', y='0') self.table_views[self.songs_btn].tkraise() self.content_frame.config(height='591', width='1400') self.content_frame.grid(column='0', row='1')
def build_gui(self): # top menu self.top_menu_frame = tk.Frame(self) # 'log out' button self.log_out_btn = CustomButton(self.top_menu_frame) self.log_out_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.log_out_btn.config(text='log out', command=self.on_log_out) self.log_out_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') # 'add funds' button self.add_funds_btn = CustomButton(self.top_menu_frame) self.add_funds_btn.config(activebackground='#39a852', background='#59c872', font=BasePage.LIGHT_FONT, relief='flat') self.add_funds_btn.config(text='add funds', command=self.on_add_funds) self.add_funds_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.85714', rely='0.0', width='200', x='0', y='0') # 'refund' button self.refund_btn = CustomButton(self.top_menu_frame) self.refund_btn.config(activebackground='#39a852', background='#59c872', font=BasePage.LIGHT_FONT, relief='flat') self.refund_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.85714', rely='0.0', width='200', x='0', y='0') self.refund_btn.config(text='refund album', state='disabled', command=self.on_refund) # 'account balance' label self.account_balance_lbl = ttk.Label(self.top_menu_frame) self.account_balance_lbl.config(background='#d1d1d1', font=BasePage.LIGHT_FONT, foreground='#515151', text='balance: $0') self.account_balance_lbl.place(anchor='nw', height='40', relx='0.68', width='150', x='0', y='0') self.top_menu_frame.config(background='#d1d1d1', height='40', width='1400') self.top_menu_frame.grid() # bottom menu self.bottom_menu_frame = tk.Frame(self) # 'account info' button self.account_info_btn = CustomButton(self.bottom_menu_frame) self.account_info_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.account_info_btn.config(state='normal', text='account info', command=self.on_personal_info_view) self.account_info_btn.place(anchor='nw', height='40', relx='0.142857', width='600', x='0', y='0') # 'my albums' button self.my_albums_btn = CustomButton(self.bottom_menu_frame) self.my_albums_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.my_albums_btn.config(text='my albums', command=self.on_my_albums_view) self.my_albums_btn.place(anchor='nw', height='40', relx='0.57142857', width='600', x='0', y='0') # 'home' button self.home_btn = CustomButton(self.bottom_menu_frame) self.home_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.home_btn.config(text='home', command=self.on_home) self.home_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') self.bottom_menu_frame.config(background='#c1c1c1', height='40', width='1400') self.bottom_menu_frame.grid(column='0', row='2') # content frame self.content_frame = tk.Frame(self) self.content_frame.config(height='591', width='1400') self.content_frame.grid(column='0', row='1') # transactions view self.transaction_view = TransactionView( master=self.content_frame, db_loader=self.master.db_loader) self.transaction_view.place(anchor='nw', height='591', width='1400', x='0', y='0') self.transaction_view.bind('<Button 1>', self.on_album_select) # account view self.account_info_frame = tk.Frame(self.content_frame) self.account_info_frame.config(width='1400', height='591') self.account_info_frame.grid() # build view dictionary self.views = { self.account_info_btn: self.account_info_frame, self.my_albums_btn: self.transaction_view } # personal info self.personal_info_frame = tk.LabelFrame(self.account_info_frame) self.label_frame = tk.Frame(self.personal_info_frame) self.username_lbl = tk.Label(self.label_frame) self.username_lbl.config(font=BasePage.LIGHT_FONT, text='username:'******'ne', relx='1.0', x='0', y='0') self.first_name_lbl = tk.Label(self.label_frame) self.first_name_lbl.config(font=BasePage.LIGHT_FONT, text='first name:') self.first_name_lbl.place(anchor='ne', relx='1.0', rely='0.1', x='0', y='0') self.last_name_lbl = tk.Label(self.label_frame) self.last_name_lbl.config(font=BasePage.LIGHT_FONT, text='last name:') self.last_name_lbl.place(anchor='ne', relx='1.0', rely='0.2', x='0', y='0') self.email_lbl = tk.Label(self.label_frame) self.email_lbl.config(font=BasePage.LIGHT_FONT, text='email:') self.email_lbl.place(anchor='ne', relx='1.0', rely='0.3', x='0', y='0') self.password_lbl = tk.Label(self.label_frame) self.password_lbl.config(font=BasePage.BOLD_FONT, state='normal', text='change your password') self.password_lbl.place(anchor='ne', relx='1', rely='0.4', x='0', y='0') self.old_password_lbl = tk.Label(self.label_frame) self.old_password_lbl.config(font=BasePage.LIGHT_FONT, text='old password:'******'ne', relx='1.0', rely='0.5', x='0', y='0') self.new_password_lbl = tk.Label(self.label_frame) self.new_password_lbl.config(font=BasePage.LIGHT_FONT, text='new password:'******'ne', relx='1.0', rely='0.6', x='0', y='0') self.new_password_conf_lbl = tk.Label(self.label_frame) self.new_password_conf_lbl.config(font=BasePage.LIGHT_FONT, text='confirm new password:'******'ne', relx='1.0', rely='0.7', x='0', y='0') self.label_frame.config(height='200', width='200') self.label_frame.place(anchor='nw', height='450', relx='0.02', rely='0.05', width='200', x='0', y='0') self.entry_frame = tk.Frame(self.personal_info_frame) self.username_entry = tk.Entry(self.entry_frame) self.username_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.username_entry.place(anchor='nw', width='300', x='0', y='0') self.first_name_entry = tk.Entry(self.entry_frame) self.first_name_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.first_name_entry.place(anchor='nw', rely='0.1', width='300', x='0', y='0') self.last_name_entry = tk.Entry(self.entry_frame) self.last_name_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.last_name_entry.place(anchor='nw', rely='0.2', width='300', x='0', y='0') self.email_entry = tk.Entry(self.entry_frame) self.email_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.email_entry.place(anchor='nw', rely='0.3', width='300', x='0', y='0') self.old_pass_entry = tk.Entry(self.entry_frame) self.old_pass_entry.config(relief='flat', font=BasePage.LIGHT_FONT, show='*') self.old_pass_entry.place(anchor='nw', rely='0.5', width='300', x='0', y='0') self.new_pass_entry = tk.Entry(self.entry_frame) self.new_pass_entry.config(relief='flat', font=BasePage.LIGHT_FONT, show='*') self.new_pass_entry.place(anchor='nw', rely='0.6', width='300', x='0', y='0') self.new_pass_conf_entry = tk.Entry(self.entry_frame) self.new_pass_conf_entry.config(relief='flat', font=BasePage.LIGHT_FONT, show='*') self.new_pass_conf_entry.place(anchor='nw', rely='0.7', width='300', x='0', y='0') # 'edit personal info' button self.edit_personal_info_btn = CustomButton(self.entry_frame) self.edit_personal_info_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.edit_personal_info_btn.config(text='edit personal details', command=self.on_personal_info_edit) self.edit_personal_info_btn.place(anchor='nw', relx='0.15', rely='0.85', width='200', x='0', y='0') # 'delete account' button self.delete_account_btn = CustomButton(self.personal_info_frame) self.delete_account_btn.config(activebackground='#ff4545', background='#ff9b9b', font=BasePage.LIGHT_FONT, relief='flat') self.delete_account_btn.config(text='delete account', command=self.on_delete_account) self.entry_frame.config(height='200', width='200') self.entry_frame.place(anchor='ne', height='450', relx='1.0', rely='0.05', width='350', x='0', y='0') self.personal_info_frame.config(borderwidth='2', font=BasePage.LIGHT_FONT, height='200', text='personal info') self.personal_info_frame.config(width='200') self.personal_info_frame.place(anchor='nw', height='500', relx='0.05', rely='0.07', width='600', x='0', y='0') # payment info self.payment_info_frame = tk.LabelFrame(self.account_info_frame) self.payment_label_frame = tk.Frame(self.payment_info_frame) self.card_nr_lbl = tk.Label(self.payment_label_frame) self.card_nr_lbl.config(font=BasePage.LIGHT_FONT, text='card number:') self.card_nr_lbl.place(anchor='ne', relx='1.0', x='0', y='0') self.exp_date_lbl = tk.Label(self.payment_label_frame) self.exp_date_lbl.config(font=BasePage.LIGHT_FONT, text='expiration date:') self.exp_date_lbl.place(anchor='ne', relx='1.0', rely='0.1', x='0', y='0') self.card_type_lbl = tk.Label(self.payment_label_frame) self.card_type_lbl.config(font=BasePage.LIGHT_FONT, text='card type:') self.card_type_lbl.place(anchor='ne', relx='1.0', rely='0.2', x='0', y='0') self.add_funds_lbl = tk.Label(self.payment_label_frame) self.add_funds_lbl.config(font=BasePage.BOLD_FONT, state='normal', text='add money to account') self.add_funds_lbl.place(anchor='ne', relx='1', rely='0.5', x='0', y='0') self.amount_lbl = tk.Label(self.payment_label_frame) self.amount_lbl.config(font=BasePage.LIGHT_FONT, text='USD amount:') self.amount_lbl.place(anchor='ne', relx='1.0', rely='0.6', x='0', y='0') self.verif_lbl = tk.Label(self.payment_label_frame) self.verif_lbl.config(font=BasePage.LIGHT_FONT, text='verification code:') self.verif_lbl.place(anchor='ne', relx='1.0', rely='0.7', x='0', y='0') self.payment_label_frame.config(height='200', width='200') self.payment_label_frame.place(anchor='nw', height='450', relx='0.02', rely='0.05', width='200', x='0', y='0') self.payment_entry_frame = tk.Frame(self.payment_info_frame) self.card_nr_entry = tk.Entry(self.payment_entry_frame) self.card_nr_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.card_nr_entry.place(anchor='nw', width='300', x='0', y='0') self.exp_date_entry = tk.Entry(self.payment_entry_frame) self.exp_date_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.exp_date_entry.place(anchor='nw', rely='0.1', width='300', x='0', y='0') self.card_type_entry = tk.Entry(self.payment_entry_frame) self.card_type_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.card_type_entry.place(anchor='nw', rely='0.2', width='300', x='0', y='0') self.amount_entry = tk.Entry(self.payment_entry_frame) self.amount_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.amount_entry.place(anchor='nw', rely='0.6', width='300', x='0', y='0') self.verif_entry = tk.Entry(self.payment_entry_frame) self.verif_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.verif_entry.place(anchor='nw', rely='0.7', width='300', x='0', y='0') # 'validate' button self.validate_btn = CustomButton(self.payment_entry_frame) self.validate_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT) self.validate_btn.config(justify='left', relief='flat', text='validate', command=self.on_validate) self.validate_btn.place(anchor='nw', relx='0.15', rely='0.85', width='200', x='0', y='0') self.payment_entry_frame.config(height='200', width='200') self.payment_entry_frame.place(anchor='ne', height='450', relx='1.0', rely='0.05', width='350', x='0', y='0') self.payment_info_frame.config(font=BasePage.LIGHT_FONT, height='200', text='payment info', width='200') self.payment_info_frame.place(anchor='nw', height='500', relx='0.52', rely='0.07', width='600', x='0', y='0')
class AccountPage(BasePage, ABC): """ The user account page of the application. Contains the user's personal information and all purhcased music albums. """ def __init__(self, *args, **kwargs): BasePage.__init__(self, *args, **kwargs) self.build_gui() # group entries by responsibilities self.personal_entries = [ self.username_entry, self.first_name_entry, self.last_name_entry, self.email_entry, self.old_pass_entry, self.new_pass_entry, self.new_pass_conf_entry ] self.payment_entries = [self.amount_entry, self.verif_entry] self.entries += self.personal_entries self.entries += self.payment_entries self.entries += [ self.card_nr_entry, self.card_type_entry, self.exp_date_entry ] # remember widgets that are toggled by button presses self.personal_toggled_lbls = [ self.password_lbl, self.old_password_lbl, self.new_password_lbl, self.new_password_conf_lbl, self.old_password_lbl ] self.payment_toggled_widgets = [ self.add_funds_lbl, self.amount_lbl, self.verif_lbl, self.validate_btn ] # remember widgets that are invisible initially self.place_forget_widgets = [self.delete_account_btn] self.is_editing_personal_info = False self.selected_transaction = None self.current_view_btn = None self.verif_code = -1 def reset(self): BasePage.reset(self) # disable widgets that need to be toggled for label in self.personal_toggled_lbls: label.config(state='disabled') for widget in self.payment_toggled_widgets: widget.config(state='disabled') # hide invisible widgets for widget in self.place_forget_widgets: widget.place_forget() self.load_account_data() for entry in self.entries: entry.config(state='readonly') self.account_balance_lbl.config( text=f'balance: ${self.master.user.account_balace / 100.0}') self.is_editing_personal_info = False self.selected_transaction = None self.on_personal_info_view() def on_home(self): self.master.show_page('home') def on_my_albums_view(self): self.set_current_view(self.my_albums_btn) def on_personal_info_view(self): self.set_current_view(self.account_info_btn) def on_log_out(self): self.master.user = None self.master.show_page('start') def on_refund(self): if not self.selected_transaction: return if self.master.refund_transaction(self.selected_transaction.tr_id, self.selected_transaction.amount): self.account_balance_lbl.config( text=f'balance: ${self.master.user.account_balace / 100.0}') self.transaction_view.delete_transaction_row( self.selected_transaction.tr_id) self.selected_transaction = None self.refund_btn.config(state='disabled', background='#59c872') else: self.refund_btn.display_message('error', delay=0.5, final_state='disabled') def on_album_select(self, event): if self.refund_btn['text'] != 'refund album': return transaction_data = self.transaction_view.get_selected_transaction_data( event) if transaction_data: self.selected_transaction = Transaction.TransactionData( *transaction_data) self.refund_btn.config(state='normal') else: self.refund_btn.config(state='disabled') def on_add_funds(self): for label in self.payment_toggled_widgets: label.config(state='normal') for entry in self.payment_entries: entry.config(state='normal') # the verification code is randomly generated by the app for demonstration purposes self.verif_code = Transaction.generate_verification_code() logging.debug(f'Generated verification code: {self.verif_code}') def on_validate(self): self.validate_btn.config(state='disabled') self.validate_task() def validate_task(self): try: amount = self.amount_entry.get().strip() # raises ValueError if amount cannot be converted to floating point float(amount) verif_code = self.verif_entry.get().strip() if self.verif_code != int(verif_code): raise ValueError('verification code incorrect') if self.master.add_user_funds(amount): self.validate_btn.display_message('success', delay=0.5, background='#59c872', final_state='disabled') self.account_balance_lbl.config( text=f'balance: ${self.master.user.account_balace / 100.0}' ) else: raise ValueError('databases error') except (ValueError, OverflowError) as err: logging.error(f'Failed to add funds to account: {err}') self.validate_btn.display_message('error', delay=0.5, final_state='disabled') for label in self.payment_toggled_widgets: label.config(state='disabled') for entry in self.payment_entries: entry.delete(0, 'end') entry.config(state='disabled') def on_personal_info_edit(self): if not self.is_editing_personal_info: self.is_editing_personal_info = True self.delete_account_btn.place(anchor='nw', relx='0.05', rely='0.85', width='200', x='0', y='0') for entry in self.personal_entries: entry.config(state='normal') for label in self.personal_toggled_lbls: label.config(state='normal') else: self.delete_account_btn.place_forget() self.is_editing_personal_info = False self.save_personal_info() def on_delete_account(self): user_pass = self.old_pass_entry.get() user = self.master.user self.delete_account_btn.config( state='disabled', background=self.delete_account_btn.default_bg) def delete_account_task(): if not len(user_pass): self.delete_account_btn.display_message('password required', 1.0, final_state='normal') elif user.match_password(user_pass): if self.master.delete_user(): self.delete_account_btn.config(state='normal') self.on_log_out() else: self.delete_account_btn.display_message( 'error', 0.5, final_state='normal') else: self.delete_account_btn.display_message('wrong password', 1.0, final_state='normal') self.master.run_background_task(delete_account_task) def set_current_view(self, new_btn: CustomButton): if self.current_view_btn: self.current_view_btn.config(font=BasePage.LIGHT_FONT, background='#d1d1d1') new_btn.config(font=BasePage.UNDERLINED_BOLD_FONT, background='#c1c1c1') self.current_view_btn = new_btn current_view = self.views[self.current_view_btn] if isinstance(current_view, TableView): self.refund_btn.tkraise() username_key = sanitize(self.master.user.username) current_view.reset() current_view.load_rows_by_name(username_key) else: self.add_funds_btn.tkraise() current_view.tkraise() def save_personal_info(self): user = self.master.user # get user input data username, first_name, last_name, email, old_pass, new_pass, new_pass_conf \ = [e.get().strip() for e in self.personal_entries] first_name = first_name.title() last_name = last_name.title() email = email.lower() try: if len(old_pass): # attempt to change user password if not user.match_password(old_pass): raise ValueError('old password incorrect') if len(new_pass ) < user.MIN_PASS_LEN or new_pass != new_pass_conf: raise ValueError('new passwords don\'t match') elif len(new_pass): raise ValueError('old password required to change password') # update in database if not self.master.update_user(username, first_name, last_name, email, new_pass): raise RuntimeError('database could not update data') except (ValueError, RuntimeError) as err: logging.error(f'Failed to edit personal data: {err}') self.edit_personal_info_btn.display_message('error', delay=0.5) self.reset() def load_account_data(self): user = self.master.user self.username_entry.insert(0, user.username) self.first_name_entry.insert(0, user.first_name) self.last_name_entry.insert(0, user.last_name) self.email_entry.insert(0, user.email) self.card_nr_entry.insert(0, user.card_nr) self.card_type_entry.insert(0, user.card_type) self.exp_date_entry.insert(0, user.expiration_date) def build_gui(self): # top menu self.top_menu_frame = tk.Frame(self) # 'log out' button self.log_out_btn = CustomButton(self.top_menu_frame) self.log_out_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.log_out_btn.config(text='log out', command=self.on_log_out) self.log_out_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') # 'add funds' button self.add_funds_btn = CustomButton(self.top_menu_frame) self.add_funds_btn.config(activebackground='#39a852', background='#59c872', font=BasePage.LIGHT_FONT, relief='flat') self.add_funds_btn.config(text='add funds', command=self.on_add_funds) self.add_funds_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.85714', rely='0.0', width='200', x='0', y='0') # 'refund' button self.refund_btn = CustomButton(self.top_menu_frame) self.refund_btn.config(activebackground='#39a852', background='#59c872', font=BasePage.LIGHT_FONT, relief='flat') self.refund_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.85714', rely='0.0', width='200', x='0', y='0') self.refund_btn.config(text='refund album', state='disabled', command=self.on_refund) # 'account balance' label self.account_balance_lbl = ttk.Label(self.top_menu_frame) self.account_balance_lbl.config(background='#d1d1d1', font=BasePage.LIGHT_FONT, foreground='#515151', text='balance: $0') self.account_balance_lbl.place(anchor='nw', height='40', relx='0.68', width='150', x='0', y='0') self.top_menu_frame.config(background='#d1d1d1', height='40', width='1400') self.top_menu_frame.grid() # bottom menu self.bottom_menu_frame = tk.Frame(self) # 'account info' button self.account_info_btn = CustomButton(self.bottom_menu_frame) self.account_info_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.account_info_btn.config(state='normal', text='account info', command=self.on_personal_info_view) self.account_info_btn.place(anchor='nw', height='40', relx='0.142857', width='600', x='0', y='0') # 'my albums' button self.my_albums_btn = CustomButton(self.bottom_menu_frame) self.my_albums_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.my_albums_btn.config(text='my albums', command=self.on_my_albums_view) self.my_albums_btn.place(anchor='nw', height='40', relx='0.57142857', width='600', x='0', y='0') # 'home' button self.home_btn = CustomButton(self.bottom_menu_frame) self.home_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.home_btn.config(text='home', command=self.on_home) self.home_btn.place(anchor='nw', height='40', relwidth='0.0', relx='0.0', rely='0.0', width='200', x='0', y='0') self.bottom_menu_frame.config(background='#c1c1c1', height='40', width='1400') self.bottom_menu_frame.grid(column='0', row='2') # content frame self.content_frame = tk.Frame(self) self.content_frame.config(height='591', width='1400') self.content_frame.grid(column='0', row='1') # transactions view self.transaction_view = TransactionView( master=self.content_frame, db_loader=self.master.db_loader) self.transaction_view.place(anchor='nw', height='591', width='1400', x='0', y='0') self.transaction_view.bind('<Button 1>', self.on_album_select) # account view self.account_info_frame = tk.Frame(self.content_frame) self.account_info_frame.config(width='1400', height='591') self.account_info_frame.grid() # build view dictionary self.views = { self.account_info_btn: self.account_info_frame, self.my_albums_btn: self.transaction_view } # personal info self.personal_info_frame = tk.LabelFrame(self.account_info_frame) self.label_frame = tk.Frame(self.personal_info_frame) self.username_lbl = tk.Label(self.label_frame) self.username_lbl.config(font=BasePage.LIGHT_FONT, text='username:'******'ne', relx='1.0', x='0', y='0') self.first_name_lbl = tk.Label(self.label_frame) self.first_name_lbl.config(font=BasePage.LIGHT_FONT, text='first name:') self.first_name_lbl.place(anchor='ne', relx='1.0', rely='0.1', x='0', y='0') self.last_name_lbl = tk.Label(self.label_frame) self.last_name_lbl.config(font=BasePage.LIGHT_FONT, text='last name:') self.last_name_lbl.place(anchor='ne', relx='1.0', rely='0.2', x='0', y='0') self.email_lbl = tk.Label(self.label_frame) self.email_lbl.config(font=BasePage.LIGHT_FONT, text='email:') self.email_lbl.place(anchor='ne', relx='1.0', rely='0.3', x='0', y='0') self.password_lbl = tk.Label(self.label_frame) self.password_lbl.config(font=BasePage.BOLD_FONT, state='normal', text='change your password') self.password_lbl.place(anchor='ne', relx='1', rely='0.4', x='0', y='0') self.old_password_lbl = tk.Label(self.label_frame) self.old_password_lbl.config(font=BasePage.LIGHT_FONT, text='old password:'******'ne', relx='1.0', rely='0.5', x='0', y='0') self.new_password_lbl = tk.Label(self.label_frame) self.new_password_lbl.config(font=BasePage.LIGHT_FONT, text='new password:'******'ne', relx='1.0', rely='0.6', x='0', y='0') self.new_password_conf_lbl = tk.Label(self.label_frame) self.new_password_conf_lbl.config(font=BasePage.LIGHT_FONT, text='confirm new password:'******'ne', relx='1.0', rely='0.7', x='0', y='0') self.label_frame.config(height='200', width='200') self.label_frame.place(anchor='nw', height='450', relx='0.02', rely='0.05', width='200', x='0', y='0') self.entry_frame = tk.Frame(self.personal_info_frame) self.username_entry = tk.Entry(self.entry_frame) self.username_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.username_entry.place(anchor='nw', width='300', x='0', y='0') self.first_name_entry = tk.Entry(self.entry_frame) self.first_name_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.first_name_entry.place(anchor='nw', rely='0.1', width='300', x='0', y='0') self.last_name_entry = tk.Entry(self.entry_frame) self.last_name_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.last_name_entry.place(anchor='nw', rely='0.2', width='300', x='0', y='0') self.email_entry = tk.Entry(self.entry_frame) self.email_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.email_entry.place(anchor='nw', rely='0.3', width='300', x='0', y='0') self.old_pass_entry = tk.Entry(self.entry_frame) self.old_pass_entry.config(relief='flat', font=BasePage.LIGHT_FONT, show='*') self.old_pass_entry.place(anchor='nw', rely='0.5', width='300', x='0', y='0') self.new_pass_entry = tk.Entry(self.entry_frame) self.new_pass_entry.config(relief='flat', font=BasePage.LIGHT_FONT, show='*') self.new_pass_entry.place(anchor='nw', rely='0.6', width='300', x='0', y='0') self.new_pass_conf_entry = tk.Entry(self.entry_frame) self.new_pass_conf_entry.config(relief='flat', font=BasePage.LIGHT_FONT, show='*') self.new_pass_conf_entry.place(anchor='nw', rely='0.7', width='300', x='0', y='0') # 'edit personal info' button self.edit_personal_info_btn = CustomButton(self.entry_frame) self.edit_personal_info_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT, relief='flat') self.edit_personal_info_btn.config(text='edit personal details', command=self.on_personal_info_edit) self.edit_personal_info_btn.place(anchor='nw', relx='0.15', rely='0.85', width='200', x='0', y='0') # 'delete account' button self.delete_account_btn = CustomButton(self.personal_info_frame) self.delete_account_btn.config(activebackground='#ff4545', background='#ff9b9b', font=BasePage.LIGHT_FONT, relief='flat') self.delete_account_btn.config(text='delete account', command=self.on_delete_account) self.entry_frame.config(height='200', width='200') self.entry_frame.place(anchor='ne', height='450', relx='1.0', rely='0.05', width='350', x='0', y='0') self.personal_info_frame.config(borderwidth='2', font=BasePage.LIGHT_FONT, height='200', text='personal info') self.personal_info_frame.config(width='200') self.personal_info_frame.place(anchor='nw', height='500', relx='0.05', rely='0.07', width='600', x='0', y='0') # payment info self.payment_info_frame = tk.LabelFrame(self.account_info_frame) self.payment_label_frame = tk.Frame(self.payment_info_frame) self.card_nr_lbl = tk.Label(self.payment_label_frame) self.card_nr_lbl.config(font=BasePage.LIGHT_FONT, text='card number:') self.card_nr_lbl.place(anchor='ne', relx='1.0', x='0', y='0') self.exp_date_lbl = tk.Label(self.payment_label_frame) self.exp_date_lbl.config(font=BasePage.LIGHT_FONT, text='expiration date:') self.exp_date_lbl.place(anchor='ne', relx='1.0', rely='0.1', x='0', y='0') self.card_type_lbl = tk.Label(self.payment_label_frame) self.card_type_lbl.config(font=BasePage.LIGHT_FONT, text='card type:') self.card_type_lbl.place(anchor='ne', relx='1.0', rely='0.2', x='0', y='0') self.add_funds_lbl = tk.Label(self.payment_label_frame) self.add_funds_lbl.config(font=BasePage.BOLD_FONT, state='normal', text='add money to account') self.add_funds_lbl.place(anchor='ne', relx='1', rely='0.5', x='0', y='0') self.amount_lbl = tk.Label(self.payment_label_frame) self.amount_lbl.config(font=BasePage.LIGHT_FONT, text='USD amount:') self.amount_lbl.place(anchor='ne', relx='1.0', rely='0.6', x='0', y='0') self.verif_lbl = tk.Label(self.payment_label_frame) self.verif_lbl.config(font=BasePage.LIGHT_FONT, text='verification code:') self.verif_lbl.place(anchor='ne', relx='1.0', rely='0.7', x='0', y='0') self.payment_label_frame.config(height='200', width='200') self.payment_label_frame.place(anchor='nw', height='450', relx='0.02', rely='0.05', width='200', x='0', y='0') self.payment_entry_frame = tk.Frame(self.payment_info_frame) self.card_nr_entry = tk.Entry(self.payment_entry_frame) self.card_nr_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.card_nr_entry.place(anchor='nw', width='300', x='0', y='0') self.exp_date_entry = tk.Entry(self.payment_entry_frame) self.exp_date_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.exp_date_entry.place(anchor='nw', rely='0.1', width='300', x='0', y='0') self.card_type_entry = tk.Entry(self.payment_entry_frame) self.card_type_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.card_type_entry.place(anchor='nw', rely='0.2', width='300', x='0', y='0') self.amount_entry = tk.Entry(self.payment_entry_frame) self.amount_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.amount_entry.place(anchor='nw', rely='0.6', width='300', x='0', y='0') self.verif_entry = tk.Entry(self.payment_entry_frame) self.verif_entry.config(relief='flat', font=BasePage.LIGHT_FONT) self.verif_entry.place(anchor='nw', rely='0.7', width='300', x='0', y='0') # 'validate' button self.validate_btn = CustomButton(self.payment_entry_frame) self.validate_btn.config(activebackground='#9a9a9a', background='#d1d1d1', font=BasePage.LIGHT_FONT) self.validate_btn.config(justify='left', relief='flat', text='validate', command=self.on_validate) self.validate_btn.place(anchor='nw', relx='0.15', rely='0.85', width='200', x='0', y='0') self.payment_entry_frame.config(height='200', width='200') self.payment_entry_frame.place(anchor='ne', height='450', relx='1.0', rely='0.05', width='350', x='0', y='0') self.payment_info_frame.config(font=BasePage.LIGHT_FONT, height='200', text='payment info', width='200') self.payment_info_frame.place(anchor='nw', height='500', relx='0.52', rely='0.07', width='600', x='0', y='0')
class StartPage(BasePage, ABC): """ The starting page of the application. Contains a log-in form, as well as a registration form for new users. """ def __init__(self, *args, **kwargs): BasePage.__init__(self, *args, **kwargs) self.build_gui() self.entries += [ self.username_entry, self.password_entry, self.username_reg_entry, self.password_reg_entry, self.password_conf_entry, self.email_entry, self.first_name_entry, self.last_name_entry, self.card_nr_entry, self.card_type_spinbox, self.exp_y_spinbox, self.exp_m_spinbox, self.exp_d_spinbox, ] def on_log_in(self): self.master.run_background_task(self.log_in_task) def log_in_task(self): username = self.username_entry.get().strip() password = self.password_entry.get().strip() self.log_in_btn.config(state='disabled') if self.master.log_in_user(username, password): self.log_in_btn.config(state='normal', background='#b1b1b1') else: self.log_in_btn.display_message('invalid', delay=1) def on_register(self): self.master.run_background_task(self.register_task) def register_task(self): self.register_btn.config(state='disabled') try: # check passwords password = self.password_reg_entry.get().strip() password_conf = self.password_conf_entry.get().strip() if len(password) < User.MIN_PASS_LEN: raise ValueError( f'password must be at least {User.MIN_PASS_LEN} characters' ) if len(password) > User.MAX_PASS_LEN: raise ValueError( f'password must be at most {User.MAX_PASS_LEN} characters') if password != password_conf: raise ValueError('passwords don\'t match') # get other user data username = self.username_reg_entry.get().strip() first_name = self.first_name_entry.get().title().strip() last_name = self.last_name_entry.get().title().strip() email = self.email_entry.get().strip() # email is optional - check if it is specified if len(email) == 0: email = User.NO_EMAIL_MSG # check card number card_nr = self.card_nr_entry.get().strip() if not (len(card_nr) == 16 and card_nr.isalnum()): raise ValueError('card number must be 16 digits long') # check if all date spinboxes are specified exp_d = self.exp_d_spinbox.get() exp_m = self.exp_m_spinbox.get() exp_y = self.exp_y_spinbox.get() if len(exp_d) * len(exp_m) * len(exp_y) == 0: raise ValueError('incomplete date') # convert date to desired format expiration_date = f'{exp_d}-{exp_m}-{exp_y}' expiration_date = datetime.datetime.strptime( expiration_date, '%d-%m-%Y').strftime('%d %b %Y') # initial account balance is always 0 account_balance = '0' card_type = self.card_type_spinbox.get() user_to_register = User(username, first_name, last_name, password, email, card_nr, expiration_date, account_balance, card_type) if user_to_register.has_empty_fields(): raise ValueError('not all input fields completed') # try to load user data into database if not self.master.register_user(user_to_register): raise ValueError('unable to insert user into database') self.register_btn.config(state='normal', background='#b1b1b1') except ValueError as err: logging.error(f'Failed to register new user: {err}') self.register_btn.display_message('error', delay=1) def build_gui(self): # log-in frame self.login_frame = tk.Frame(self) self.login_frame.config(background='#d3d3d3', height=self.winfo_height(), padx='50', width='600') self.login_frame.grid(column='1', row='0') filler_top = tk.Frame(self.login_frame) filler_top.grid(pady='120', columnspan='2') filler_bot = tk.Frame(self.login_frame) filler_bot.grid(row='4', pady='120', columnspan='2') # username and password fields and labels self.username_lbl = tk.Label(self.login_frame) self.username_lbl.config(background='#d3d3d3', font=BasePage.LIGHT_FONT, text='username:'******'1', padx='15', pady='20', sticky='e') self.username_entry = tk.Entry(self.login_frame, font=BasePage.LIGHT_FONT) self.username_entry.config(relief='flat') self.username_entry.grid(column='1', padx='20', pady='20', row='1') self.username_entry.bind('<Return>', lambda event: self.on_log_in()) self.password_lbl = tk.Label(self.login_frame) self.password_lbl.config(background='#d3d3d3', font=BasePage.LIGHT_FONT, text='password:'******'15', pady='20', row='2', sticky='e') self.password_entry = tk.Entry(self.login_frame, show='*', font=BasePage.LIGHT_FONT) self.password_entry.config(relief='flat') self.password_entry.grid(column='1', padx='20', pady='20', row='2') self.password_entry.bind('<Return>', lambda event: self.on_log_in()) # 'log-in' button self.log_in_btn = CustomButton(self.login_frame) self.log_in_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.log_in_btn.config(text='log in', width='10') self.log_in_btn.grid(column='0', columnspan='2', padx='5', pady='10', ipadx='5', ipady='3', row='3') self.log_in_btn.configure(command=self.on_log_in) # application info frame self.info_frame = tk.Frame(self, background='#c3c3c3') filler_top = tk.Frame(self.info_frame) filler_top.grid(pady='130', columnspan='2') filler_bot = tk.Frame(self.info_frame) filler_bot.grid(row='3', pady='136', columnspan='2') self.info_frame.config(height=self.winfo_height(), padx='30', width='400') self.info_frame.grid(column='0', row='0') self.app_info_msg = tk.Message(self.info_frame) self.app_info_msg.config( font='{Bahnschrift Light} 16 {bold}', takefocus=False, text='Welcome to iMusic.\nPlease log in to your account.', width='300', background='#c3c3c3') self.app_info_msg.grid(padx='50', row='1', sticky='w') self.description_msg = tk.Message(self.info_frame) self.description_msg.config( font='{Bahnschrift Light} 14 {}', takefocus=False, text='Browse and buy high-quality music from a variety of genres. ', width='300', background='#c3c3c3') self.description_msg.grid(column='0', padx='50', row='2', sticky='w') # registration frame self.register_frame = tk.Frame(self) self.register_frame.config(height='800', padx='70', width='400') self.register_frame.grid(column='2', pady='30', row='0') self.register_msg = tk.Message(self.register_frame) self.register_msg.config(font='{Bahnschrift Light} 16 {bold}', text='Don\'t have an account yet?\nRegister ' 'now for free!', width='350') self.register_msg.grid(columnspan='2', padx='5', pady='4') self.username_reg_lbl = tk.Label(self.register_frame) self.username_reg_lbl.config(font=BasePage.LIGHT_FONT, text='username:'******'10', pady='5', row='1', sticky='e') self.first_name_lbl = tk.Label(self.register_frame) self.first_name_lbl.config(font=BasePage.LIGHT_FONT, text='first name:') self.first_name_lbl.grid(padx='10', pady='5', row='2', sticky='e') self.last_name_lbl = tk.Label(self.register_frame) self.last_name_lbl.config(font=BasePage.LIGHT_FONT, text='last name:') self.last_name_lbl.grid(padx='10', pady='5', row='3', sticky='e') self.password_reg_lbl = tk.Label(self.register_frame) self.password_reg_lbl.config(font=BasePage.LIGHT_FONT, text='password:'******'10', pady='5', row='4', sticky='e') self.password_conf_lbl = tk.Label(self.register_frame) self.password_conf_lbl.config(font=BasePage.LIGHT_FONT, text='confirm password:'******'10', pady='5', row='5', sticky='e') self.email_lbl = tk.Label(self.register_frame) self.email_lbl.config(font=BasePage.LIGHT_FONT, text='email:') self.email_lbl.grid(padx='10', pady='5', row='6', sticky='e') self.card_nr_lbl = tk.Label(self.register_frame) self.card_nr_lbl.config(font=BasePage.LIGHT_FONT, text='card number:') self.card_nr_lbl.grid(padx='10', pady='5', row='7', sticky='e') self.exp_date_lbl = tk.Label(self.register_frame) self.exp_date_lbl.config(font=BasePage.LIGHT_FONT, text='expiration date:') self.exp_date_lbl.grid(padx='10', pady='5', row='9', sticky='e') self.card_type_lbl = tk.Label(self.register_frame) self.card_type_lbl.config(font=BasePage.LIGHT_FONT, text='type:') self.card_type_lbl.grid(padx='10', pady='5', row='8', sticky='e') self.username_reg_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.username_reg_entry.config(relief='flat') self.username_reg_entry.grid(column='1', row='1') self.first_name_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.first_name_entry.config(relief='flat') self.first_name_entry.grid(column='1', row='2') self.last_name_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.last_name_entry.config(relief='flat') self.last_name_entry.grid(column='1', row='3') self.password_reg_entry = tk.Entry(self.register_frame, show='*', font=BasePage.LIGHT_FONT) self.password_reg_entry.config(relief='flat') self.password_reg_entry.grid(column='1', row='4') self.password_conf_entry = tk.Entry(self.register_frame, show='*', font=BasePage.LIGHT_FONT) self.password_conf_entry.config(relief='flat') self.password_conf_entry.grid(column='1', row='5') self.email_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.email_entry.config(relief='flat') self.email_entry.grid(column='1', row='6') self.card_nr_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.card_nr_entry.config(relief='flat') self.card_nr_entry.grid(column='1', row='7') self.card_type_spinbox = tk.Spinbox(self.register_frame) spinbox_vals = self.master.db_loader.load_spinbox( self.card_type_spinbox, 'SELECT NAME FROM CARD_TYPES') self.card_type_spinbox.config(font=BasePage.LIGHT_FONT, relief='flat', values=spinbox_vals, width='10', state='readonly') self.card_type_spinbox.grid(column='1', row='8') self.exp_date_frame = ttk.Frame(self.register_frame) self.exp_y_spinbox = tk.Spinbox(self.exp_date_frame) year = tk.StringVar() self.exp_y_spinbox.config(font=BasePage.LIGHT_FONT, from_='2020', to='2099', increment='1', relief='flat', state='readonly') self.exp_y_spinbox.config(textvariable=year, width='5') self.exp_y_spinbox.grid(column='1', padx='5', row='0') self.exp_m_spinbox = tk.Spinbox(self.exp_date_frame) month = tk.StringVar() self.exp_m_spinbox.config(font=BasePage.LIGHT_FONT, from_='1', to='12', increment='1', relief='flat') self.exp_m_spinbox.config(textvariable=month, width='5', state='readonly') self.exp_m_spinbox.grid(column='1', padx='5', row='1') self.exp_d_spinbox = tk.Spinbox(self.exp_date_frame) day = tk.StringVar() self.exp_d_spinbox.config(font=BasePage.LIGHT_FONT, from_='1', to='31', increment='1', relief='flat') self.exp_d_spinbox.config(textvariable=day, width='5', state='readonly') self.exp_d_spinbox.grid(column='1', padx='5', row='2') self.year_lbl = tk.Label(self.exp_date_frame) self.year_lbl.config(font=BasePage.LIGHT_FONT, text='year:') self.year_lbl.grid(column='0', row='0', sticky='e') self.month_lbl = tk.Label(self.exp_date_frame) self.month_lbl.config(font=BasePage.LIGHT_FONT, text='month:') self.month_lbl.grid(column='0', row='1', sticky='e') self.day_lbl = tk.Label(self.exp_date_frame) self.day_lbl.config(font=BasePage.LIGHT_FONT, text='day:') self.day_lbl.grid(column='0', row='2', sticky='e') self.exp_date_frame.config(height='200', width='400') self.exp_date_frame.grid(column='1', padx='5', pady='5', row='9') # 'register' button self.register_btn = CustomButton(self.register_frame) self.register_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.register_btn.config(text='register', width='10') self.register_btn.configure(command=self.on_register) self.register_btn.grid(column='0', columnspan='2', padx='5', pady='20', ipadx='5', ipady='3', row='10')
def build_gui(self): # log-in frame self.login_frame = tk.Frame(self) self.login_frame.config(background='#d3d3d3', height=self.winfo_height(), padx='50', width='600') self.login_frame.grid(column='1', row='0') filler_top = tk.Frame(self.login_frame) filler_top.grid(pady='120', columnspan='2') filler_bot = tk.Frame(self.login_frame) filler_bot.grid(row='4', pady='120', columnspan='2') # username and password fields and labels self.username_lbl = tk.Label(self.login_frame) self.username_lbl.config(background='#d3d3d3', font=BasePage.LIGHT_FONT, text='username:'******'1', padx='15', pady='20', sticky='e') self.username_entry = tk.Entry(self.login_frame, font=BasePage.LIGHT_FONT) self.username_entry.config(relief='flat') self.username_entry.grid(column='1', padx='20', pady='20', row='1') self.username_entry.bind('<Return>', lambda event: self.on_log_in()) self.password_lbl = tk.Label(self.login_frame) self.password_lbl.config(background='#d3d3d3', font=BasePage.LIGHT_FONT, text='password:'******'15', pady='20', row='2', sticky='e') self.password_entry = tk.Entry(self.login_frame, show='*', font=BasePage.LIGHT_FONT) self.password_entry.config(relief='flat') self.password_entry.grid(column='1', padx='20', pady='20', row='2') self.password_entry.bind('<Return>', lambda event: self.on_log_in()) # 'log-in' button self.log_in_btn = CustomButton(self.login_frame) self.log_in_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.log_in_btn.config(text='log in', width='10') self.log_in_btn.grid(column='0', columnspan='2', padx='5', pady='10', ipadx='5', ipady='3', row='3') self.log_in_btn.configure(command=self.on_log_in) # application info frame self.info_frame = tk.Frame(self, background='#c3c3c3') filler_top = tk.Frame(self.info_frame) filler_top.grid(pady='130', columnspan='2') filler_bot = tk.Frame(self.info_frame) filler_bot.grid(row='3', pady='136', columnspan='2') self.info_frame.config(height=self.winfo_height(), padx='30', width='400') self.info_frame.grid(column='0', row='0') self.app_info_msg = tk.Message(self.info_frame) self.app_info_msg.config( font='{Bahnschrift Light} 16 {bold}', takefocus=False, text='Welcome to iMusic.\nPlease log in to your account.', width='300', background='#c3c3c3') self.app_info_msg.grid(padx='50', row='1', sticky='w') self.description_msg = tk.Message(self.info_frame) self.description_msg.config( font='{Bahnschrift Light} 14 {}', takefocus=False, text='Browse and buy high-quality music from a variety of genres. ', width='300', background='#c3c3c3') self.description_msg.grid(column='0', padx='50', row='2', sticky='w') # registration frame self.register_frame = tk.Frame(self) self.register_frame.config(height='800', padx='70', width='400') self.register_frame.grid(column='2', pady='30', row='0') self.register_msg = tk.Message(self.register_frame) self.register_msg.config(font='{Bahnschrift Light} 16 {bold}', text='Don\'t have an account yet?\nRegister ' 'now for free!', width='350') self.register_msg.grid(columnspan='2', padx='5', pady='4') self.username_reg_lbl = tk.Label(self.register_frame) self.username_reg_lbl.config(font=BasePage.LIGHT_FONT, text='username:'******'10', pady='5', row='1', sticky='e') self.first_name_lbl = tk.Label(self.register_frame) self.first_name_lbl.config(font=BasePage.LIGHT_FONT, text='first name:') self.first_name_lbl.grid(padx='10', pady='5', row='2', sticky='e') self.last_name_lbl = tk.Label(self.register_frame) self.last_name_lbl.config(font=BasePage.LIGHT_FONT, text='last name:') self.last_name_lbl.grid(padx='10', pady='5', row='3', sticky='e') self.password_reg_lbl = tk.Label(self.register_frame) self.password_reg_lbl.config(font=BasePage.LIGHT_FONT, text='password:'******'10', pady='5', row='4', sticky='e') self.password_conf_lbl = tk.Label(self.register_frame) self.password_conf_lbl.config(font=BasePage.LIGHT_FONT, text='confirm password:'******'10', pady='5', row='5', sticky='e') self.email_lbl = tk.Label(self.register_frame) self.email_lbl.config(font=BasePage.LIGHT_FONT, text='email:') self.email_lbl.grid(padx='10', pady='5', row='6', sticky='e') self.card_nr_lbl = tk.Label(self.register_frame) self.card_nr_lbl.config(font=BasePage.LIGHT_FONT, text='card number:') self.card_nr_lbl.grid(padx='10', pady='5', row='7', sticky='e') self.exp_date_lbl = tk.Label(self.register_frame) self.exp_date_lbl.config(font=BasePage.LIGHT_FONT, text='expiration date:') self.exp_date_lbl.grid(padx='10', pady='5', row='9', sticky='e') self.card_type_lbl = tk.Label(self.register_frame) self.card_type_lbl.config(font=BasePage.LIGHT_FONT, text='type:') self.card_type_lbl.grid(padx='10', pady='5', row='8', sticky='e') self.username_reg_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.username_reg_entry.config(relief='flat') self.username_reg_entry.grid(column='1', row='1') self.first_name_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.first_name_entry.config(relief='flat') self.first_name_entry.grid(column='1', row='2') self.last_name_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.last_name_entry.config(relief='flat') self.last_name_entry.grid(column='1', row='3') self.password_reg_entry = tk.Entry(self.register_frame, show='*', font=BasePage.LIGHT_FONT) self.password_reg_entry.config(relief='flat') self.password_reg_entry.grid(column='1', row='4') self.password_conf_entry = tk.Entry(self.register_frame, show='*', font=BasePage.LIGHT_FONT) self.password_conf_entry.config(relief='flat') self.password_conf_entry.grid(column='1', row='5') self.email_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.email_entry.config(relief='flat') self.email_entry.grid(column='1', row='6') self.card_nr_entry = tk.Entry(self.register_frame, font=BasePage.LIGHT_FONT) self.card_nr_entry.config(relief='flat') self.card_nr_entry.grid(column='1', row='7') self.card_type_spinbox = tk.Spinbox(self.register_frame) spinbox_vals = self.master.db_loader.load_spinbox( self.card_type_spinbox, 'SELECT NAME FROM CARD_TYPES') self.card_type_spinbox.config(font=BasePage.LIGHT_FONT, relief='flat', values=spinbox_vals, width='10', state='readonly') self.card_type_spinbox.grid(column='1', row='8') self.exp_date_frame = ttk.Frame(self.register_frame) self.exp_y_spinbox = tk.Spinbox(self.exp_date_frame) year = tk.StringVar() self.exp_y_spinbox.config(font=BasePage.LIGHT_FONT, from_='2020', to='2099', increment='1', relief='flat', state='readonly') self.exp_y_spinbox.config(textvariable=year, width='5') self.exp_y_spinbox.grid(column='1', padx='5', row='0') self.exp_m_spinbox = tk.Spinbox(self.exp_date_frame) month = tk.StringVar() self.exp_m_spinbox.config(font=BasePage.LIGHT_FONT, from_='1', to='12', increment='1', relief='flat') self.exp_m_spinbox.config(textvariable=month, width='5', state='readonly') self.exp_m_spinbox.grid(column='1', padx='5', row='1') self.exp_d_spinbox = tk.Spinbox(self.exp_date_frame) day = tk.StringVar() self.exp_d_spinbox.config(font=BasePage.LIGHT_FONT, from_='1', to='31', increment='1', relief='flat') self.exp_d_spinbox.config(textvariable=day, width='5', state='readonly') self.exp_d_spinbox.grid(column='1', padx='5', row='2') self.year_lbl = tk.Label(self.exp_date_frame) self.year_lbl.config(font=BasePage.LIGHT_FONT, text='year:') self.year_lbl.grid(column='0', row='0', sticky='e') self.month_lbl = tk.Label(self.exp_date_frame) self.month_lbl.config(font=BasePage.LIGHT_FONT, text='month:') self.month_lbl.grid(column='0', row='1', sticky='e') self.day_lbl = tk.Label(self.exp_date_frame) self.day_lbl.config(font=BasePage.LIGHT_FONT, text='day:') self.day_lbl.grid(column='0', row='2', sticky='e') self.exp_date_frame.config(height='200', width='400') self.exp_date_frame.grid(column='1', padx='5', pady='5', row='9') # 'register' button self.register_btn = CustomButton(self.register_frame) self.register_btn.config(activebackground='#9a9a9a', background='#b1b1b1', font=BasePage.LIGHT_FONT, relief='flat') self.register_btn.config(text='register', width='10') self.register_btn.configure(command=self.on_register) self.register_btn.grid(column='0', columnspan='2', padx='5', pady='20', ipadx='5', ipady='3', row='10')