class BalanceWidget(tkinter.Frame): # is a widget that tells the user the balance and the sum def __init__(self, parent, **optional_arguments): # initialized the frame and sub frames tkinter.Frame.__init__(self, parent) self.colors = None self.table_edit_listener: TableEditListener = None self.field_count = default_field_count self.expenditures_set = False # set up and process the optional arguments self.title_font = default_title_font self.head_font = default_table_head_font self.entry_font = default_entry_font self.title_text = default_title_text self.table_widget_name = None self.process_optional_arguments(optional_arguments) # sets up the title label self.head_label: tkinter.Label = None self.setup_title_label() # set up the edit button self.edit_button = None self.done_button = None self.setup_edit_button() # sets up the balance table self.balance_table: TableWidget = None self.setup_balance_table() self.balance_table.hide_config_buttons() def add_listener(self, listener): self.table_edit_listener = listener def process_optional_arguments(self, optional_arguments): # processes optional arguments passed to the Balance Widget # store the label fonts if 'title_font' in optional_arguments: self.title_font = optional_arguments['title_font'] if 'head_font' in optional_arguments: self.head_font = optional_arguments['head_font'] if 'entry_font' in optional_arguments: self.entry_font = optional_arguments['entry_font'] # stores the name if 'name' in optional_arguments: self.title_text = optional_arguments['name'] if self.title_text == "Initial Balance": self.table_widget_name = "initialBalances" elif self.title_text == "Current Balance": self.table_widget_name = "currentBalances" def setup_title_label(self): # adds a title label above the table self.head_label = tkinter.Label(self) self.head_label.config(text=self.title_text, font=self.title_font) self.head_label.grid(row=0, column=0, sticky="E") def setup_edit_button(self): self.edit_button = tkinter.Button(self, text="Edit", command=lambda: self.edit_pressed()) self.done_button = tkinter.Button(self, text="Done", command=lambda: self.done_pressed()) self.edit_button.grid(row=0, column=1, sticky="W") def edit_pressed(self): if self.expenditures_set: self.balance_table.show_config_buttons() self.edit_button.grid_forget() self.done_button.grid(row=0, column=1, sticky="W") def done_pressed(self): self.balance_table.hide_config_buttons() self.done_button.grid_forget() self.edit_button.grid(row=0, column=1, sticky="W") def send_edit_to_database(self, table_name: str, row_index: int, values): # passes the row values to the listener to the DatabaseModel to be processed and stored in the database value_dict = {'amount': TableWidget.unformat_from_currency(values[1]), 'source': values[0]} self.table_edit_listener.send_edit_to_database(table_name, row_index, value_dict) def setup_balance_table(self): # invert_axis is True because the data will be added in cols self.balance_table = TableWidget(self, 2, self.field_count, table_name=self.table_widget_name, invert_axis=True, column_widths=default_field_col_widths, head_font=self.head_font, entry_font=self.entry_font, header_widths=default_header_widths) self.balance_table.add_listener(self) self.balance_table.set_header_values(['Source', 'Amount']) self.balance_table.grid(row=1, columnspan=2) def set_balances(self, balance_matrix: [[]]): # sets the values in the balances table given the balance matrix table = [] for row_index in range(len(balance_matrix)): values = [balance_matrix[row_index][1], TableWidget.format_as_currency(balance_matrix[row_index][2])] table.append(values) self.balance_table.load_table_data(table) self.expenditures_set = True def set_colors(self, color_dict: {str: str}): self.colors = color_dict self.update_colors() def update_colors(self): if self.colors is not None: self.config(bg=self.colors['bg_col']) self.head_label.config(bg=self.colors['bg_col'], fg=self.colors['text_col']) self.balance_table.set_colors(self.colors) self.edit_button.config(fg=self.colors['button_col']['button_text_col'], highlightbackground=self.colors['button_col']['button_bg_col'], activeforeground=self.colors['button_col']['button_pressed_text'], activebackground=self.colors['button_col']['button_pressed_bg']) self.done_button.config(fg=self.colors['button_col']['button_text_col'], highlightbackground=self.colors['button_col']['button_bg_col'], activeforeground=self.colors['button_col']['button_pressed_text'], activebackground=self.colors['button_col']['button_pressed_bg'])
class ExpenditureWidget(tkinter.Frame, TableEditListener): # is a widget that displays the expenditures in the database def __init__(self, parent, **optional_arguments): # initializes the frame and sub frames tkinter.Frame.__init__(self, parent) self.colors = None self.expenditures_set = False self.table_edit_listener: TableEditListener = None self.field_count = default_field_count # setup the default parameters then process the optional arguments self.title_font = default_title_font self.head_font = default_table_head_font self.entry_font = default_entry_font self.title_text = default_title_text self.process_optional_arguments(optional_arguments) # setup the title label self.head_label = None self.setup_title_label() # setup the edit button self.edit_button = None self.done_button = None self.setup_edit_button() # setup the expenditure table self.expenditure_table = None self.setup_expenditure_table() def add_listener(self, listener: TableEditListener): self.table_edit_listener = listener def process_optional_arguments(self, optional_arguments): # processes the optional arguments passed to the constructor if 'title_font' in optional_arguments: self.title_font = optional_arguments['title_font'] if 'head_font' in optional_arguments: self.head_font = optional_arguments['head_font'] if 'entry_font' in optional_arguments: self.entry_font = optional_arguments['entry_font'] if 'name' in optional_arguments: self.title_text = optional_arguments['name'] def setup_title_label(self): # adds a title label above the table self.head_label = tkinter.Label(self) self.head_label.config(text=self.title_text, font=self.title_font) self.head_label.grid(row=0, column=0, sticky="E") def setup_expenditure_table(self): # adds the expenditure table self.expenditure_table = TableWidget( self, 3, self.field_count + 1, table_name="expenditures", invert_axis=False, column_widths=default_field_col_widths, head_font=self.head_font, entry_font=self.entry_font) self.expenditure_table.hide_config_buttons() self.expenditure_table.add_listener(self) self.expenditure_table.set_header_values(['Amount', 'Name', 'Type']) self.expenditure_table.grid(row=1, columnspan=2) def setup_edit_button(self): self.edit_button = tkinter.Button(self, text="Edit", command=lambda: self.edit_pressed()) self.done_button = tkinter.Button(self, text="Done", command=lambda: self.done_pressed()) self.edit_button.grid(row=0, column=1, sticky="W") def edit_pressed(self): if self.expenditures_set: self.expenditure_table.show_config_buttons() self.edit_button.grid_forget() self.done_button.grid(row=0, column=1, sticky="W") def done_pressed(self): self.expenditure_table.hide_config_buttons() self.done_button.grid_forget() self.edit_button.grid(row=0, column=1, sticky="W") def set_expenditures(self, expenditure_matrix: [[]]): # passes the label values to the table to be inserted into the labels self.expenditures_set = True table = [] for row_index in range(len(expenditure_matrix)): values = [ TableWidget.format_as_currency( expenditure_matrix[row_index][1]), expenditure_matrix[row_index][2], expenditure_matrix[row_index][3] ] table.append(values) self.expenditure_table.load_table_data(table) def send_edit_to_database(self, table_name: str, row_index: int, values): # passes the row values to the listener to the DatabaseModel to be processed and stored in the database value_dict = { 'amount': TableWidget.unformat_from_currency(values[0]), 'name': values[1], 'type': values[2] } self.table_edit_listener.send_edit_to_database(table_name, row_index, value_dict) def set_colors(self, color_dict: {str: str}): self.colors = color_dict self.update_colors() def update_colors(self): if self.colors is not None: self.config(bg=self.colors['bg_col']) self.head_label.config(bg=self.colors['bg_col'], fg=self.colors['text_col']) self.expenditure_table.set_colors(self.colors) self.edit_button.config( fg=self.colors['button_col']['button_text_col'], highlightbackground=self.colors['button_col']['button_bg_col'], activeforeground=self.colors['button_col'] ['button_pressed_text'], activebackground=self.colors['button_col'] ['button_pressed_bg']) self.done_button.config( fg=self.colors['button_col']['button_text_col'], highlightbackground=self.colors['button_col']['button_bg_col'], activeforeground=self.colors['button_col'] ['button_pressed_text'], activebackground=self.colors['button_col'] ['button_pressed_bg'])
class DataVisualizer(tkinter.LabelFrame): def __init__(self, parent, **optional_arguments): tkinter.LabelFrame.__init__(self, parent, text=optional_arguments['text']) self.colors = None # setup default parameters and then process optional arguments self.field_count = default_field_count self.process_optional_arguments(optional_arguments) # setup the table showing initial balance, current balance, and expenditure totals self.totals_table: TableWidget = None self.load_total_amounts() # sets up a separator between the two tables separator = Separator(self) separator.grid(row=0, column=1, sticky="NS") # setup the table showing spending by category self.category_table: TableWidget = None self.load_spending_by_category() # setup the pie chart self.pie_chart = PieChart(self) self.pie_chart.grid(row=1, columnspan=3) self.load_table_data(None, None, None) def process_optional_arguments(self, optional_arguments): if 'field_count' in optional_arguments.keys(): self.field_count = optional_arguments['field_count'] def load_spending_by_category(self): self.category_table = TableWidget(self, 2, self.field_count, head_font=default_table_head_font, entry_font=default_entry_font, entry_justify_list=["right", "left"], head_justify_list=["right", "left"]) self.category_table.hide_config_buttons() self.category_table.set_header_values(['Category', 'Amount']) self.category_table.grid(row=0, column=2) def load_total_amounts(self): self.totals_table = TableWidget( self, 3, 1, entry_font=default_entry_font, entry_justify=["left"], head_justify_list=["right", "right", "right"], invert_axis=True) self.totals_table.hide_config_buttons() self.totals_table.set_header_values( ["Initial Total", "Expenditure Total", "Current Total"]) self.totals_table.grid(row=0, column=0) def load_table_data(self, expenditures_by_type: [[]], initial_balances, current_balances): self.load_expenditure_data(expenditures_by_type) self.load_totals_data(initial_balances, expenditures_by_type, current_balances) self.update_colors() def load_expenditure_data(self, expenditures_by_type: [[]]): if expenditures_by_type is not None and expenditures_by_type != []: labels = [] values = [] table = [] for row_index in range(len(expenditures_by_type)): labels.append(expenditures_by_type[row_index][0]) values.append(expenditures_by_type[row_index][1]) table.append([ expenditures_by_type[row_index][0], TableWidget.format_as_currency( expenditures_by_type[row_index][1]) ]) self.category_table.load_table_data(table) self.pie_chart.construct_pie_chart( labels, values, text_col=self.pie_chart.label_text_col) else: self.category_table.clear_labels() self.pie_chart.construct_empty_chart() def load_totals_data(self, initial_balances, expenditures_by_type, current_balances): if expenditures_by_type is not None and expenditures_by_type != []: total_spending = 0 for row in expenditures_by_type: total_spending += row[1] spending_text = TableWidget.format_as_currency(total_spending) self.totals_table.set_value(spending_text, 0, 1) else: self.totals_table.set_value("-", 0, 1) if initial_balances is not None and initial_balances != []: total_initial_balance = 0 for balance in initial_balances: total_initial_balance += balance[2] initial_text = TableWidget.format_as_currency( total_initial_balance) self.totals_table.set_value(initial_text, 0, 0) else: self.totals_table.set_value("-", 0, 0) if current_balances is not None and current_balances != []: total_current_balance = 0 for balance in current_balances: total_current_balance += balance[2] current_text = TableWidget.format_as_currency( total_current_balance) self.totals_table.set_value(current_text, 0, 2) else: self.totals_table.set_value("-", 0, 2) def set_colors(self, color_dict: {str: str}): self.colors = color_dict self.update_colors() def update_colors(self): if self.colors is not None: self.config(bg=self.colors['bg_col'], fg=self.colors['text_col']) self.pie_chart.set_colors(self.colors['pie_chart_colors']) self.category_table.set_colors( self.colors['category_table_colors']) self.totals_table.set_colors(self.colors['totals_table_colors'])