1

I want to create a program where the user can create different buttons with the click of the mouse, those buttons should be independent. With this logic, the user can create a checkbutton that works, change from green to red when is selected. My problem is that if the user click the mouse again, the checkbutton moves instead of creating a new checkbutton. Any suggestion how to do it?

from tkinter import *

root = Tk()

button1 = IntVar()

def color_checkbutton():  # define the colors of the checkbutton
    if button1.get() == 1:
        example_checkbutton.configure(bg='red')
    else:
        example_checkbutton.configure(bg='green')
example_checkbutton = Checkbutton(root, variable=button1, textvariable=button1, command=color_checkbutton)

def place_checkbutton_in_canvas(e):  # order to insert the checkbutton
    xx_and = e.x
    yy_and = e.y
    example_checkbutton.place(x=xx_and, y=yy_and)

root.bind('<Button-1>', place_checkbutton_in_canvas)

root.mainloop()
fernando
  • 89
  • 1
  • 15

1 Answers1

3

You do only have one example_checkbutton. Whenever you call the .place()method, this button is moved around.

If you want new ones, just create them as new Checkbox-widgets:

def place_checkbutton_in_canvas(e):  # order to insert the checkbutton
    if len(str(e.widget))<3: ## Don't place a new one if a checkbox was clicked
        xx_and = e.x
        yy_and = e.y
        Checkbutton(root, variable=button1, textvariable=button1, command=color_checkbutton).place(x=xx_and, y=yy_and)

This creates new checkbuttons which are all linked to the button1 variable.

EDIT:

If you want new checkbuttons, you'll have to maintain a list of IntVar() and Checkbutton() objects which is getting longer with each click. The code below should work. I also execute the color change upon creation to create them green and red.

from tkinter import *

root = Tk()

buttons = []

class CMD: #Auxilliary function for callbacks using parameters. Syntax: CMD(function, argument1, argument2, ...)
    def __init__(s1, func, *args):
        s1.func = func
        s1.args = args
    def __call__(s1, *args):
        args = s1.args+args
        s1.func(*args)

def color_checkbutton(pos=0):  # define the colors of the checkbutton
    if buttons[pos][0].get() == 1:
        buttons[pos][2].configure(bg='red')
    else:
        buttons[pos][2].configure(bg='green')

def place_checkbutton_in_canvas(e):  # order to insert the checkbutton
    if len(str(e.widget))<3: ## Don't place a new one if a checkbox was clicked
        b = IntVar()
        pos = len(buttons)
        xx_and = e.x
        yy_and = e.y
        buttons.append([b,pos, Checkbutton(root, variable=b, textvariable=b, command=CMD(color_checkbutton,pos))])
        buttons[-1][2].place(x=xx_and, y=yy_and)
        color_checkbutton(pos)

root.bind('<Button-1>', place_checkbutton_in_canvas)

root.mainloop()
Martin Wettstein
  • 2,771
  • 2
  • 9
  • 15
  • Thank you, it does create new checkbuttons, but I guess now my problem is that the variable in those checkbuttons have to be different. Sharing the variable button1 makes them "not independent" to each other. In fact the checkbutton name should be different to be able to configure the color independently as well. Thats what i don't know how to do it – fernando Aug 18 '20 at 12:38
  • 1
    The edit covers this shortcoming. It gets a little more complicated with the auxiliary class CMD but it works and creates as many checkboxes as you want that can be colored independently. – Martin Wettstein Aug 18 '20 at 12:44
  • Thank you Martin, it is exactly what I wanted, it gets complicated for my level (very low), I need to understand well how classes work, but it really helps. – fernando Aug 18 '20 at 12:49
  • 1
    Just forget about the code of CMD. I have been using this code chunk for 10 years before I knew enough about OOP to actually understand what it does. Just copy&paste it. It allows you to bind functions with arguments to widgets. The more crucial part, here, is the list. You can store tk widgets and variables in lists and they retain their functionality. So, you can get and set their values, move them around and many other things, just by referring to the correct index in the list. I use these lists for questionnaires etc. They are quite useful. – Martin Wettstein Aug 18 '20 at 12:53
  • 1
    You can do this in the `color_checkbutton()` function that is already bound to the event of clicking a checkbox. In this function, you can look at the state of all checkboxes (`.get()` methods of the first element of each list) and do something if the desired values are `1`. At the moment it only checks the current one but the others are just one list index up or down. No problem, there. – Martin Wettstein Aug 18 '20 at 18:48
  • The program works, but I want to understand one thing, what are the attributes of buttons; b and pos? what do they do? – fernando Aug 19 '20 at 17:38
  • 1
    `b` is the `IntVar()` that holds the information on whether the button is selected. You need this first element to check the status of the checkbutton. The `pos` element is a little redundant. It's just the list index of the checkbutton (it's 0 in the first, 1 in the second...). But you could use the `pos` element to store the pair of checkbuttons that are linked if you create two of them. So, you could always check both of them as you know both indices for each of the buttons. – Martin Wettstein Aug 19 '20 at 19:45