0

I'm making a financial program for myself and I'm stuck at this point for whole day and I've tried so many solutions that didn't work for me so I decided to ask for help.

Edit: I connected program with database and in this part I'm trying to make "payment in installments" - to check that I payed item for that month. I extracted lines of code that is required for this problem and renamed most variables so it can be at least a bit understandable for you guys, since I'm beginner and I'm making programs for myself in free time sorry for mistakes.

Pain point: If selected payment from list is on last installment (for example 4/4) I want to make function and bind it on that same button "Pay selected" that will destroy exactly button which holds label with name of payment. I managed to delete it from database, but button don't disappear until program is restarted since it won't be created because it's not in database anymore, but I want to delete it immediately after last payment is done. It's really complicated for me to explain this problem since I'm not that good English speaker. I posted video of my original program so you guys can understand what I want to accomplish.

Link for video: https://streamable.com/5veg32

Code:

from tkinter import *
import sqlite3

conn = sqlite3.connect('Financije.db')
c = conn.cursor()

c.execute('''CREATE TABLE IF NOT EXISTS PRIMANJA
             ([ID] INTEGER PRIMARY KEY,[Opis] text, [Iznos] integer, [Datum] text)''')

c.execute('''CREATE TABLE IF NOT EXISTS OTPLATA_NA_RATE
             ([ID] INTEGER PRIMARY KEY,[Opis] text, [Iznos_rate] integer, [Broj_rata] text, [Datum_kupnje] text)''')

c.execute('''CREATE TABLE IF NOT EXISTS SKIDANJE_S_RAČUNA
             ([ID] INTEGER PRIMARY KEY,[Opis] text, [Iznos] integer, [Date] text)''')

c.execute('''CREATE TABLE IF NOT EXISTS KASICA
             ([ID] INTEGER PRIMARY KEY, [Stanje_računa] integer)''')

c.execute('SELECT * FROM KASICA')
check = c.fetchone()
if check is None:
    c.execute('INSERT INTO KASICA VALUES (?, ?);', (None, 0.0))
c.execute('SELECT * FROM OTPLATA_NA_RATE')
check = c.fetchone()
if check is None:
    c.execute('INSERT INTO OTPLATA_NA_RATE VALUES (?, ?, ?, ?, ?);', (None, "Test1", 600, "0/2", "12/08/2021"))
    c.execute('INSERT INTO OTPLATA_NA_RATE VALUES (?, ?, ?, ?, ?);', (None, "Test2", 600, "0/3", "12/08/2021"))
    c.execute('INSERT INTO OTPLATA_NA_RATE VALUES (?, ?, ?, ?, ?);', (None, "Test3", 600, "0/4", "12/08/2021"))
    c.execute('INSERT INTO OTPLATA_NA_RATE VALUES (?, ?, ?, ?, ?);', (None, "Test4", 600, "0/5", "12/08/2021"))
    c.execute('INSERT INTO OTPLATA_NA_RATE VALUES (?, ?, ?, ?, ?);', (None, "Test5", 600, "0/6", "12/08/2021"))
# Save (commit) the changes
conn.commit()


class FinancialCalc:
    def __init__(self, master):
        self.lista_opisa_rata = list()

        # Start screen define:
        self.master = master
        self.master.title("Test")
        self.master.geometry("300x300")
        self.master.resizable(False, False)

        self.otplata_na_rate_button = Button(master, text="payment in installments",
                                             command=self.otplata_na_rate_deiconify)
        self.otplata_na_rate_button.place(x=95, y=155)

        #   Otplata_na_rate TopLevel - definiranje
        self.onr_top = Toplevel()
        self.onr_top.withdraw()
        self.onr_top.protocol("WM_DELETE_WINDOW", lambda: (self.master.deiconify(), self.onr_top.withdraw()))
        self.onr_top.title("Test")
        self.onr_label = Label(self.onr_top, text="List of payments:", font=("Helvetica", 11, "underline", "bold"))
        self.onr_opis_label = Label(self.onr_top, text="Payment description:", font=("Helvetica", 11, "underline", "bold"))
        self.onr_oznacena_rata_label = Label(self.onr_top, text="Installment description:", font=("Helvetica", 11))
        self.onr_broj_rata_label = Label(self.onr_top, text="Installments:", font=("Helvetica", 11))
        self.onr_iznos_rate_label = Label(self.onr_top, text="Value:", font=("Helvetica", 11))
        self.onr_datum_isplate_label = Label(self.onr_top, text="Final payment:", font=("Helvetica", 11))

        self.onr_oznacena_rata_txt = Label(self.onr_top, text="", font=("Helvetica", 11))
        self.onr_broj_rata_txt = Label(self.onr_top, text="", font=("Helvetica", 11))
        self.onr_iznos_rate_txt = Label(self.onr_top, text="", font=("Helvetica", 11))
        self.onr_datum_isplate_txt = Label(self.onr_top, text="", font=("Helvetica", 11))

        self.onr_pl_pojed_btn = Button(self.onr_top, text="Pay selected", command=self.pay_selected)

    def pay_selected(self):
        p_description = self.onr_oznacena_rata_txt.cget("text")
        num_of_installments = self.onr_broj_rata_txt.cget("text").split("/")
        if int(num_of_installments[0])+1 >= int(num_of_installments[1]):
            c.execute('DELETE FROM OTPLATA_NA_RATE WHERE Opis = ?', (p_description,))
            conn.commit()

        else:
            target_3_novo = str(int(num_of_installments[0])+1) + "/" + num_of_installments[1]
            c.execute('UPDATE OTPLATA_NA_RATE SET Broj_rata = ? WHERE Opis = ?', (target_3_novo, p_description,))
            conn.commit()
            self.get_description(p_description)

    def get_description(self, opis):
        c.execute('SELECT * FROM OTPLATA_NA_RATE WHERE Opis=?;', (opis,))
        for x in c:
            dodaj_godinu = 0
            self.onr_oznacena_rata_txt.config(text=x[1])
            self.onr_iznos_rate_txt.config(text=str(x[2]) + " kn")
            self.onr_broj_rata_txt.config(text=x[3])
            datum_racunanje = int(x[4].split("/")[1]) + int(x[3].split("/")[1])
            while datum_racunanje > 12:
                datum_racunanje -= 12
                dodaj_godinu += 1
            datum_isplate = str(datum_racunanje) + "/" + str(int(x[4].split("/")[2])+dodaj_godinu)
            self.onr_datum_isplate_txt.config(text=datum_isplate)

    def otplata_na_rate_deiconify(self):
        # Otplata_na_rate TopLevel - define window:
        self.master.withdraw()
        self.onr_top.deiconify()
        self.onr_label.grid(row=0, columnspan=3, sticky="NESW")
        self.onr_opis_label.grid(row=0, column=5, sticky="NESW")
        self.onr_oznacena_rata_label.grid(row=1, column=4, padx=5, sticky="E")
        self.onr_broj_rata_label.grid(row=2, column=4, padx=5, sticky="E")
        self.onr_iznos_rate_label.grid(row=3, column=4, padx=5, sticky="E")
        self.onr_datum_isplate_label.grid(row=4, column=4, padx=5, sticky="E")

        self.onr_oznacena_rata_txt.grid(row=1, column=5, padx=5, sticky="NSWE")
        self.onr_broj_rata_txt.grid(row=2, column=5, padx=5, sticky="NSWE")
        self.onr_iznos_rate_txt.grid(row=3, column=5, padx=5, sticky="NSWE")
        self.onr_datum_isplate_txt.grid(row=4, column=5, padx=5, sticky="NSWE")

        c.execute('SELECT Opis FROM OTPLATA_NA_RATE')
        for opis in c:
            self.lista_opisa_rata.append(opis[0])
        column = 0
        row = 1
        for opis in self.lista_opisa_rata:
            # Append function to button
            function = lambda x=opis: self.get_description(x)
            rata_button = Button(self.onr_top, text=opis, width=10, command=function)
            rata_button.grid(row=row, column=column, padx=5, pady=5)
            column += 1
            if column == 3:
                row += 1
                column = 0

        #   Define placement of Button widget - created with loop
        if row < 5:
            row = 5
        if row >= 5:
            row += 1

        self.onr_pl_pojed_btn.grid(row=row, column=5, padx=2, pady=5)

        x = self.master.winfo_x()
        y = self.master.winfo_y()
        self.onr_top.geometry(f"+{x}+{y}")


root = Tk()
my_gui = FinancialCalc(root)
root.mainloop()

Thanks!

Freezy
  • 89
  • 1
  • 8
  • 1
    Please make a [mre]. That way we can see what you did wrong. – TheLizzard Sep 06 '21 at 20:55
  • 1
    @TheLizzard Okay mate, I will edit post tomorrow when I come back home from work since I don't have enough time now. Gonna try to isolate the part of the code that is needed – Freezy Sep 06 '21 at 20:59
  • @TheLizzard Hey mate, I just edited post with code sample. Check it out if you can help, thanks! – Freezy Sep 07 '21 at 17:27
  • @Freezy you should provide a [mre], I don't think we need to see all of that database stuff and other things that don't cause the issue – Matiiss Sep 07 '21 at 18:22
  • @Matiiss Mate without database stuff you can't resolve issue, because Buttons, Labels and bunch other stuff are created from database values – Freezy Sep 07 '21 at 20:52

2 Answers2

0

You could use the code bellow :

import tkinter as tk

i = 0
def click():
    global i
    if i > 4:
        btn.destroy()
    else:
        i += 1

root = tk.Tk()

btn = tk.Button(root, text="test", command=click)
btn.pack()

root.mainloop()
TheLizzard
  • 7,248
  • 2
  • 11
  • 31
Ayyoub ESSADEQ
  • 776
  • 2
  • 14
  • fix indentation and don't use `` event on a `Button` widget, they have a `command` argument which should be used for calling commands on button click, also you don't need to and really shouldn't use a `global` here, it just occupies the namespace, also you can simply use `i += 1` – Matiiss Sep 07 '21 at 15:15
  • Hey guys, I just edited post with code sample. Check it out if you can help, thanks! – Freezy Sep 07 '21 at 17:27
  • @Matiiss I edited the answer but I want to know how you would avoid the global variable without using classes? – TheLizzard Sep 07 '21 at 17:32
  • @TheLizzard first of I would use classes but for this case you could simply use `btn.i`, first do `btn.i = 0` after initializing the button, then in the function use `btn.i` instead of `i` (and remove the `global` keyword) (actually in hopes that the class doesn't have that attribute already, but it probably shouldn't, worst it could have is sth like `._i` but who would leave some `i` attribute like that?) – Matiiss Sep 07 '21 at 18:18
  • @Matiiss For small examples like this, where there is no way of confusing the 5 variables: `i`, `btn`, `root`, `click`, and `tk`, there is no problem in using a global variable. The only time global variables are a problem is when there are too many variables to keep track of and you might override a variable by accident. For this small example, it will be an overkill to use classes. – TheLizzard Sep 07 '21 at 19:17
  • @TheLizzard I learnt something new today :) . But , I think that the problem with using the `command` is that even if the user doesn't click on the button it's automatically updated to one and I think that is due the state of the button at first . – Ayyoub ESSADEQ Sep 07 '21 at 19:50
  • @AyyoubESSADEQ No. You may have run into [this](https://stackoverflow.com/q/5767228/11106801) problem. Using `command=function` is the same as `.bind("", lambda event: function())`. – TheLizzard Sep 07 '21 at 19:56
0

no need for help for that part anymore, I just managed to do it with a dictionary. This is first part where I join button ID as value to Button text as key

for opis in self.lista_opisa_rata:
    function = lambda x=opis: self.get_description(x)
    rata_button = Button(self.onr_top, text=opis, width=10, command=function)
    rata_button.grid(row=row, column=column, padx=5, pady=5)
    self.dict_for_buttons[opis] = rata_button

This is second part where I delete button by matching label description name with button name and pulling ID from dictionary to delete button by ID.

def pay_selected(self):
    p_description = self.onr_oznacena_rata_txt.cget("text")
    num_of_installments = self.onr_broj_rata_txt.cget("text").split("/")
    if int(num_of_installments[0]) + 1 >= int(num_of_installments[1]):
        c.execute('DELETE FROM OTPLATA_NA_RATE WHERE Opis = ?',(p_description,))
        conn.commit()
        btn_to_del = self.dict_for_buttons[p_description]
        btn_to_del.destroy()
        self.dict_for_buttons.pop(p_description)

Working perfectly, but now another error occurred.. Anyway thanks for help, gonna ask again if I can't solve that alone!

Freezy
  • 89
  • 1
  • 8