Expense

Expense

Above UI we are going to create by Tkinter for Expense for exesting User 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()

    root.title("Expense")

    root.configure(bg="#585858")
    root.resizable(width=0, height=0)
    win_width = 1280
    win_height = int(1280 * 56.25 / 100)
    root.geometry(str(win_width) + "x" + str(win_height))

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

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

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

    
    bg_canvas = RoundBackgroundFrame(root, self.width, self.height, padding, cornerradius, self.color, "#585858")
    bg_canvas.place(width=self.width, height=self.height, x=win_width / 2 - self.width / 2,y=win_height / 2 - self.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 frames with some reduced dimensions.

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

    #Add Heading
    add_heading_label(self.base_frame, self.color, AppConstant.FONT_SIZE, self.height, self.width, root)

    def add_heading_label(base_frame, color, font_size, height, width, root):
        label_heading = Label(base_frame, text="Expense", 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)


    #Add Line
    add_line_border(self.base_frame, self.color, self.height, self.width)

    def add_line_border(base_frame, color, height, width):
        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 * 10 / 100)
        line_canvas.create_line(0, 0, width, 0, fill="#787878")


    button_font = add_action_frame(self.base_frame, self.color, self.height, self.width)

    #Add Action Frame
    def add_action_frame(base_frame, color, height, width):
        afw = width * 0.35
        afh = height * 0.87
        button_font = ("Lucida Grande", AppConstant.FONT_SIZE - 6)
        label_frame_action = LabelFrame(base_frame, text="Actions", font=button_font, pady=afw * 2 / 100,
                                    padx=afw * 2 / 100, bg=color)
        label_frame_action.place(width=afw, height=afh, x=0, y=height * 0.12)
        return button_font


    #ADD History
    add_history_frame(self.base_frame, button_font, self.color, self.height, self.width)
    
    def add_history_frame(base_frame, button_font, color, height, width):
        sfw = width * 0.638
        sfh = height * 0.87
        label_frame_search = LabelFrame(base_frame, text="History", font=button_font, pady=sfw * 2 / 100,
                                    padx=sfw * 2 / 100, bg=color)
        label_frame_search.place(width=sfw, height=sfh, x=width * 0.36, y=height * 0.12)

        
    

So we are done with all frame and Now we will draw Expanse UI.

    
    # Add Expense UI

    self.amount_entrylable = CustomEntrySimple(self.base_frame, 300, 60, ("Lucida Grande", AppConstant.FONT_SIZE - 6),
                                                   self.color,"Amount")
    self.amount_entrylable.place(x=50, y=150)

    self.cat_label = Label(self.base_frame, text="Category", font=('Lucida Grande', AppConstant.FONT_SIZE - 6),
                               background=self.color, foreground="black", anchor=W)
    self.cat_label.place(width=300, height=20, x=50, y=230)

    self.category_entry_var = tk.StringVar()
    self.category_entry = Combobox(self.base_frame, state="readonly", justify="center",textvariable=self.category_entry_var,
                                       font=("Lucida Grande", AppConstant.FONT_SIZE))
    self.category_entry['values'] = ("Select", "Food", "Health Care", "Insurance", "Travel", "Other")
    self.category_entry.current(0)

    self.category_entry.place(x=50, y=260, width=300)

    self.des_label = Label(self.base_frame, text="Description", font=('Lucida Grande', AppConstant.FONT_SIZE - 6),
                               background=self.color, foreground="black", anchor=W)
    self.des_label.place(width=300, height=20, x=50, y=310)

      
    self.inputtxt = Text(self.base_frame, highlightthickness=2, bg="white", fg="black",
                             font=('Lucida Grande', AppConstant.FONT_SIZE - 6))
    self.inputtxt.place(x=50, y=340, height=200, width=300)

    self.add_expanse_button()
        

    

Here is our class CustomEntrySimple:

    
    class CustomEntrySimple(tk.Canvas):

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

        self.width = width
        self.height = height
        self.color = color

        self.create_text(6, 5, anchor=W, font=font, text=text, fill="#000000")
        self.create_line(5, height - 8, width - 5, height - 8, fill="#808080")

        frame = Frame(parent,width=width * 95 / 100, height=height * 50 / 100, bg=color)
        self.entry = Entry(frame, bg=color, bd=0, highlightthickness=0, font=font)

        self.entry.place(relwidth=1, relheight=1, x=0, y=0)

        self.create_window(6, height / 2 - (height * 50 / 100) / 2, anchor=NW, window=frame)
        self.update()
        

Create a "Add Expense" button and show it.

    
    def add_expanse_button(self):
        self.add_button = RoundedButton(self.base_frame, 250, 130 / 2.56, self.color, "images/button33.png",
                                        "Add Expense",
                                        font=("Lucida Grande", AppConstant.FONT_SIZE - 2),
                                        command=self.payment_click)
        self.add_button.place(x=75, y=self.height * 0.88)

Now draw History UI with table use of "Treeview" of Tkinter

    
     #user Name
    self.label_name = Label(self.base_frame, font=("Lucida Grande", AppConstant.FONT_SIZE - 3), text="Name :
                                                     " + AppConstant.USER_NAME,anchor=CENTER,bg=self.color)
       
    self.label_name.place(x=self.width / 2 - 150, y=self.height * 0.20)

    #Table

    style = ttk.Style()
    style.layout("Custom.Treeview.Heading", [("Custom.Treeheading.cell", {'sticky': 'nswe'}),
                        ("Custom.Treeheading.border", {'sticky': 'nswe', 'children': [
                        ("Custom.Treeheading.padding", {'sticky': 'nswe', 'children': [
                        ("Custom.Treeheading.image", {'side': 'right', 'sticky': ''}),
                        ("Custom.Treeheading.text", {'sticky': 'we'})
                        ]})
                        ]}),
                ])
    style.map("Custom.Treeview", background=[('selected', '#ad51ad')])
    style.configure("Custom.Treeview", highlightthickness=0, bd=0, font=('Calibri', 11), rowheight=30)

    self.table_heading()

    self.tree = ttk.Treeview(self.base_frame, height=13, show='tree', style="Custom.Treeview")
    self.tree.tag_configure('odd', background='#ffbfff', foreground="#000000", )
    self.tree.tag_configure('even', background='#FFFFFF', foreground="#000000", )
    self.tree.place(x=self.width / 2 - 160, y=230)
    self.tree["columns"] = "1", "2", "3", "4", "5"
    self.tree.column("#1", width=140)
    self.tree.column("#0", width=0)
    self.tree.column("#2", width=140)
    self.tree.column("#3", width=140)
    self.tree.column("#4", width=140)
    self.tree.column("#5", width=170)

    self.update_table_data()

    vsb = ttk.Scrollbar(self.base_frame, orient="vertical", command=self.tree.yview)
    vsb.place(x=self.width - 24, y=230, height=395)

    self.tree.configure(yscrollcommand=vsb.set)
    self.tree.column("#1", anchor=tk.CENTER)
    self.tree.column("#2", anchor=tk.CENTER)
    self.tree.column("#3", anchor=tk.CENTER)
    self.tree.column("#4", anchor=tk.CENTER)
    self.tree.column("#5", anchor=tk.CENTER)


    self.tree.heading("#1", text="User Id", anchor=tk.CENTER)
    self.tree.heading("#2", text="Name")
    self.tree.heading("#3", text="Contact No")
    self.tree.heading("#4", text="Email Id")
    self.tree.heading("#5", text="address")



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

We have created our own heading for History table, lets configure it.

    

    def table_heading(self):

        heading_y = 200

        bg_color = "#800080"
        canvas = Canvas(self.base_frame, width=740, height=30)
        canvas.create_rectangle(0, 0, 740, 30, fill=bg_color)
        canvas.place(x=self.width / 2 - 163, y=heading_y - 3)



        user_name_label = Label(self.base_frame, text="Debit",
                                font=('Calibri', AppConstant.FONT_SIZE - 3, 'bold'), background=bg_color,
                                foreground="white")
        user_name_label.place(x=self.width / 2 - 125, y=heading_y, width=120)
        user_name_label.configure(anchor="center")

        user_email_label = Label(self.base_frame, text="Debit Date",
                                font=('Calibri', AppConstant.FONT_SIZE - 3, 'bold'), background=bg_color,
                                foreground="white")
        user_email_label.place(x=self.width / 2 + 10, y=heading_y, width=130)
        user_email_label.configure(anchor="center")

        user_contact_label = Label(self.base_frame, text="Balance",
                                    font=('Calibri', AppConstant.FONT_SIZE - 3, 'bold'), background=bg_color,
                                    foreground="white")
        user_contact_label.place(x=self.width / 2 + 150, y=heading_y, width=120)
        user_contact_label.configure(anchor="center")

        user_adds_label = Label(self.base_frame, text="Category",
                                font=('Calibri', AppConstant.FONT_SIZE - 3, 'bold'), background=bg_color,
                                foreground="white")
        user_adds_label.place(x=self.width / 2 + 290, y=heading_y, width=140)
        user_adds_label.configure(anchor="center")

        user_id_label = Label(self.base_frame, text="Description",
                                font=('Calibri', AppConstant.FONT_SIZE - 3, 'bold'), background=bg_color,
                                foreground="white")
        user_id_label.place(x=self.width / 2 + 430, y=heading_y, width=120)
        user_id_label.configure(anchor="center")

        

UI part complete, Here is logic of fetch data from database and update table.

    
    def update_table_data(self):
        conn = Util.connect_db()
        cursor = conn.cursor()
        cursor.execute('SELECT debit, debit_date, balance, category, description FROM statement where user_id IS ?', (AppConstant.USER_ID,))
        self.sql_output = cursor.fetchall()

        count = 0
        for data in self.sql_output:
            if count % 2 == 0:
                self.tree.insert('', 'end', values=data, tags=('even',))
            else:
                self.tree.insert('', 'end', values=data, tags=('odd',))
            count = count + 1
            
            

Here is logic of add expense money add in expense sheet when click "Add Expense".

    
    def payment_click(self):

        conn = Util.connect_db()
        cursor = conn.cursor()

        var = ""
        amount = ""
        category = ""
        description = ""
        credit_date = datetime.today().strftime('%Y-%m-%d')
        debit_date = datetime.today().strftime('%Y-%m-%d')
        acc_no = AppConstant.USER_ID
        balance = 0
        old_credit = 0
        old_debit = 0
        is_amount_entered = False
        is_category_entered = False
        is_descrip_entered = False


        if self.amount_entrylable.entry.get() == "" or self.amount_entrylable.entry.get() == " ":
            var += " Amount Should not be empty, "
        else:
            amount = self.amount_entrylable.entry.get()
            is_amount_entered = True

        if self.category_entry_var.get() == "Select":
            var += " Category Should not be empty, "
        else:
            category = self.category_entry_var.get()
            is_category_entered = True

        if self.inputtxt.get(1.0, "end-1c") == "" or self.inputtxt.get(1.0, "end-1c") == " ":
            var += " Description Should not be empty, "
        else:
            description = self.inputtxt.get(1.0, "end-1c")
            is_descrip_entered = True


        if is_amount_entered and is_descrip_entered and is_category_entered:
            cursor.execute('SELECT  credit FROM statement where user_id IS ?', (acc_no,))
            sql_output = cursor.fetchall()

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


            cursor.execute('SELECT debit FROM statement where user_id IS ?', (acc_no,))
            sql_output = cursor.fetchall()

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


    

            entered_amount = int(amount)

            old_balance = (old_credit - old_debit)
            print(old_balance)

            if old_balance >= entered_amount:

                balance = old_balance - entered_amount


                credit = ""
                credit_date = ""

                cursor.execute('INSERT INTO statement(user_id, credit, debit, balance, credit_date, debit_date, description, category) VALUES(?,?,?,?,?,?,?,?)',
                                (acc_no, credit, amount, balance, credit_date, debit_date, description, category))

                conn.commit()

                messagebox.showinfo("Success", "Success debited successfully, ")
                self.amount_entrylable.entry.delete(0, 'end')
                self.category_entry.current(0)
                self.inputtxt.delete('1.0', END)
                self.tree.delete(*self.tree.get_children())
                self.update_table_data()

            else:
                print(var)
                messagebox.showerror("showerror", "Insufficient Balance.")

        else:
            print(var)
            messagebox.showerror("showerror", var)
    

At last we will manage closing of window

    
    def on_closing():
                        
        root.destroy()
        args[0].deiconify()
                       
    root.protocol("WM_DELETE_WINDOW", on_closing)
                       

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.