2

I googled a lot but it didn't work. Posted a question last week but no answer as it seems that it was too lengthily...

Hopefully, the new question is much clearer.

This is just a small piece of the code and if you run it you will be able to reproduce the issue. What I need to do basically is to get user input (E1 from mainGUI class) and pass it to insert() function from Database class. The error I get when I try to add an entry is:

"self.curs.execute("INSERT INTO diary VALUES (?)", (date)) sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 1, and there are 0 supplied."

I can read from the database with no problems.

Any help would be much appreciated.

from tkinter import *
import sqlite3

    class mainGUI(object):

    def __init__(self,window):

        self.window=window 

        self.E1 = Entry(window)                           
        self.E1.grid(row=1, column=0)
        self.EG1 = self.E1.get()

        global E4
        E4 = Listbox(window)                           
        E4.grid(row=2)

        B1 = Button(window, text="Add entry", command=lambda: database.insert(self.EG1)) 

        B1.grid(row=1, column=4)

        B2 = Button(window, text="View all", command=database.view_all)  
        B2.grid(row=2, column=4, sticky="WN")

        window.mainloop()

class Database(mainGUI):

    def __init__(self, db):
        self.conn = sqlite3.connect(db)
        self.curs = self.conn.cursor() 
        self.curs.execute("CREATE TABLE IF NOT EXISTS diary (date TEXT)")   
        self.conn.commit()

    def insert(self,date): 
        database.add_entry(date) 
        E4.delete(0, END)
        E4.insert(END,(date))

    def add_entry(self,date):     
        self.curs.execute("INSERT INTO diary VALUES (?)", (date))
        self.conn.commit()

    def view_all(self): 
        E4.delete(0, END)    
        self.curs.execute("SELECT * FROM diary")
        data = self.curs.fetchall()
        for row in data:
           E4.insert(END,row)                           

if __name__ == "__main__":   
    database = Database("dbase.db")
    window=Tk()
    gui = mainGUI(window)
6
  • Wouldn't passing under Database def__init__(self, db, mainGUI) allow you to use mainGUI.E4 in the insert function? Commented Feb 19, 2018 at 2:23
  • 1
    The Database should not interact with the GUI, so I suggest reworking it so the Database can function standalone while the GUI utilities it. You also what to call self.E1.get() on button click, to get the current value. If you do it in the __init__ method then it will just be an empty string. Commented Feb 19, 2018 at 2:37
  • I forgot to add comma after date in add_entry() function. Now the sqlite3.ProgrammingError is gone and insert is working fine. However, when I select from the database there is only the empty tuple {}, no value from the user input... Commented Feb 19, 2018 at 2:42
  • Thanks @Steven Summers. Any recommendations how to rework the code and how to add self.E1.get() to the button? Commented Feb 19, 2018 at 2:46
  • I removed self.EG1 = self.E1.get() and for the button command added database.insert(self.E1.get()). Now I can successfully insert the value into the database, it is not just empty tuple anymore. Any ideas how to get rid of the global E4? Commented Feb 19, 2018 at 2:58

2 Answers 2

2

If you are using classes, you should not be using global variables. Instead, you need to be accessing attributes and methods of your object instances. That means creating E4 as an instance variable, and access it via the instance of the GUI class.

Also, your Database should not inherit from mainGUI. That is not how inheritance should be used.

You have your Database coded so that it need to alter the GUI, which is unusual. Instead, your GUI should be the only thing that can modify the GUI, and your Database should be the only thing that modifies the database. When the GUI needs to send information to, or get information from the database, it can call methods on the database.

So, the first step is to pass the database object to your mainGUI class (which should be named MainGUI according to PEP8):

class MainGUI(object):
    def __init__(self,window, db):
        self.window = window
        self.db = db
        ...

Next, pass in the database object when you create the GUI:

if __name__ == "__main__":   
    database = Database("dbase.db")
    window=Tk()
    gui = MainGUI(window, database)

With that, you can now use the database to get and fetch data, and use the GUI to display the data.

For example, your GUI should have the "view_all" method, since it deals with altering the view. It should call the "get_all" method of the database. By not using lambda, your code becomes much easier to read, write, and debug.

class mainGUI(object):
    def __init__(self,window, db):
        ...
        self.E4 = Listbox(...)
        ...
        B2 = Button(..., command=self.view_all) 

    def view_all(self):
        data = self.db.get_all()
        self.E4.delete(0, END)    
        for row in data:
           self.E4.insert(END,row)

Finally, create a get_all method in your Database that does nothing but get and return the data:

class MainGUI(object):
    def get_all(self):   
        self.curs.execute("SELECT * FROM diary")
        data = self.curs.fetchall()
        return data

With all of the above, and after applying the same concepts to the other methods (ie: the Database class should only set or get data), you have a reusable Database class that is completely independent of the GUI that uses it.

Sign up to request clarification or add additional context in comments.

Comments

0

One thing I noticed is you did not associate any textvariable to the Entry widget, hence it is passing null value to the other class. Please try making self.EG1 a textvariable and then passing it to the function.

    self.EG1 = StringVar()
    self.E1 = Entry(window,textvariable=self.EG1)                           
    self.E1.grid(row=1, column=0)

    B1 = Button(window, text="Add entry", command=lambda: database.insert(self.EG1.get())) 

2 Comments

You don't need a stringvariable in this case. self.E1.get() returns the exact same thing and requires one less object to manage.
I have already set it up without StringVar and it works well. Any idea how to pass E4 variable from mainGUI class to Database class?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.