Insert data from a csv file into a TKinter's text widget

I made a silly calendar/scheduler using tkcalendar for scheduling events on a specific day.
Basically it works well, but, if there are more events recorded on a specific day, only one of them will be shown in the textbox, while in the shell they are shown correctly.
Here is the code:

from tkinter import *
from tkcalendar import Calendar
from tkinter import messagebox
import csv

fin_cal = Tk()

def seleziona_data(event):
    global sel_data
    sel_data = calendario.get_date()
    print(sel_data)

def inserisci_evento():
    data = StringVar()
    ora = StringVar()
    nominativo = StringVar()
    evento = StringVar()
    
    frm_eventi = Frame(fin_cal)
    frm_eventi.pack()
    
    etic_data = Label(frm_eventi, text='Data')
    etic_data.pack(pady=10)
    entry_data = Entry(frm_eventi, textvariable=data, justify='center')
    entry_data.pack()
    entry_data.insert(0, sel_data)

    etic_ora = Label(frm_eventi, text='Ora')
    etic_ora.pack(pady=10)
    entry_ora = Entry(frm_eventi, textvariable=ora, justify='center')
    entry_ora.pack()
    
    etic_nom = Label(frm_eventi, text="Nominativo")
    etic_nom.pack(pady=10)
    entry_nominativo = Entry(frm_eventi, textvariable=nominativo, justify='center')
    entry_nominativo.pack()
    
    etic_evento = Label(frm_eventi, text="Evento")
    etic_evento.pack(pady=10)
    entry_evento = Entry(frm_eventi, width=35, textvariable=evento)
    entry_evento.pack()
    
    
    def registra_evento():
        a_data = data.get()
        a_ora = ora.get()
        a_nominativo = nominativo.get()
        a_evento = evento.get()
        with open('/path/to/file.csv', 'a',
              encoding='UTF-8') as calendario_eventi:
            scrit_eventi = csv.writer(calendario_eventi)
            scrit_eventi.writerow([a_data,
                                   a_ora,
                                   a_nominativo,
                                   a_evento])
    
        if registra_evento:
            messagebox.showinfo(title="Registrazione evento",
                                message="Evento registrato nel calendario")
    
    pls_evento = Button(frm_eventi,
                        text="Registra evento",
                        command=registra_evento)
    pls_evento.pack(pady=10)
    
    def chiudi_ins_ev():
        frm_eventi.destroy()

    pls_chiudi = Button(frm_eventi, text='x', command=chiudi_ins_ev)
    pls_chiudi.pack()

def leggi_cal():
    cal_in_lett = open('/path/to/file.csv', 'r',
                                 encoding='UTF-8')
    let_kal = csv.reader(cal_in_lett)
    for rig in let_kal:
        if sel_data in rig:
            testo_evnt = str(rig)
            print(rig)
    
    t_bx = Text(fin_cal)
    t_bx.pack(pady=30)
    t_bx.insert('end', testo_evnt)


calendario_in_lettura = open('/path/to/file.csv', 'r',
     encoding='UTF-8')
lettr_cal = csv.reader(calendario_in_lettura)
for riga in lettr_cal:
    dt = riga[0]
    orario = riga[1]
    nmntv = riga[2]
    evnt = riga[3]

calendario = Calendar(fin_cal,
                      date_pattern='dd/mm/y')
calendario.pack(pady=15)

pls_inser_evento = Button(fin_cal,
                          text="Inserisci nuovo evento",
                          command=inserisci_evento)
pls_inser_evento.pack(pady=15)

pls_mostra = Button(fin_cal,
                    text="Mostra eventi del giorno selezionato",
                    command=leggi_cal)
pls_mostra.pack()
    

fin_cal.bind('<Button-1>', seleziona_data)


fin_cal.mainloop()

and this is what I get:

How can I get the correct print into the textbox with all the events for the specific selected day?

Inside the loop, we print the rig if it meets the condition - that is, each calendar event that happens on that date. But we assign that event to testo_evnt, replacing anything that might have been there before. Then when we add text to the t_bx text box, that happens outside of the loop, so only one event’s text can get added.

Either do the t_bx.insert stuff inside the loop (which means, create and pack t_bx before the loop, so that there is only one); or else use some more careful logic to create the testo_evnt string.

You may also want to consider creating t_bx well in advance (e.g. as a global), and only show/hide it when needed. Right now, the code will make a new Text for each button click, and they don’t get removed from the UI, as far as I can see. yes, the Text object that Python knows about will be garbage-collected, but Tcl/Tk has its own resources…

1 Like

Actually the Text objects will stay around. Each widget has an internal dict storing all its children, so yes they’ll keep being produced. Definitely avoid creating lots of widgets, just reuse them where possible.

1 Like

After some days, I decided to use a treeview instead text box.
So, now I have a calendar/scheduler that work fine, but it raises an error about a button that I cannot understand.
Basically, it doesn’t affect the functionality of the function but I would solve it to be sure this won’t affect something else.

Here’s the code:

from tkinter import *
from tkinter import ttk
from tkcalendar import Calendar
from tkinter import messagebox
from datetime import date
import csv


fin_cal = Tk()


oggi = date.today()
frmt_oggi = oggi.strftime('%d/%m/%Y')
print(frmt_oggi)

def seleziona_data(event):
    global sel_data
    sel_data = calendario.get_date()
    print(sel_data)

def inserisci_evento():
    frm_eventi = Frame(fin_cal)
    frm_eventi.pack()
    
    def chiudi_ins_ev():
        frm_eventi.destroy()
    
    if sel_data < frmt_oggi:
        pass
        chiudi_ins_ev()
        messagebox.showinfo(title="Registrazione evento",
                                                 message="Impossibile aggiungere nuovi eventi a date precedenti a quella odierna")
    
    if sel_data >= frmt_oggi:
        data = StringVar()
        ora = StringVar()
        nominativo = StringVar()
        evento = StringVar()
        
        etic_data = Label(frm_eventi, text='Data')
        etic_data.pack(pady=10)
        entry_data = Entry(frm_eventi, textvariable=data, justify='center')
        entry_data.pack()
        entry_data.insert(0, sel_data)

        etic_ora = Label(frm_eventi, text='Ora')
        etic_ora.pack(pady=10)
        entry_ora = Entry(frm_eventi, textvariable=ora, justify='center')
        entry_ora.pack()
        
        etic_nom = Label(frm_eventi, text="Nominativo")
        etic_nom.pack(pady=10)
        entry_nominativo = Entry(frm_eventi, textvariable=nominativo, justify='center')
        entry_nominativo.pack()
        
        etic_evento = Label(frm_eventi, text="Evento")
        etic_evento.pack(pady=10)
        entry_evento = Entry(frm_eventi, width=35, textvariable=evento)
        entry_evento.pack()
        

        pls_chiudi = Button(frm_eventi, text='x', command=chiudi_ins_ev)
        pls_chiudi.pack()

    def registra_evento():        
        if sel_data >= frmt_oggi:
            a_data = data.get()
            a_ora = ora.get()
            a_nominativo = nominativo.get()
            a_evento = evento.get()
            with open('/path/to/file.csv', 'a', encoding='UTF-8') as calendario_eventi:
                scrit_eventi = csv.writer(calendario_eventi)
                scrit_eventi.writerow([a_data, a_ora, a_nominativo, a_evento])

                messagebox.showinfo(title="Registrazione evento",
                                                         message="Evento registrato nel calendario")
        
    pls_evento = Button(frm_eventi, text="Registra evento", command=registra_evento)
    pls_evento.pack(pady=10)   


def leggi_cal():
    tv_cal = ttk.Treeview(fin_cal, columns=('Data', 'Ora', 'Nominativo', 'Evento'))
                    
    tv_cal['show'] = 'headings'
    tv_cal.column('#0')
    tv_cal.column('#1', anchor='n', width=100)
    tv_cal.column('#2', anchor='n', width=60)
    tv_cal.column('#3', anchor='n', width=170)
    tv_cal.column('#4', width=500)
    
    tv_cal.heading('#0')
    tv_cal.heading('#1', text='Data')
    tv_cal.heading('#2', text='Ora')
    tv_cal.heading('#3', text='Nominativo')
    tv_cal.heading('#4', text='Evento')
    
    tv_cal.pack(pady=9)
    
    
    cal_in_lett = open('/path/to/file.csv', 'r', encoding='UTF-8')
    let_kal = csv.reader(cal_in_lett)
    for riga in let_kal:
        dt = riga[0]
        orario = riga[1]
        nmntv = riga[2]
        evnt = riga[3]
           
        if sel_data in riga:
            tv_cal.insert("", 0, values=(dt, orario, nmntv, evnt))
            print(riga)
            
    
    def chiudi_leggi():
        tv_cal.destroy()
        pls_chiudi_leggi.destroy()
                
    pls_chiudi_leggi = Button(fin_cal, text='x', command=chiudi_leggi)
    pls_chiudi_leggi.pack()

              
calendario = Calendar(fin_cal, date_pattern='dd/mm/y')
calendario.pack(pady=15)


pls_inser_evento = Button(fin_cal, text="Inserisci nuovo evento",                                     command=inserisci_evento)
pls_inser_evento.pack(pady=15)

pls_mostra = Button(fin_cal, text="Mostra eventi del giorno selezionato",                         command=leggi_cal)
pls_mostra.pack()


fin_cal.bind('<Button-1>', seleziona_data)


fin_cal.mainloop()


and the error is:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.11/tkinter/__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "/path/to/file.py", line 83, in inserisci_evento
    pls_evento = Button(frm_eventi,
                 ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/tkinter/__init__.py", line 2706, in __init__
    Widget.__init__(self, master, 'button', cnf, kw)
  File "/usr/lib/python3.11/tkinter/__init__.py", line 2628, in __init__
    self.tk.call(
_tkinter.TclError: bad window path name ".!frame2"

I know, I could use the built-in functions/method of tkcalendar’s module, but I discover them late.
Anyway, as a newbie coder on my days off, it was a way to practice with conditions and stuff like that.

So, any suggest to solve it?
Thanks in advance.

Hi,

in the following line of code, you have created and defined your top level window as:

fin_cal = Tk()  # Top level window for your application

You have added the binding key to the following button (but fin_cal is not a button, it is your window):

fin_cal.bind('<Button-1>', seleziona_data) # Adding binding key to button

Can you make a blanket statement like this? In the material that I reviewed, you had to apply bindings on a per button basis. If this is possible, can you please show where it states that you can apply the binding to all buttons in a window by applying it to the name of the window?

The buttons that you have created in your application are (I only copied and pasted the front part of their expressions for brevity):

pls_evento = Button(frm_eventi
pls_chiudi = Button(frm_eventi,
pls_chiudi_leggi = Button(fin_cal
pls_inser_evento = Button(fin_cal
pls_mostra = Button(fin_cal,

Thus, depending on the button that you are adding the binding key functionality to, you have to append the button name as part of the binding definition and not the name of the window.

For example, if you wanted to add this binding key to the pls_evento button, you would define it as:

pls_evento.bind('<Button-1>', seleziona_data)  

You have to do this for every button on an individual basis per the material that I referenced.

Additionaly, because now you are calling the ‘seleziona_data’ function from more than one location (i.e., mouse pointer, and now the added binding key), you have to add the *args keyword as part of the function arguments, as in:

def seleziona_data(event, *args):

Hope this helps.

P.S.

For some reason, I was getting an error when attempting to download the tkcalender module. I got the following error when attempting to do so in red letters:

ERROR: Could not find a version that satisfies the requirement tkcalender (from version: none)
ERROR: No matching distribution found for tkcalender

Possibly because it’s spelled tkcalendar instead?

Doh! Now ya’s done it. (yup, that was it) :grin:

Hello,

I please try this code. I modified the part where you read in the .csv file (in the leggi_cal function). You can compare it to the one you uploaded. You will have to edit the file directory as noted by the constant: CSV_FILE_PATH so that it is in tune to your directory requirements. This streamlines the code just a bit because you only need to modify the file directory in only one location verses two locations as is currently the case (at the top of the file).

Here is some feedback (not all exhaustive):

  1. If you click on the ‘Mostra eventi del giorno selezionato’ button first when the application is first opened, you get an exception error. NameError: name ‘sel_data’ is not defined

  2. In order for the application to ‘work’, you first have to click the top ‘Inserisci nuovo evento’ button prior to clicking on the ‘Mostra eventi delf giorno selezionato’ button.

  3. If you click on the ‘Insecisci nuovo evento’ button, and then click the ‘Regisra evento’ button without actually entering any data, a pop-up appears with the message: ‘Evento registrato nel calendario’, confirming that the event was recorded (when none was entered). You need to update the code to catch this. If you reference the file, you will notice that only the date was recorded. On the next run of the module, you will then encounter an error when attempting to read it because 3 of the last 4 cells of the row (list) recorded are empty (as the file is read line per line).

  4. Would help if you added the carriage Return binding to the buttons in case users want to use
    the keyboard ‘Enter’ to confirm selection.

In any case, here is the slightly modified code:

---------------- UPDATE ------------------------------
#1 - #3 have been mitigated

#2 - Updated the registra_evento function so that it now checks and verifies that all fields were entered. However, would help if data entered was in correct format.

#3 - Mitigated by binding the seleziona_data to both buttons (reference the two lines of code near the bottom.


from tkinter import *
from tkinter import ttk
from tkcalendar import Calendar
from tkinter import messagebox
from datetime import date
import csv
from pathlib import Path

fin_cal = Tk()

CSV_FILE_PATH = Path('/path/to/file.csv') # Verify the correct directory entered

oggi = date.today()
frmt_oggi = oggi.strftime('%d/%m/%Y')
print(frmt_oggi)

def seleziona_data(*args):
    global sel_data
    sel_data = calendario.get_date()
    print(sel_data)

def inserisci_evento():
    frm_eventi = Frame(fin_cal)
    frm_eventi.pack()
    
    def chiudi_ins_ev():
        frm_eventi.destroy()
    
    if sel_data < frmt_oggi:
        pass
        chiudi_ins_ev()
        messagebox.showinfo(title = "Registrazione evento",
                            message = "Impossibile aggiungere nuovi eventi a date precedenti a quella odierna")
    
    if sel_data >= frmt_oggi:
        data = StringVar()
        ora = StringVar()
        nominativo = StringVar()
        evento = StringVar()
        
        etic_data = Label(frm_eventi, text = 'Data')
        etic_data.pack(pady = 10)
        entry_data = Entry(frm_eventi, textvariable = data, justify = 'center')
        entry_data.pack()
        entry_data.insert(0, sel_data)

        etic_ora = Label(frm_eventi, text = 'Ora')
        etic_ora.pack(pady=10)
        entry_ora = Entry(frm_eventi, textvariable = ora, justify = 'center')
        entry_ora.pack()
        
        etic_nom = Label(frm_eventi, text = "Nominativo")
        etic_nom.pack(pady = 10)
        entry_nominativo = Entry(frm_eventi, textvariable = nominativo, justify = 'center')
        entry_nominativo.pack()
        
        etic_evento = Label(frm_eventi, text = "Evento")
        etic_evento.pack(pady = 10)
        entry_evento = Entry(frm_eventi, width = 35, textvariable = evento)
        entry_evento.pack()
        

        pls_chiudi = Button(frm_eventi, text='x', command = chiudi_ins_ev)
        pls_chiudi.pack()

    def registra_evento():
                  
        a_data = data.get()
        a_ora = ora.get()
        a_nominativo = nominativo.get()
        a_evento = evento.get()

        if (a_data and a_ora) and (a_nominativo and a_evento):
        
            with open(CSV_FILE_PATH, 'a', encoding = 'UTF-8') as calendario_eventi:
                
                scrit_eventi = csv.writer(calendario_eventi)
                scrit_eventi.writerow([a_data, a_ora, a_nominativo, a_evento])

                messagebox.showinfo(title = "Registrazione evento",
                                    message = "Evento registrato nel calendario")

        
    pls_evento = Button(frm_eventi, text = "Registra evento", command = registra_evento)
    pls_evento.pack(pady = 10)   


def leggi_cal():
    
    tv_cal = ttk.Treeview(fin_cal, columns = ('Data', 'Ora', 'Nominativo', 'Evento'))
                    
    tv_cal['show'] = 'headings'
    tv_cal.column('#0')
    tv_cal.column('#1', anchor = 'n', width = 100)
    tv_cal.column('#2', anchor = 'n', width = 60)
    tv_cal.column('#3', anchor = 'n', width = 170)
    tv_cal.column('#4', width = 500)
    
    tv_cal.heading('#0')
    tv_cal.heading('#1', text='Data')
    tv_cal.heading('#2', text='Ora')
    tv_cal.heading('#3', text='Nominativo')
    tv_cal.heading('#4', text='Evento')
    
    tv_cal.pack(pady=9)

    with open(CSV_FILE_PATH, 'r', encoding = 'UTF-8') as file:
        
        csvreader = csv.reader(file)
        
        for riga in csvreader:
            
            dt = riga[0]
            orario = riga[1]
            nmntv = riga[2]
            evnt = riga[3]

            if sel_data in riga:
                tv_cal.insert("", 0, values = (dt, orario, nmntv, evnt))           

    def chiudi_leggi():
        tv_cal.destroy()
        pls_chiudi_leggi.destroy()
                
    pls_chiudi_leggi = Button(fin_cal, text = 'x', command = chiudi_leggi)
    pls_chiudi_leggi.pack()

              
calendario = Calendar(fin_cal, date_pattern = 'dd/mm/y')
calendario.pack(pady = 15)


pls_inser_evento = Button(fin_cal, text = "Inserisci nuovo evento", command = inserisci_evento)
pls_inser_evento.pack(pady = 15)

pls_mostra = Button(fin_cal, text = "Mostra eventi del giorno selezionato", command = leggi_cal)
pls_mostra.pack()

pls_inser_evento.bind('<Button-1>', seleziona_data)
pls_mostra.bind('<Button-1>', seleziona_data)

fin_cal.mainloop()