Dashboard

Expense Dashboard

Above UI we are going to create by Tkinter for Dashboard in our Expense management system desktop application. For that first we create a window and configure its title, size and color. Here we are creating a window at Top Level so we can add this window at the top of any other window.

    
        root = tk.Toplevel()
        self.root = tk.Toplevel()
        self.root.title("Dashboard")
        self.root.configure(bg="#585858")
        self.root.resizable(width=0, height=0)
        win_width = 1280
        print(1280 * 56.25 / 100)
        win_height = int(1280 * 56.25 / 100)

        self.root.geometry(str(win_width) + "x" + str(win_height))
        Util.center(self.root)

        login_root = None

        if args.__sizeof__() > 0:
            login_root = args[0]

        # Get screen size
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        Util.set_font_size(screen_width, screen_height, win_width, win_height)

        width = win_width * 96 / 100
        height = win_height * 94 / 100
    

After creation of window, we will add a canvas background.

    
    bg_canvas = RoundBackgroundFrame(self.root, width, height, padding, cornerradius, color, "#585858")
    bg_canvas.place(width=width, height=height, x=win_width / 2 - width / 2, y=win_height / 2 - height / 2)

    class RoundBackgroundFrame(tk.Canvas):

        def __init__(self, parent, width, height, padding, cornerradius, color, bg):
            tk.Canvas.__init__(self, parent, borderwidth=0, relief="flat", highlightthickness=0, bg=bg)
            self.width = width
            self.height = height
            self.padding = padding
            self.cornerradius = cornerradius
            self.color = color
  
            original = Image.open("images/corner.png")
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_r_t = ImageTk.PhotoImage(resized)

            original = original.rotate(90, expand=0)
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_l_t = ImageTk.PhotoImage(resized)

            original = original.rotate(90, expand=0)
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_l_b = ImageTk.PhotoImage(resized)

            original = original.rotate(90, expand=0)
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_r_b = ImageTk.PhotoImage(resized)

            self.shape(width, height, padding, cornerradius, color)
            (x0, y0, x1, y1) = self.bbox("all")
            width = (x1 - x0)
            height = (y1 - y0)
            self.configure(width=width, height=height)

        def shape(self, width, height, padding, cornerradius, color):
            self.create_polygon((padding, height - cornerradius - padding, padding, cornerradius + padding,
                                padding + cornerradius, padding, width - padding - cornerradius, padding,
                                width - padding, cornerradius + padding, width - padding,
                                height - cornerradius - padding, width - padding - cornerradius, height - padding,
                                padding + cornerradius, height - padding), fill=color, outline=color)

            self.create_image(width - self.image_r_t.width(), 0, image=self.image_r_t, anchor=NW)
            self.create_image(0, 0, image=self.image_l_t, anchor=NW)
            self.create_image(0, height - self.image_l_b.width(), image=self.image_l_b, anchor=NW)
            self.create_image(width - self.image_r_b.width(), height - self.image_r_b.width(), image=self.image_r_b, anchor=NW)


    

Once your window is created with custom background, we will create a frame with some reduced dimensions.

    
        width = width * 98 / 100
        height = height * 96 / 100
        base_frame = Frame(self.root, width=width, height=height, bg=color)
        bg_canvas.create_window(width / 100, height * 2 / 100, anchor=NW, window=base_frame)

        add_heading_label(base_frame, color, AppConstant.FONT_SIZE, height, width)

        #Add Heading
        def add_heading_label(base_frame, color, font_size, height, width):
            label_heading = Label(base_frame, text="Expense Management System", anchor=CENTER, bg=color,
                                    font=("Lucida Grande", font_size + 6))
            label_heading.place(width=width * 90 / 100, height=height * 7 / 100, x=width * 5 / 100, y=height * 0.5 / 100)
            label_dashboard = Label(base_frame, text="Dashboard", anchor=CENTER, bg=color,
                                    font=("Lucida Grande", font_size - 6))
            label_dashboard.place(width=width * 90 / 100, height=height * 4 / 100, x=width * 5 / 100, y=height * 8 / 100)
        #Line
        add_line_border(base_frame, color, AppConstant.FONT_SIZE, height, width, self.root, login_root)


        def add_line_border(base_frame, color, font_size, height, width, root, login_root):
            line_canvas = Canvas(base_frame, bg=color, borderwidth=0, relief="flat", highlightthickness=0)
            line_canvas.place(width=width - 4, height=5, x=2, y=height * 13 / 100)
            line_canvas.create_line(0, 0, width, 0, fill="#787878")
            line_canvas2 = Canvas(base_frame, bg=color, borderwidth=0, relief="flat", highlightthickness=0)
            line_canvas2.place(width=width - 4, height=5, x=2, y=height * 20 / 100)
            line_canvas2.create_line(0, 0, width, 0, fill="#787878")
            frame_name = Frame(base_frame, bg=color)
            frame_name.place(width=width * 94 / 100, x=width * 3 / 100, y=height * 14.3 / 100)
            frame_name.grid_propagate(False)
            label_name = Label(frame_name, font=("Lucida Grande", font_size - 6), text="Hi, " + AppConstant.STAFF_NAME,
                                anchor=CENTER,bg=color)
            label_name.pack(side="left", fill=None, expand=False)

            button_logout = RoundedButton(frame_name, 130, 130 / 4.2, color, "images/button37.png", "Logout",font=("Lucida Grande",
                                             font_size - 6),command=lambda: logout_click(root, login_root))
            button_logout.pack(side="right", fill=None, expand=False)

        #Add Action Frame 
        add_action_frame(base_frame, color, AppConstant.FONT_SIZE, height, width, self.root)

        def add_action_frame(base_frame, color, font_size, height, width, root):
            lfw = width * 94 / 100
            lfbw = lfw * 10 / 100
    
            button_font = ("Lucida Grande", font_size - 6)
            labelframe = LabelFrame(base_frame, text="Actions",
                                    font=font.Font(family="Lucida Grande", size=13, weight='normal'),
                                    pady=lfw * 2 / 100, padx=lfw * 2 / 100, bg=color)
            labelframe.place(width=lfw - lfw / 3, height=lfbw + lfw * 6 / 100, x=width / 5 - 20, y=height * 70 / 100)

            button_vacant_room = AddActionFrame(labelframe, lfbw, lfbw, color, "Add Money",
                                                    button_font, "images/add_money.png",
                                                    command=lambda: credit_click(root))
            button_allote_room = AddActionFrame(labelframe, lfbw, lfbw, color, "Expense",
                                                    button_font, "images/transaction.png", command=lambda: expense_click(root))
            button_add_money = AddActionFrame(labelframe, lfbw, lfbw, color, "Statement",
                                                    button_font, "images/statement.png", command=lambda: statement_click(root))

            button_add_money.grid(row=0, column=3, padx=(lfbw * 55 / 100))
            button_allote_room.grid(row=0, column=2, padx=(lfbw * 55 / 100))
            button_vacant_room.grid(row=0, column=1, padx=(lfbw * 55 / 100))


            self.root.attributes('-alpha', 0.0)
            Util.center(self.root)
            self.root.attributes('-alpha', 1.0)
        
    

Here is simple RoundedButton Class put that code in seperate class "Util" because we are going to use it multiple time in our project.

    
class RoundedButton(tk.Canvas):

    def __init__(self, parent, width, height, bg, icon_path, text, font, command=None):
        tk.Canvas.__init__(self, parent, borderwidth=0, relief="flat", highlightthickness=0, bg=bg)
        self.command = command
        self.width = width
        self.height = height
        self.text = text
        self.font = font

        original = Image.open(icon_path)
        resized = original.resize((int(width), int(height)), Image.ANTIALIAS)
        self.image = ImageTk.PhotoImage(resized)

        self.shape(width, height)
        (x0, y0, x1, y1) = self.bbox("all")
        width = (x1 - x0)
        height = (y1 - y0)
        self.configure(width=width, height=height)
        self.bind("", self._on_press)
        self.bind("", self._on_release)

    def shape(self, width, height):
        self.create_image(0, 0, image=self.image, anchor=NW)
        self.create_text(width / 2, height / 2, anchor=CENTER, font=self.font, text=self.text, fill="#FFFFFF")

    def shape_press(self, width, height):
        self.create_image(0, 0, image=self.image, anchor=NW)
        self.create_text(width / 2, height / 2, anchor=CENTER, font=self.font, text=self.text, fill="#000000")

    def _on_press(self, event):
        self.configure(relief="sunken")
        self.delete("all")
        # self.update()
        self.shape_press(self.width, self.height)

    def _on_release(self, event):
        self.configure(relief="raised")
        self.delete("all")
        self.shape(self.width, self.height)
        if self.command is not None:
            self.command()
    
    

We have created a Database named "expense.db", lets connect that database and put that code in seperate class "Util" because we are going to use it multiple time in our project.

    
    class Util:

        @staticmethod
        def connect_db():
            db = None
            try:
                db = sql.connect("expense.db")
                return db
            except sql.Error as error:
                print("Failed to insert data into sqlite table", error)
                

So we are done with all frame and connect from database now draw Deskframe UI .

    
    self.total_bal = 0
    self.total_expense = 0
    self.total_credit = 0

    self.food_expense = 200
    self.health_expense = 400
    self.insurance_expense = 500
    self.travel_expense = 200
    self.other_expense = 400


    #Account data
    self.get_account_table_data()

    #Food data   
    self.get_food_data()

    #Insurance data
    self.get_insurance_data()

    #Travle Data
    self.get_travel_data()

    #Health data
    self.get_health_data()

    #Other data
    self.get_other_data()
    
    

Here is logic for get Account data from database .

    
    def get_account_table_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()

        old_balance = 0
        old_credit = 0
        old_debit = 0

        cursor.execute('SELECT balance FROM statement where user_id IS ?', (AppConstant.USER_ID,))
        sql_outputTwo = cursor.fetchall()
        count = 0
        for amountdata in sql_outputTwo:
            if count == len(sql_outputTwo) - 1:
                for items in amountdata:
                    old_balance = old_balance + items
            count += 1

        
        self.total_bal = old_balance
        cursor.execute(
            'SELECT debit FROM statement where user_id IS ?', (AppConstant.USER_ID,))
        sql_output = cursor.fetchall()

        for data in sql_output:
            for items in data:
                if items != "":
                    old_debit = old_debit + items

        self.total_expense = old_debit

        cursor.execute('SELECT credit FROM statement where user_id IS ?', (AppConstant.USER_ID,))
        sql_outputTwo = cursor.fetchall()

        for data in sql_outputTwo:
            for items in data:
                if items != "":
                    old_credit = old_credit + items

        self.total_credit = old_credit
    

Here is logic for get Food data from database.

    
    def get_food_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()

        
        old_data = 0
        category = "Food"

        cursor.execute('SELECT debit FROM statement where user_id IS ? and category IS ?', (AppConstant.USER_ID, category))
        sql_output = cursor.fetchall()

        for data in sql_output:
            for items in data:
                if items != "":
                    old_data = old_data + items


        self.food_expense = old_data

   

    def get_health_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()

        
        old_data = 0
        category = "Health Care"

        cursor.execute('SELECT debit FROM statement where user_id IS ? and category IS ?', (AppConstant.USER_ID, category))
        sql_output = cursor.fetchall()

        for data in sql_output:
            for items in data:
                if items != "":
                    old_data = old_data + items

        self.health_expense = old_data

        

Here is logic for get Insurance data from database.

    
    def get_insurance_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()

       
        old_data = 0
        category = "Insurance"

        cursor.execute('SELECT debit FROM statement where user_id IS ? and category IS ?', (AppConstant.USER_ID, category))
        sql_output = cursor.fetchall()

        for data in sql_output:
            for items in data:
                if items != "":
                    old_data = old_data + items

        self.insurance_expense = old_data

        

Here is logic for get Travel data from database.

    
    def get_travel_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()

        
        old_data = 0
        category = "Travel"

        cursor.execute('SELECT debit FROM statement where user_id IS ? and category IS ?', (AppConstant.USER_ID, category))
        sql_output = cursor.fetchall()

        for data in sql_output:
            for items in data:
                if items != "":
                    old_data = old_data + items

        self.travel_expense = old_data

        

Here is logic for get Othere data from database.

    
    def get_other_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()

        old_data = 0
        category = "Other"

        cursor.execute('SELECT debit FROM statement where user_id IS ? and category IS ?', (AppConstant.USER_ID, category))
        sql_output = cursor.fetchall()

        for data in sql_output:
            for items in data:
                if items != "":
                    old_data = old_data + items

        self.other_expense = old_data
        
    

Here's logic for Added data frame is plot into pie chart .

    
    my_dpi = 100
    fig = plt.figure(figsize=(6, 6), dpi=my_dpi)
    fig.set_size_inches(4, 3)

    labels = 'Food', 'Health', 'Insurance', 'Travel', 'Other'
    sizes = [self.food_expense, self.health_expense, self.insurance_expense,
                      self.travel_expense, self.other_expense]
    colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue', 'green']
    explode = (0, 0, 0, 0, 0)  # explode 1st slice (Ireland), makes it more prominent

    # Plot pie chart
    plt.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90)

    plt.axis('equal')  # creates the pie chart like a circle

    canvasbar = FigureCanvasTkAgg(fig, master=self.root)
    canvasbar.draw()
    canvasbar.get_tk_widget().place(relx=0.2, rely=0.45, anchor=CENTER)  # show the barchart on the ouput window

    fig2 = plt.figure(figsize=(6, 6), dpi=my_dpi)
    fig2.set_size_inches(4, 3)

    labelstwo = 'Credit', 'Expense'
    sizestwo = [self.total_credit, self.total_expense]
    colorstwo = ['coral', 'teal']
    explodetwo = (0, 0)  # explode 1st slice (Ireland), makes it more prominent

    # Plot pie chart
    plt.pie(sizestwo, explode=explodetwo, labels=labelstwo, colors=colorstwo, autopct='%1.1f%%', shadow=True, startangle=140)

    plt.axis('equal')  # creates the pie chart like a circle

    canvasbar = FigureCanvasTkAgg(fig2, master=base_frame)
    canvasbar.draw()
    canvasbar.get_tk_widget().place(relx=0.5, rely=0.45, anchor=CENTER)  # show the barchart on the ouput window

    fig3 = plt.figure(figsize=(6, 6), dpi=my_dpi)
    fig3.set_size_inches(4, 3)

    labelsthree = 'Credit', 'Expense', 'Balance'
    sizesthree = [self.total_credit, self.total_expense, self.total_bal]
    colorsthree = ['#7582c0', '#b2aa7e', 'green']
    explodethree = (0, 0, 0)  # explode 1st slice (Ireland), makes it more prominent

    # Plot pie chart
    plt.pie(sizesthree, explode=explodethree, labels=labelsthree, colors=colorsthree, autopct='%1.1f%%', shadow=True,startangle=140)

    plt.axis('equal')  # creates the pie chart like a circle

    canvasbar = FigureCanvasTkAgg(fig3, master=base_frame)
    canvasbar.draw()
    canvasbar.get_tk_widget().place(relx=0.8, rely=0.45, anchor=CENTER)  # show the barchart on the ouput window


        

When Click Action UI to perform Tasks.

    
def statement_click(root):
    root.withdraw()
    Statement(root)


def expense_click(root):
    root.withdraw()
    Expense(root)


def credit_click(root):
    root.withdraw()
    Credit(root)
    

At last here is logic for logout from Application when Click "LOGOUT" Button

    
def logout_click(root, login_root):
    db = Util.connect_db()
    cursor = db.cursor()
    key_is_logined = "IS_LOGIN"
    cursor.execute("UPDATE system_setting SET value = ? WHERE key = ?",
                   (False, key_is_logined,))
    db.commit()
    root.destroy()
    Util.center(login_root)
    login_root.deiconify()         
                       

Programmer Mirta is for learning and training. Projects might be simple to improve learning. Projects are constantly reviewed to avoid errors, but we cannot assure full correctness of all content. While using Programmer Mitra, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 2021 by Programmer Mitra. All Rights Reserved.