mudricd
mudricd

Reputation: 143

Tkinter - How to pass instance variable to another class?

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)

Upvotes: 2

Views: 2492

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 385970

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.

Upvotes: 2

v.coder
v.coder

Reputation: 1932

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())) 

Upvotes: 0

Related Questions