How do I fix this error? self.category_id.set(row[1]), IndexError: string index out of range

Question:

I’m adding data into sqlite table and when I try to update the table, I’m getting this error ‘string index not in range’.

Again when I execute the update command, all the columns gets updated except the identity column but my intention is only to update a selected row.

what I not doing right from the code below>

Your assistance will be highly appreciated.

The error is happening in the function update_record(self).

This is my code:

import tkinter
from tkinter import *
from tkinter import ttk
import tkinter.messagebox
import sqlite3

root =Tk()
root.title('Accounting App')
root.config(bg='#3d6466')
root.geometry("520x400")
root.resizable(False, False)

style = ttk.Style()
style.theme_use('alt')
style.configure("TCombobox", fieldbackground="Grey", background="Grey")

class Backend():
    def __init__(self):
        self.conn = sqlite3.connect('accounting.db')
        self.cur = self.conn.cursor()

        #self.conn.execute("""DROP TABLE IF EXISTS account_type""")

        self.conn.execute("""CREATE TABLE IF NOT EXISTS account_type(
         id             INTEGER     PRIMARY KEY,
         category_id    INTEGER         NOT NULL,
         category_type  TEXT        NOT NULL
         )"""),

        self.conn.commit()
        # elf.conn.close()

    # =========Account Type======
    class Account_type():

        def insert_account_type(self, category_id, category_type):
            self.conn = sqlite3.connect('accounting.db')
            self.cur = self.conn.cursor()
            self.cur.execute("""INSERT INTO  account_type(category_id,category_type) VALUES(?,?);""",
                             (category_id, category_type,))
            self.conn.commit()
            self.conn.close()

        def view_account_type(self):
            self.conn = sqlite3.connect('accounting.db')
            self.cur = self.conn.cursor()
            self.cur.execute("SELECT * FROM account_type")
            rows = self.cur.fetchall()
            self.conn.close()
            return rows

acc_type = Backend.Account_type()
tb = Backend()

class Front_end():
    def __init__(self, master):
        # Frames

        global cur
        global conn
        conn = sqlite3.connect('accounting.db')
        cur = conn.cursor()
        # Frames
        self.left_frame = LabelFrame(master,bg='#3d6466', relief=SUNKEN,width=200)
        self.left_frame.pack(fill = 'both',expand = YES , padx = 5,side=LEFT,anchor=NW)
        self.right_frame = LabelFrame(master, bg='#3d6466', relief=SUNKEN)
        self.right_frame.pack(fill = 'both',expand = YES ,side=LEFT,anchor=NW)

        self.top_right_frame = LabelFrame(self.right_frame, bg='#3d6466', relief=SUNKEN,text='Details',fg='maroon')
        self.top_right_frame.pack(fill=BOTH,side=TOP, anchor=NW,expand=YES)

        self.top_r_inner_frame = LabelFrame(self.right_frame, bg='#3d6466', relief=SUNKEN, text='...', fg='maroon',height=10)
        self.top_r_inner_frame.pack(fill=BOTH, side=TOP, anchor=SW, expand=YES)

        self.bottom_right_frame = LabelFrame(self.right_frame, bg='#3d6466', relief=SUNKEN, text='Field View', fg='maroon')
        self.bottom_right_frame.pack(fill=BOTH,side=TOP, anchor=SW, expand=YES)

        self.my_canvas = Canvas(self.top_right_frame,bg='#3d6466')
        self.my_canvas.pack(side=LEFT,fill='both', expand=YES)

        # vertical configuration of scrollbar
        self.yscrollbar = ttk.Scrollbar(self.top_right_frame, orient=VERTICAL, command = self.my_canvas.yview)
        self.yscrollbar.pack(side=RIGHT,fill='both')

        self.my_canvas.config(yscrollcommand = self.yscrollbar.set)

        self.top_right_frame = Frame(self.my_canvas, bg='#3d6466', relief=SUNKEN)
        self.my_canvas.create_window((0,0),window=self.top_right_frame, anchor=NW)
        self.my_canvas.bind('<Configure>',lambda e:self.my_canvas.configure(scrollregion = self.my_canvas.bbox('all')))


        self.side_frame = LabelFrame(self.left_frame,bg='#3d6466',relief=SUNKEN,text='Menu Buttons',fg='maroon',)
        self.side_frame.pack(side=TOP,anchor=NW,expand=YES )

        # Side Buttons
        self.btn1 = Button(self.side_frame, text='Main Account Types', bg='#3d6466', font=('cambria', 12), anchor=W,
                           fg='white', width=18,height=2,command=self.main_account)
        self.btn1.grid(row=0, column=0, sticky=W)

    def main_account(self):

        # variables
        self.category_id = StringVar()
        self.category_type = StringVar()

        self.category_search =StringVar()

        # functions
        def add_main_accounts(self):
            if self.category_id.get() == "":
                tkinter.messagebox.showinfo('All fields are required')
            else:
                Backend.Account_type.insert_account_type(self,
                    self.category_id.get(),self.category_type.get())

                tkinter.messagebox.showinfo('Entry successful')

        def display_account_types(self):
            self.trv.delete(*self.trv.get_children())
            for rows in Backend.Account_type.view_account_type(self):
                self.trv.insert("", END, values=rows)

        def get_account_type(e):
            selected_row = self.trv.focus()
            data = self.trv.item(selected_row)
            row = data["values"]

            """Grab items and send them to entry fields"""
            self.category_id.set(row[1])
            self.category_type.set(row[2])

        def clear(self):
            self.category_id.set("")
            self.category_type.set("")

        **def update_record(self):
            selected = self.trv.focus()
            self.trv.item(selected, values=(
                self.category_id.get(), self.category_type.get()))
            conn = sqlite3.connect("accounting.db")
            cur = conn.cursor()
            if self.category_id.get() == "" or self.category_type.get() == "" :
                tkinter.messagebox.showinfo('All fields are required!')
                return
            update_record = tkinter.messagebox.askyesno('Confirm please',
                                                        'Do you want to update records?')
            if update_record > 0:
                cur.execute(
                    "UPDATE account_type SET category_id=:cat_id, category_type=:type",
                     {'cat_id': self.category_id.get(), 'type': self.category_type.get()})
                tkinter.messagebox.showinfo('Record update successful!')
            conn.commit()**

            # call the function for Clearing the fields
            clear(self)
            conn.close()

        """=================TreeView==============="""
        # Scrollbars
        ttk.Style().configure("Treeview", background = "#3d6466", foreground = "white", fieldbackground = "grey")

        scroll_x = Scrollbar(self.bottom_right_frame, orient = HORIZONTAL)
        scroll_x.pack(side = BOTTOM, fill = X)
        scroll_y = Scrollbar(self.bottom_right_frame, orient = VERTICAL)
        scroll_y.pack(side = RIGHT, fill = Y)

        # Treeview columns & setting scrollbars
        self.trv = ttk.Treeview(self.bottom_right_frame, height=5, columns=
        ('id','category_id', 'category_type'), xscrollcommand = scroll_x.set, yscrollcommand = scroll_y.set)

        # Treeview style configuration
        ttk.Style().configure("Treeview", background = "#3d6466", foreground = "white", fieldbackground = "#3d6466")

        # Configure vertical and Horizontal scroll
        scroll_x.config(command = self.trv.xview)
        scroll_y.config(command = self.trv.yview)

        # Treeview Headings/columns
        self.trv.heading('id', text = 'NO')
        self.trv.heading('category_id', text = 'Category ID')
        self.trv.heading('category_type', text = 'Category Type')

        self.trv['show'] = 'headings'

        # Treeview columns width
        self.trv.column('id', width = 50)
        self.trv.column('category_id', width = 70)
        self.trv.column('category_type', width = 90)

        self.trv.pack(fill = BOTH, expand = YES,anchor = NW)

        # Binding Treeview with data
        self.trv.bind('<<TreeviewSelect>>',get_account_type) # trv.bind('<Double-1>',"")

        # Account Types Labels
        self.lbl1 = Label(self.top_right_frame,text = 'Category ID',anchor = W,width=12,font = ('cambria',13,),bg = '#3d6466')
        self.lbl1.grid(row = 0,column = 0,pady = 5)

        self.lbl1 = Label(self.top_right_frame, text='Category Type', anchor=W, width=12, font=('cambria', 13,),  bg='#3d6466')
        self.lbl1.grid(row=1, column=0, pady=5)

        self.lbl2 = Label(self.top_right_frame, text='Search Account', anchor=W, width=12, font=('cambria', 13,),
                          bg='#3d6466')
        self.lbl2.grid(row=6, column=0, pady=5)

        # Account Type Entries
        self.entry1 = Entry(self.top_right_frame,textvariable = self.category_id,font = ('cambria',13,),bg = 'Grey',width=14)
        self.entry1.grid(row = 0,column=1,sticky = W,padx = 4,columnspan=2)
        self.entry1 = Entry(self.top_right_frame, textvariable=self.category_type, font=('cambria', 13,), bg='Grey', width=14)
        self.entry1.grid(row=1, column=1, sticky=W, padx=4, columnspan=2)
        self.entry2 = Entry(self.top_right_frame, textvariable=self.category_search, font=('cambria', 13,), bg='Grey', width=14)
        self.entry2.grid(row=6, column=1, sticky=W, padx=4, columnspan=2)

        # Buttons
        self.btn_1 = Button(self.top_right_frame,text='Add',font=('cambria',12,'bold'),bg='#3d6466',
                            activebackground='green', fg = 'white',width=12,height = 1,relief=RAISED,
                            command = lambda :[add_main_accounts(self),display_account_types(self),clear(self)])
        self.btn_1.grid(row = 3,column = 0,pady=6, padx=6)
        self.btn_2 = Button(self.top_right_frame, text = 'View',command=lambda :[display_account_types(self),clear(self)],
                            font=('cambria', 12, 'bold'), bg = '#3d6466', activebackground='green',
                           fg ='white', width=12, height = 1, relief=RAISED)
        self.btn_2.grid(row = 3, column=1,padx=0)
        self.btn_3 = Button(self.top_right_frame, text = 'Update', command= lambda :[update_record(self),
                                display_account_types(self)],font=('cambria', 12, 'bold'), bg = '#3d6466',
                           activebackground = 'green', fg='white', width = 12, height = 1, relief=RAISED)
        self.btn_3.grid(row = 4, column = 0,pady=6,padx=10)

# calling the class

app = Front_end(root)


root.mainloop() 
Asked By: B _J

||

Answers:

Your update function updates all the records exist. To avoid this you should use WHERE. Here its fixed version

def update_record(self):
    selected = self.trv.focus()
    
    oldValues = self.trv.item(selected)["values"]

    self.trv.item(selected, values=(oldValues[0],
        self.category_id.get(), self.category_type.get()))
    conn = sqlite3.connect("accounting.db")
    cur = conn.cursor()
    if self.category_id.get() == "" or self.category_type.get() == "" :
        tkinter.messagebox.showinfo('All fields are required!')
        return
    update_record = tkinter.messagebox.askyesno('Confirm please',
                                                'Do you want to update employee records?')
    if update_record > 0:
        cur.execute(
            "UPDATE account_type SET category_id=:cat_id, category_type=:type WHERE id=:id_value",
             {'cat_id': self.category_id.get(), 'type': self.category_type.get(), "id_value": oldValues[0]})
        tkinter.messagebox.showinfo('Record update successful!')
    conn.commit()

    # call the function for Clearing the fields
    clear(self)
    conn.close()

We used selected rows idx etc.
However your error string index out of range is not about this. The function above going to fix your record update problem. Your error is because it tries to show a record value but after refreshed table, nothing is selected. So it returns empty.

def get_account_type(e):
    selected_row = self.trv.focus()
    data = self.trv.item(selected_row)
    row = data["values"]
    if(row == ""):
        return
    """Grab items and send them to entry fields"""
    self.category_id.set(row[1])
    self.category_type.set(row[2])

Now if row is just an empty string, function stops executing and you get no errors.

Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.