Esempio n. 1
0
 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()
Esempio n. 2
0
 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()
Esempio n. 3
0
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')
Esempio n. 4
0
    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')
Esempio n. 5
0
 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')
Esempio n. 6
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')
Esempio n. 7
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')
Esempio n. 8
0
 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')