Refresh treeview after input from Input form

Hello everyone,
I am relatively new to Python programming. That’s why sometimes I run into a problem that I just can’t figure out.
I am making a small OOP programming application and have created several py files.
One of the files is view.py, which displays a treeview connected to PostgreSQL when the Show button is clicked.
The second Input button opens the input form where the data is entered and after clicking the Save button, the data is successfully saved in PostgreSQL, but the problem is that I can’t understand why the newly entered data is not automatically refreshed in the treeview.
When I close the currently open treeview and reopen it, the data is then displayed.

I use this method when clicking the Save button: self.view_guests_form.load_data()
which should refresh the data in the treeview after input.

Sorry if I’m using a little bad terminology, but I’m still learning!

Obviously, I’m not connecting that about refreshing the data with the view.py file, i.e. treeview?

Can someone give me some advice or guidance? Is there anything else I need to show from the code?

Thanks in advance for your answers and help!

You say “a treeview connected to PostgreSQL”, but how are you doing that? Are you just filling the treeview using its .insert method?

If yes, then you need to refresh it yourself when you change something in the database.

@MRAB
thx for response, first of all I apologize again about my bad terminology, newbie I am…

  1. a treeview connected to PostgreSQL but how are you doing that?

that’s in view.py (for treeview) ofc. part of the code

def load_data(self):
        try:
            connection = psycopg2.connect(
                host="**********",
                database="********",
                user="**********",
                password="*********",
                sslmode="require"
            )
            cursor = connection.cursor()
            query = """
                SELECT id, cl_broj, ime, prezime, ured, poduzece, dolazak, odlazak, nocenja, 
                mjesec, app, osoba, smjestaj, osig, sm_osig, bor_prist, hrana, ukupno, napomena 
                FROM gosti
            """
            cursor.execute(query)
            rows = cursor.fetchall()

# Clearing current data in treeview
            for i in self.tree.get_children():
                self.tree.delete(i)
            #self.tree.insert('', tk.END, values=formatted_row)

# Adding new data
            for row in rows:
                formatted_row = list(row)
                formatted_row[6] = self.format_eu_date(formatted_row[6])    
                formatted_row[7] = self.format_eu_date(formatted_row[7])  

                for index in [12, 13, 14, 15, 16, 17]:  # Index of columns to be formatted  
                    formatted_row[index] = self.format_eu_number(formatted_row[index])


# Insert each line only once
                self.tree.insert('', tk.END, values=formatted_row)
                #self.tree.insert('', tk.END, values=(row[0], *formatted_row)) 
 
        except Exception as e:
            print(f"Error retrieving data: {e}")
        finally:
            if cursor:
                cursor.close()
            if connection:
                connection.close()

Hope it answers your question… and please my app is for Croatia so data in PostgreSQL and treeview are in Croatian language (eg. ime, prezime - name, surname…)

I have in entry.py (entry form) this definition:

# Save the entry to the database - Save Entry
    def save_guest(self):
**Rest of the code...**.
            cursor.execute(insert_query, guest_data)
            connection.commit()
            print("The data has been saved successfully.")

            self.obrisi_unos()
            print("The form has been cleared for a new entry.")

            # We call the 'load_data' method to refresh the data in treeview
            if self.view_guests_form:  
                self.view_guests_form.load_data()
                   print("TreeView is refreshed with new entry Data.")

        except Exception as e:
            print(f"Error saving data: {e}")
        finally:
            if cursor is not None:
                cursor.close()
            if connection is not None:
                connection.close()

I have also edit.py and all is the same init with

class EditGuestForm(tk.Toplevel):
    def __init__(self, parent, guest_id=None, view_guests_form=None):
        super().__init__(parent)
        self.parent = parent
        self.guest_id = guest_id
        self.view_guests_form = view_guests_form
        print("EditGuestForm view_guests_form reference:", view_guests_form)

guset_id - is for selecting row by id number (that is only difference)

and in entry.py
class EntryGuestForm(tk.Toplevel):
    def __init__(self, parent, view_guests_form=None):
        super().__init__(parent)
        self.parent = parent
        self.view_guests_form = view_guests_form
        print("EntryGuestForm view_guests_form reference:", view_guests_form)

but in terminal I see no connection between entry and view.py
EntryGuestForm view_guests_form reference: None

but in Edit.py I see it…
EditGuestForm view_guests_form reference: .!viewguestsform

Probably because that my treeview wont automatically refresh?

Thank you again for response and hope you can help me,
Cheers!

When you put data into a treeview, the treeview knows nothing about the source of that data, and it has no way of knowing when that data changes.

You have to refresh the data in the treeview yourself.

The treeviews .insert method returns an item ID for the row that was added. What you should do is store that ID somewhere for each row that you add so that when you change something in that row you can use the treeviews .item method to refresh the row.

I’ll see if I can come up with some sample code - I’ve done it often enough!

OK, here’s the sample code. Double-click on a row to edit it:

from os.path import dirname, join
import tkinter as tk
import tkinter.ttk as ttk

class EditDialog(tk.Toplevel):
    def __init__(self, parent, row):
        tk.Toplevel.__init__(self, parent)
        self.title('Edit row')
        self.grab_set()
        self.parent = parent
        self.row = row

        # Use a grid layout.
        self.grid_columnconfigure(0, weight=0)
        self.grid_columnconfigure(1, weight=1)

        # The entry boxes.
        tk.Label(self, text='cl_broj:').grid(row=0, column=0)
        self.cl_broj_entry = tk.Entry(self)
        self.cl_broj_entry.grid(row=0, column=1, sticky='we')

        tk.Label(self, text='ime:').grid(row=1, column=0)
        self.ime_entry = tk.Entry(self)
        self.ime_entry.grid(row=1, column=1, sticky='we')

        tk.Label(self, text='prezime:').grid(row=2, column=0)
        self.prezime_entry = tk.Entry(self)
        self.prezime_entry.grid(row=2, column=1, sticky='we')

        tk.Label(self, text='ured:').grid(row=3, column=0)
        self.ured_entry = tk.Entry(self)
        self.ured_entry.grid(row=3, column=1, sticky='we')

        tk.Label(self, text='poduzece:').grid(row=4, column=0)
        self.poduzece_entry = tk.Entry(self)
        self.poduzece_entry.grid(row=4, column=1, sticky='we')

        tk.Label(self, text='dolazak:').grid(row=5, column=0)
        self.dolazak_entry = tk.Entry(self)
        self.dolazak_entry.grid(row=5, column=1, sticky='we')

        tk.Label(self, text='odlazak:').grid(row=6, column=0)
        self.odlazak_entry = tk.Entry(self)
        self.odlazak_entry.grid(row=6, column=1, sticky='we')

        tk.Label(self, text='nocenja:').grid(row=7, column=0)
        self.nocenja_entry = tk.Entry(self)
        self.nocenja_entry.grid(row=7, column=1, sticky='we')

        # The OK and Cancel buttons.
        frame = tk.Frame(self)
        frame.grid(row=8, column=0, columnspan=2, sticky='we')

        tk.Button(frame, text='Cancel', command=self.on_cancel).pack(side='right')
        tk.Button(frame, text='OK', command=self.on_ok).pack(side='right')

        self.bind('<Return>', self.on_ok)
        self.bind('<Escape>', self.on_cancel)

        # Fill the entry boxes.
        self.cl_broj_entry.insert(0, self.row['cl_broj'])
        self.ime_entry.insert(0, self.row['ime'])
        self.prezime_entry.insert(0, self.row['prezime'])
        self.ured_entry.insert(0, self.row['ured'])
        self.poduzece_entry.insert(0, self.row['poduzece'])
        self.dolazak_entry.insert(0, self.row['dolazak'])
        self.odlazak_entry.insert(0, self.row['odlazak'])
        self.nocenja_entry.insert(0, self.row['nocenja'])

        self.ok = False

    def on_ok(self, event=None):
        self.row['cl_broj'] = self.cl_broj_entry.get()
        self.row['ime'] = self.ime_entry.get()
        self.row['prezime'] = self.prezime_entry.get()
        self.row['ured'] = self.ured_entry.get()
        self.row['poduzece'] = self.poduzece_entry.get()
        self.row['dolazak'] = self.dolazak_entry.get()
        self.row['odlazak'] = self.odlazak_entry.get()
        self.row['nocenja'] = self.nocenja_entry.get()

        self.ok = True
        self.destroy()

    def on_cancel(self, event=None):
        self.ok = False
        self.destroy()

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title('CSV Table Viewer')
        self.state('zoomed')

        # Make a table from a TreeView and add scrollbars.
        self.grid_rowconfigure(0, weight=1)
        self.grid_rowconfigure(1, weight=0)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=0)

        yscrollbar = tk.Scrollbar(self, orient='vertical')
        yscrollbar.grid(row=0, column=1, sticky='ns')
        xscrollbar = tk.Scrollbar(self, orient='horizontal')
        xscrollbar.grid(row=1, column=0, sticky='we')

        self.table = ttk.Treeview(self, show='headings', selectmode='extended', xscrollcommand=xscrollbar.set, yscrollcommand=yscrollbar.set)
        self.table.grid(row=0, column=0, sticky='nswe')

        # Double-click on a row to edit it.
        self.table.bind('<Double-1>', self.on_edit_row)

        # Load the data from a CSV file.
        self.data_path = join(dirname(__file__), 'data.csv')
        self.load_data()

        # Add headings to the table.
        headings = self.fieldnames
        self.table['columns'] = headings

        for i, heading in enumerate(headings):
            self.table.heading(i, text=heading)

        # Fill the table.
        for row in self.rows:
            values = self.make_values(row)
            # The 'iid' entry is the ID of the row in the table.
            row['iid'] = self.table.insert('', 'end', values=values)

        # Make a lookup dict that maps the ID of a row to the row's data.
        self.row_by_iid = {row['iid']: row for row in self.rows}

    def load_data(self):
        # Load the data from the CSV file.
        try:
            with open(self.data_path, encoding='utf-8') as file:
                reader = csv.DictReader(file)
                self.fieldnames = reader.fieldnames
                self.rows = list(reader)
        except FileNotFoundError:
            # No CSV file, so make up some data.
            self.fieldnames = ['cl_broj', 'ime', 'prezime', 'ured', 'poduzece', 'dolazak', 'odlazak', 'nocenja']
            self.rows = []

            for i in range(1, 11):
                row = {fieldname: f'{fieldname} {i}' for fieldname in self.fieldnames}
                self.rows.append(row)

    def make_values(self, row):
        return [row[fieldname] for fieldname in self.fieldnames]

    def on_edit_row(self, event=None):
        # Which row are we editing?
        selection = self.table.selection()

        if len(selection) != 1:
            return

        row = self.row_by_iid[selection[0]]
        dialog = EditDialog(self, row)
        dialog.wait_window()

        if dialog.ok:
            # The dialog's OK button was clicked, so the data has changed.
            # Update the table.
            values = self.make_values(row)
            self.table.item(row['iid'], values=values)

App().mainloop()
1 Like