Home > other >  How to save and restore the status of a variable number of tkinter Checkbuttons in a sqlite database
How to save and restore the status of a variable number of tkinter Checkbuttons in a sqlite database

Time:01-26

I have checkboxes that save their status (active or inactive) in a database. They are all grouped in chk_lst = [] and then they are saved in a single column of a table (so only id, checkbox). These are saved and loaded correctly. Obviously, as you know, either 0 or 1 is saved.

Next I want to assign a function, a class or any kind of other code to each checkbox, so that I activate the checkbox and execute the assigned code. So I call a certain checkbox like this thanks to a condition with sql, but I don't know if I'm doing it right.

I'm afraid this code may be wrong and / or have problems in the future due to something I underestimated or didn't think right. I'm not getting errors, but I don't know if I'm running it correctly, considering the code and database will be implemented with many checkboxes

conn = sqlite3.connect('....')
cursor = conn.cursor()

cursor.execute('SELECT checkbox FROM table_example')
x = cursor.fetchone()

if checkbox[0] == "1": #THIS PART
    code....
else:
    None

This instead, for information purposes for completeness of the answer, is the code I use to save the checkboxes in the database. It works good and properly. The problem is not here

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

root = tk.Tk()
root.geometry("200x200")
root.configure(bg='white')

chk_lst = []

#Checkbox
Checkbutton1 = IntVar()
Checkbutton2 = IntVar() 
            
Button1 = Checkbutton(root, text = "Checkbox 1", variable = Checkbutton1, onvalue = 1, offvalue = 0, height = 1,
                      bg="white", foreground='black', activebackground="white")
Button1.place(x=10, y=36)


Button2 = Checkbutton(root, text = "Checkbox 2", variable = Checkbutton2, onvalue = 1, offvalue = 0, height = 1,
                      bg="white", foreground='black', activebackground="white") 
Button2.place(x=10, y=66)

chk_lst.extend([Checkbutton1,Checkbutton2])


# Save Function
def save():
    conn = sqlite3.connect(".....")
    c = conn.cursor()
    
    for idx,chk_btn in enumerate(chk_lst,start=1):
        c.execute(f'SELECT checkbox FROM table_example WHERE id=?',(idx,))
        rec = c.fetchall()

        if rec:
            c.execute("UPDATE table_example SET checkbox=? WHERE id=?;", (chk_btn.get(),idx))
        else:
            c.execute("INSERT INTO table_example VALUES (?,?);", (idx,chk_btn.get()))
        
    conn.commit()
    conn.close()

    messagebox.showinfo("Saved successfully","Saved successfully")


# Load Function   
def load():
    conn = sqlite3.connect("....")
    c = conn.cursor()
    c.execute("SELECT * FROM table_example")
    vals = c.fetchall()
    
    for val,chk_btn in zip(vals,chk_lst):
        chk_btn.set(val[1])
    
    conn.close()

save = Button(root, text="save", bg='#b40909', foreground='white', command= save)
save.pack()
save.place(x=10, y=96)


load()

root.mainloop()

As mentioned above, the database is just a simple table with id column and checkbox column

CodePudding user response:

Lets just forget the SQL part for a moment, I think the problem can still be fixed if we forget the SQL part. I had to read the question few times to deduce what you were trying to convery.

So to begin with, all you want to do is toggle a function each time a checkbutton is ticked or not, right? You do not have to search so much for it, there is a command option for Checkbutton that will get executed, each time you tick/untick a checkbutton. Then all you need is to pass the required IntVar to the function, so you can check the value of the checkbutton.

from tkinter import *

root = Tk()

def func(var):
    if var.get(): # Also same as `if var.get() == 1`
        print('It is ticked') # Replace with whatever you wanted to do if the checkbutton is ticked
    else:
        print('Not ticked')

def new_win(cb):
    top = Toplevel(root)
    
    def cmd(cb):
        cb.invoke() # Invoke the linked function, this will tick the checkbutton

    Button(top,text='Click me to toggle the tickbox',command=lambda: cmd(cb)).pack(pady=5)

var = IntVar()
cb = Checkbutton(root,text='Checkbutton 1',variable=var,command=lambda: func(var))
cb.pack(padx=10,pady=10)

Button(root,text='Open new window',command=lambda: new_win(cb)).pack()

root.mainloop()

CodePudding user response:

If I understand what you’re doing. Every time you click on a checkbutton, it writes the result to the database, checking if the record is already present, it also shows you the previous value. I added record save time...

    #!/usr/bin/python3
import sys
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import sqlite3 as lite
import datetime


class Main(ttk.Frame):
    def __init__(self, parent, ):
        super().__init__(name="main")

        self.parent = parent
        self.checkbutton1 = tk.BooleanVar()
        self.checkbutton2 = tk.BooleanVar()
        self.dict_checks =  {1: self.checkbutton1, 2: self.checkbutton2}        
        self.init_ui()
        self.init_db()

    def init_db(self,):

        self.con = lite.connect(self.master.kwargs["database"],
                                detect_types=lite.PARSE_DECLTYPES|lite.PARSE_COLNAMES,
                                isolation_level='IMMEDIATE')
        self.con.text_factory = lite.OptimizedUnicode

        sql = "CREATE TABLE IF NOT EXISTS 'examples' ('example_id' INTEGER PRIMARY KEY,'checkbox_id' INTEGER, 'status' BOOLEAN DEFAULT '1', click_date TEXT)"
        cur = self.con.cursor()
        cur.execute(sql, ())
        self.con.commit()
        
    def init_ui(self):

        f0 = ttk.Frame(self)
        f1 = ttk.Frame(f0,)

        ttk.Label(f1, text="First Checkbutton:").pack()
        ttk.Checkbutton(f1,
                        onvalue=1,
                        offvalue=0,
                        variable=self.checkbutton1,
                        command=self.on_callback).pack()

        ttk.Label(f1, text="Second Checkbutton:").pack()
        ttk.Checkbutton(f1,
                        onvalue=1,
                        offvalue=0,
                        variable=self.checkbutton2,
                        command=self.on_callback).pack()

        f2 = ttk.Frame(f0,)

        bts = [("Callback", 7, self.on_callback, "<Alt-k>"),
               ("Read", 0, self.on_read, "<Alt-r>"),
               ("Close", 0, self.on_close, "<Alt-c>")]

        for btn in bts:
            ttk.Button(f2,
                       text=btn[0],
                       underline=btn[1],
                       command = btn[2]).pack(fill=tk.X, padx=5, pady=5)
            self.parent.bind(btn[3], btn[2])
            
        f1.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        f2.pack(side=tk.RIGHT, fill=tk.Y, expand=0)
        f0.pack(fill=tk.BOTH, expand=1)
        
    def on_callback(self, evt=None):

        print("Previous values:")
        self.on_read()

        cur = self.con.cursor()
        now = datetime.datetime.now()

        for k,v in self.dict_checks.items():

            if self.check_status(k) is not None:
                sql = "UPDATE examples SET status =?, click_date= ? WHERE checkbox_id =?;"
                args = (v.get(),now, k)
            else:
                sql = "INSERT INTO examples (checkbox_id, status, click_date)VALUES(?,?,?);"
                args = (k, v.get(),now)
               
            cur.execute(sql, args)

        self.con.commit()

        cur.close()

        print("Actual values:")
        self.on_read()

    def check_status(self, checkbox_id):

        sql = "SELECT * FROM examples WHERE checkbox_id =?"
        args = (checkbox_id,)
        cur = self.con.cursor()
        cur.execute(sql, args)
        rs = cur.fetchone()
        cur.close()
        return rs
        
    def on_read(self,):

        cur = self.con.cursor()
        sql = "SELECT * FROM examples;"
        cur.execute(sql)
        rs = cur.fetchall()
        cur.close()

        if rs:
            for i in rs:
                print(i)
        print("*"*79)
        
    def on_close(self, evt=None):
        
        self.con.close()
        self.parent.on_exit()

class App(tk.Tk):
    """Main Application start here"""
    def __init__(self, *args, **kwargs):
        super().__init__()

        self.args = args
        self.kwargs = kwargs
        self.protocol("WM_DELETE_WINDOW", self.on_exit)
        self.set_style(kwargs["style"])  
        self.set_title(kwargs["title"])
        self.resizable(width=False, height=False)
        
        Main(self).pack(fill=tk.BOTH, expand=1)

    def set_style(self, which):
        self.style = ttk.Style()
        self.style.theme_use(which)
        
    def set_title(self, title):
        s = "{0}".format(title)
        self.title(s)
        
    def on_exit(self):
        """Close all"""
        msg = "Do you want to quit?"
        if messagebox.askokcancel(self.title(), msg, parent=self):
            self.destroy()

def main():

    args = []

    for i in sys.argv:
        args.append(i)

    #('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
    kwargs = {"style":"clam", "title":"Simple App", "database":"examples.db"}

    app = App(*args, **kwargs)

    app.mainloop()

if __name__ == '__main__':
    main()            
    

enter image description here

enter image description here

  •  Tags:  
  • Related