0

I have a problem with 3 checkboxes intersecting when using list in checkbox functions. I would like to use intersection in relation to the quantity and combination of selected checkboxes.

I tried to insert the checkbox functions in a list and combine them with a condition if the checkboxes are only selected or deselected:

all_test = [(Button1_func()) if Checkbutton1.get() else not Checkbutton1.get(),
            (Button2_func()) if Checkbutton2.get() else not Checkbutton2.get(),
            (Button3_func()) if Checkbutton3.get() else not Checkbutton3.get()]

Next, I put the list in c = set.intersection(*all_test)

If I try to select one or more checkboxes, I get this error:

TypeError: descriptor 'intersection' for 'set' objects doesn't apply to a 'bool' object

Let me explain better, with an example, what I would like to achieve. For example if I select only checkbox1 I would like to print a, b, c, d, e. If I select all the checkboxes I would like to print only a, b, because the elements in common in the three lists are only a, b. How can I print what I said above without getting errors?

I'm looking for a solution that allows me to manage even 20 checkboxes if I need to extend the code in the future.

from tkinter import ttk
import tkinter as tk

root = tk.Tk()
root.geometry("300x300")

funclist = set()
Checkbutton1 = tk.IntVar()
Checkbutton2 = tk.IntVar()
Checkbutton3 = tk.IntVar()

#CHECKBOX'S FUNCTIONS
def Button1_func():
    test1 = ["a", "b", "c", "d", "e"]
    return test1
 
def Button2_func():
    test2 = ["a", "b", "c"]
    return test2

def Button3_func():
    test3 = ["a", "b"]
    return test3

def clicked(flag, func):
    if flag:
        funclist.add(func)
    else:
        funclist.remove(func)


#CHECKBOX
Button1 = tk.Checkbutton(root, text = "Checkbox 1", variable = Checkbutton1, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton1.get(), Button1_func))
Button1.place(x=10, y=36)

Button2 = tk.Checkbutton(root, text = "Checkbox 2", variable = Checkbutton2, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton2.get(), Button2_func))
Button2.place(x=10, y=66)

Button3 = tk.Checkbutton(root, text = "Checkbox 3", variable = Checkbutton3, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton3.get(), Button3_func))
Button3.place(x=10, y=96)


all_test = [(Button1_func()) if Checkbutton1.get() else not Checkbutton1.get(),
            (Button2_func()) if Checkbutton2.get() else not Checkbutton2.get(),
            (Button3_func()) if Checkbutton3.get() else not Checkbutton3.get()]


def try_print():
    #if each checkbox is True:
    if funclist and all(func() for func in funclist): 
        c = set.intersection(*all_test)      
        print(c)

#PRINT BUTTON
button = tk.Button(root, text="Print", command= lambda: [try_print()])
button.place(x=10, y=140)

root.mainloop()

UPDATE CODE (with addition of: saving and loading in database)

import sqlite3
from tkinter import ttk
import tkinter as tk

root = tk.Tk()
root.geometry("300x300")

conn = sqlite3.connect("xyz.db")
c = conn.cursor()

chk_lst = []
fn_lst = []
funclist = set()
Checkbutton1 = tk.IntVar()
Checkbutton2 = tk.IntVar()
Checkbutton3 = tk.IntVar()

#CHECKBOX'S FUNCTIONS
def Button1_func():
    test1 = ["a", "b", "c", "d", "e"]
    return test1
 
def Button2_func():
    test2 = ["a", "b", "c"]
    return test2

def Button3_func():
    test3 = ["a", "b"]
    return test3

def clicked(flag, func):
    if flag:
        funclist.add(func)
    else:
        funclist.remove(func)


#CHECKBOX
Button1 = tk.Checkbutton(root, text = "Checkbox 1", variable = Checkbutton1, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton1.get(), Button1_func))
Button1.place(x=10, y=36)

Button2 = tk.Checkbutton(root, text = "Checkbox 2", variable = Checkbutton2, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton2.get(), Button2_func))
Button2.place(x=10, y=66)

Button3 = tk.Checkbutton(root, text = "Checkbox 3", variable = Checkbutton3, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton3.get(), Button3_func))
Button3.place(x=10, y=96)


all_test = [(Button1_func()) if Checkbutton1.get() else not Checkbutton1.get(),
            (Button2_func()) if Checkbutton2.get() else not Checkbutton2.get(),
            (Button3_func()) if Checkbutton3.get() else not Checkbutton3.get()]


def try_print():
    if funclist and all(func() for func in funclist): 
        c = set.intersection(*all_test)      
        print(c)

chk_lst.extend([Checkbutton1, Checkbutton2, Checkbutton3])
fn_lst.extend([Button1_func, Button2_func, Button3_func])

#SAVE IN DATABASE
def save():
    conn = sqlite3.connect("xyz.db")
    c = conn.cursor()

    for idx,chk_btn in enumerate(chk_lst,start=1):
        c.execute(f'SELECT button1 FROM test WHERE id=?',(idx,))
        rec = c.fetchall()

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

    messagebox.showinfo('SAVE', 'Good!')


#LOAD WHEN OPEN WINDOWS
def load():
    conn = sqlite3.connect("xyz.db")
    c = conn.cursor()
    c.execute("SELECT * FROM test")
    vals = c.fetchall()

    for val, chk_btn, func in zip(vals, chk_lst, fn_lst):
        chk_btn.set(val[1])
        
        if val[1] == '1': #update the funclist set based on the data fetched from the database
            funclist.add(func)

    conn.close()



#PRINT BUTTON
button = tk.Button(root, text="Print", command= lambda: [try_print()])
button.place(x=10, y=140)

load()

root.mainloop()

1 Answers1

0

The following should work as you require (with some inline comments):

import tkinter as tk

root = tk.Tk()
root.geometry("300x300")

# store the output of the functions in a list rather than a set
datalist = []

Checkbutton1 = tk.IntVar()
Checkbutton2 = tk.IntVar()
Checkbutton3 = tk.IntVar()


#CHECKBOX'S FUNCTIONS
def Button1_func():
    # put these in a set
    test1 = set(["a", "b", "c", "d", "e"])
    return test1


def Button2_func():
    test2 = set(["a", "b", "c"])
    return test2


def Button3_func():
    test3 = set(["a", "b"])
    return test3


def clicked(flag, func):
    if flag:
        # append set to the list
        datalist.append(func())
    else:
        # remove set from the list
        datalist.remove(func())


#CHECKBOX
Button1 = tk.Checkbutton(root, text = "Checkbox 1", variable = Checkbutton1, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton1.get(), Button1_func))
Button1.place(x=10, y=36)

Button2 = tk.Checkbutton(root, text = "Checkbox 2", variable = Checkbutton2, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton2.get(), Button2_func))
Button2.place(x=10, y=66)

Button3 = tk.Checkbutton(root, text = "Checkbox 3", variable = Checkbutton3, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton3.get(), Button3_func))
Button3.place(x=10, y=96)


def try_print():
    # check that there is something in the list
    if len(datalist) > 0:
        c = set.intersection(*datalist)      
        print(c)

#PRINT BUTTON
button = tk.Button(root, text="Print", command=try_print)
button.place(x=10, y=140)

root.mainloop()

Note: due to using sets the order of the lists will not necessarily be preserved. If preserving the order is important then another method of finding the intersection will be needed (see, e.g., the various answers here).

Update

An updated answer to try and work with the updated code is:

import sqlite3
from tkinter import ttk
import tkinter as tk

root = tk.Tk()
root.geometry("300x300")

conn = sqlite3.connect("xyz.db")
c = conn.cursor()

chk_lst = []
fn_lst = []
datalist = []

Checkbutton1 = tk.IntVar()
Checkbutton2 = tk.IntVar()
Checkbutton3 = tk.IntVar()

#CHECKBOX'S FUNCTIONS
def Button1_func():
    test1 = set(["a", "b", "c", "d", "e"])
    return test1
 
def Button2_func():
    test2 = set(["a", "b", "c"])
    return test2

def Button3_func():
    test3 = set(["a", "b"])
    return test3

def clicked(flag, func):
    if flag:
        datalist.append(func())
    else:
        datalist.remove(func())


#CHECKBOX
Button1 = tk.Checkbutton(root, text = "Checkbox 1", variable = Checkbutton1, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton1.get(), Button1_func))
Button1.place(x=10, y=36)

Button2 = tk.Checkbutton(root, text = "Checkbox 2", variable = Checkbutton2, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton2.get(), Button2_func))
Button2.place(x=10, y=66)

Button3 = tk.Checkbutton(root, text = "Checkbox 3", variable = Checkbutton3, onvalue = 1, offvalue = 0, height = 1,
                         bg="#d9d9d9", foreground='black', activebackground="#d9d9d9",
                         command=lambda: clicked(Checkbutton3.get(), Button3_func))
Button3.place(x=10, y=96)


def try_print():
    if len(datalist) > 0:
        c = set.intersection(*datalist)      
        print(c)

chk_lst.extend([Checkbutton1, Checkbutton2, Checkbutton3])
fn_lst.extend([Button1_func, Button2_func, Button3_func])

#SAVE IN DATABASE
def save():
    conn = sqlite3.connect("xyz.db")
    c = conn.cursor()

    for idx,chk_btn in enumerate(chk_lst,start=1):
        c.execute(f'SELECT button1 FROM test WHERE id=?',(idx,))
        rec = c.fetchall()

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

    messagebox.showinfo('SAVE', 'Good!')


#LOAD WHEN OPEN WINDOWS
def load():
    conn = sqlite3.connect("xyz.db")
    c = conn.cursor()
    c.execute("SELECT * FROM test")
    vals = c.fetchall()

    for val, chk_btn, func in zip(vals, chk_lst, fn_lst):
        chk_btn.set(val[1])
        
        if val[1] == '1': #update the funclist set based on the data fetched from the database
            datalist.append(func())

    conn.close()


#PRINT BUTTON
button = tk.Button(root, text="Print", command= lambda: [try_print()])
button.place(x=10, y=140)

load()

root.mainloop()
Matt Pitkin
  • 3,989
  • 1
  • 18
  • 32
  • Yes, if preserving order is important then set's can't be used, so the intersection will have to be found in a way that doesn't use sets, e.g., https://stackoverflow.com/questions/3697432/how-to-find-list-intersection – Matt Pitkin Dec 23 '22 at 10:22
  • Thank you. This works, but I observe that you have changed some barrel things of my code. My code also included Saving and Loading checkboxes from a db - both worked correctly. Now, your changes have broken something (something simple I guess). I've updated the question by adding the original code. Can you apply your code to my save and load as well please? I think we need to change very little. I tried, but I get errors. Sorry to ask this, but I had no idea that your changes could harm the codebase. I hope you will help me, because it's quite simple to fix, but I can't. Thank you –  Dec 23 '22 at 10:59
  • Where do your `chk_lst` and `fn_lst` variables get defined? Or, should it just be `chk_lst = [Checkbutton1, Checkbutton2, Checkbutton3]` rather than `chk_lst.extend([Checkbutton1, Checkbutton2, Checkbutton3])`, and similarly for `fn_list`? – Matt Pitkin Dec 23 '22 at 11:09
  • I've updated the question. I forgot to write chk_lst = [] and fn_lst = []. Sorry. How can I fix saving and loading with your new code? Thank you –  Dec 23 '22 at 11:59
  • I've added an update to the answer based on your full code. I can't double check this works, but hopefully it should. The main change just involved appending to the `datalist` in the `load` function. – Matt Pitkin Dec 23 '22 at 12:28
  • I'll try your code later because I'm not at my house right now. I will write to you in a few hours. In the meantime, thank you –  Dec 23 '22 at 13:07
  • Your code seems to work fine.There is only one thing that does not convince me.In my code the contents of the checkbox were being processed when the button was clicked (e.g. try putting a print("hello") into the checkbox). Now,with your code,the cb is processed when I click on it: that would be fine,but the problem is that the checkbox is processed even when I DESELECT it, i.e. when I disable it, it processes the content equally and is uncomfortable.Is there a way to have the content of the cb ONLY process when it's selected and NOT when it's deselected? Thank you, Happy Christmas –  Dec 25 '22 at 03:21
  • I'm not sure why there would be a difference between your code and mine in this case. They both have the same flow control (if statement) in the `clicked` function, so I would expect would perform equivalently when selecting/deselecting. If there are differences, I'm afraid I don't know why. – Matt Pitkin Dec 27 '22 at 13:55
  • Thank you all the same. I accepted your solution. You're kind. Can I ask you to rate my question please? I'm new and I need some votes for the score. After your vote, I will delete this comment. Thank you –  Dec 28 '22 at 01:27