tkinter: checkbox doesn't update in for-loop [duplicate] - python

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
I'm stuck on the following problem. With a for-loop, I want to make a few checkboxes that automatically update a label stating whether the checkbox is ticked or not. However, it gives the wrong results (it always says that the checkboxes are ticked, whether this is the case or not; noteworthy is the fact that the checkboxes are unticked by default), see here how the GUI looks like (including error). The IntVars corresponding with the checkboxes are working correctly, as can be seen when ticking at least one of the checkboxes and pressing a button whose function is to read the checkboxes. See also the following code:
import tkinter as tk
top = tk.Tk()
n_passes = 3
checkbox_var = [0] * n_passes
checkbox = [0] * n_passes
def tick_passes(i): # update label saying if checkboxes are ticked
if checkbox_var[i].get == 0:
label = tk.Label(top, text = f"pass #{i} not ticked")
else:
label = tk.Label(top, text = f"pass #{i} ticked")
label.grid(row = 1, column = i)
def check_checkbox_var(): # check whether checkbox_var[i] is updated
for i in range(n_passes):
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
for i in range(n_passes):
checkbox_var[i] = tk.IntVar() # turn on/off certain passes
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
checkbox[i] = tk.Checkbutton(top, text = f"Tick pass {i}", variable =
checkbox_var[i], command = tick_passes(i))
checkbox[i].grid(row = 0, column = i, sticky=tk.W)
var_button = tk.Button(top, text = "Check checkbox_var", command =
check_checkbox_var).grid(row = 2, column = 0) # check whether checkbox_var[i] is updated
top.mainloop()
Could somebody help me with updating the labels? If there is another way to fix this issue, e.g. with buttons to be pressed instead of checkbuttons to be ticked, that would also work for mee.

i is always 2 because you're actually not running any loop after mainloop is started.
The following kind of works but you need to change something about the labels, because right now all labels are just added on top of each other. You should create them once and then just update the text but I'll leave that part for you.
import tkinter as tk
top = tk.Tk()
n_passes = 3
checkbox_var = [0] * n_passes
checkbox = [0] * n_passes
def tick_passes(): # update label saying if checkboxes are ticked
for i in range(n_passes):
if checkbox_var[i].get() == 0:
label = tk.Label(top, text = f"pass #{i} not ticked")
else:
label = tk.Label(top, text = f"pass #{i} ticked")
label.grid(row = 1, column = i)
def check_checkbox_var(): # check whether checkbox_var[i] is updated
for i in range(n_passes):
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
for i in range(n_passes):
print(i)
checkbox_var[i] = tk.IntVar() # turn on/off certain passes
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
checkbox[i] = tk.Checkbutton(top, text = f"Tick pass {i}", variable =
checkbox_var[i], command = tick_passes)
checkbox[i].grid(row = 0, column = i, sticky=tk.W)
var_button = tk.Button(top, text = "Check checkbox_var", command =
check_checkbox_var).grid(row = 2, column = 0) # check whether checkbox_var[i] is updated
top.mainloop()

Related

Why can't I specific the default selected radiobutton?

I am making a simple GUI using Python's tkinter module, and I'm having a lot of trouble with radiobuttons. I wish to have the first selected by default, but the other two are selected at the start. Additionally, when I just pass the cursor over the window, the first one becomes checked (I do not click) so all 3 show as selected. My code:
import tkinter as tk
class openGUI(object):
def __init__(self):
# title of window
window.title("DUNE LArTPC Simulator")
# label for choices
self.question = tk.Label(window, text = "Do you want to create new or analyse existing data?")
self.question.grid(row = 0, column = 0, columnspan = 3)
# buttons corresponding to choices
self.createBtn = tk.Button(window, text = "Create", command = self.createData)
self.analyseBtn = tk.Button(window, text = "Analyse", command = self.analyseData)
self.createBtn.grid(row = 1, column = 0)
self.analyseBtn.grid(row = 1, column = 2)
def analyseData(self):
"""
Not implemented yet.
"""
pass
def createData(self):
# edit window to display new widgets (irritating to have lots of windows open!)
window.title("Select variable")
self.question.destroy()
self.createBtn.destroy()
self.analyseBtn.destroy()
# text in window
variableQ = tk.Label(window, text = "Please select Independent variable for dataset:")
variableQ.grid(row = 0, column = 0, columnspan = 3)
# radioselect variable
selection = tk.StringVar()
selection.set("lifetime")
# radioselect buttons
lifetimeBtn = tk.Radiobutton(window, variable = selection, value = "lifetime", text = "Lifetime")
elecNoiseBtn = tk.Radiobutton(window, variable = selection, value = "electronic", text = "Electronic Noise")
radioactivityBtn = tk.Radiobutton(window, variable = selection, value = "radioactive", text = "Radioactivity")
lifetimeBtn.grid(row = 1, column = 0)
elecNoiseBtn.grid(row = 1, column = 1)
radioactivityBtn.grid(row = 1, column = 2)
# create window
window = tk.Tk()
# create class object with methods to populate
# window with widgets
initWin = openGUI()
# enter mainloop
window.mainloop()
Running the above gives me:
I have tried using lifetimeBtn.select() method instead of setting the StringVar(), but this does not seem to work either. What have I missed?
EDIT: added rest of code to show how I am using class and functions to manipulate the window.
It is because selection is a local variable inside createData() function and it will be garbage collected after function completes.
Change selection to instance variable self.selection.

Need help completing code for password conversion

I'm very new at Python and need some help finishing the code. This is Tkiner related. I have an entry box, a button, and a lower frame for the output.
def loop_over_input(the_str=''):
master_list = []
for char in the_str:
tmp_char = passwordConversion[char]
master_list.append(tmp_char)
print("Master Pass List: ", master_list)
return master_list
This will work in command line with a couple of other lines. I'm not sure how tell it when I put text in the entry field and click the button to return the results in my lower frame. I have moved def loop_over_input to different parts of the code I think I may need to reference the test entry box and the button and the lower box.
I will post the complete code if requested to do so.
Firstly, you need to indent your code correctly. Everything that is in the function loop_over_input needs to be indented once more than the line def loop_over_input(the_str=''):
A few other notes. If you look up the documentation for the tkinter button, it will explain how to link a command to it. The piece of code you have supplied appears to be what you want the button command to be. Printing the list will do so in the shell, not in a frame below your entry field and button.
Here's some example code that should do what you want:
import tkinter as tk
# Creating tk window
window = tk.Tk()
# Master list
master_list = []
master_list_string = tk.StringVar()
# Frames
top_frame = tk.Frame(window)
top_frame.pack(expand = True, fill = 'x', pady = 10, padx = 10)
bottom_frame = tk.Frame(window)
bottom_frame.pack(expand = True, fill = 'both', padx = 10)
# Entry box
myEntry = tk.Entry(top_frame)
myEntry.pack(side = 'left',expand = True, fill = 'x', padx = 10)
# Label to display master list
myLabel = tk.Label(bottom_frame, textvariable = master_list_string)
myLabel.pack(expand = True, fill = 'both')
# Button to submit
def clicked():
master_list.append(myEntry.get())
myEntry.delete(0, 'end')
printed_list = ''
for password in master_list:
printed_list += "\n" + password
master_list_string.set(printed_list)
myButton = tk.Button(top_frame, text = "Submit", command = clicked)
myButton.pack(side = 'left', padx = 10)
# Mainloop
window.mainloop()
The two frames allow you to have the top section with your entry and button, while the bottom frame is for your output. However, you cannot just use a frame as your output, as your frame can't display text. Instead, use a Label widget linked to a StringVar which allows the text in the Label to update when the variable is changed.
The button command then takes the string entered into the entry, saves it to the master list and then sets the StringVar to the updated list, which automatically updates the Label.
I would highly recommend ready the documentation on Effbot, it's quite easy to understand with good examples. Link here

overlapping statuses using tkinter module

I am trying to work on GUI using tkinter module. I created label with random greetings generator. However,they are overlapping with previous generated labels.This is the code:
import tkinter
import random
window = tkinter.Tk()
# to rename the title of the window
window.title("GUI")
window.geometry("500x500")
#defining Functions
def search_greetings():
phrases = ["Hallo ", "Hoi ", "Greetings "]
name = str(entry1.get())
text = ".Please enter your search term below."
return phrases[random.randint(0, 2)] + name + text
def search_display():
greeting = search_greetings()
# This creates the text field
greeting_display = tkinter.Label(window,text = search_greetings())
greeting_display.grid(row=6,column=1)
search_box = tkinter.Entry()
search_box.grid(row=7)
# pack is used to show the object in the window
label = tkinter.Label(window, text = "Hello World! Welcome to my app")
label.grid(row = 0)
# creating 2 text labels and input labels
tkinter.Label(window, text = "Username").grid(row = 2) # this is placed in 1 0
# 'Entry' is used to display the input-field
entry1 = tkinter.Entry()
entry1.grid(row = 2, column = 1) # this is placed in 1 1
tkinter.Label(window, text = "Password").grid(row = 3) # this is placed in 2 0
tkinter.Entry().grid(row = 3, column = 1) # this is placed in 2 1
# 'Checkbutton' is used to create the check buttons
tkinter.Checkbutton(window, text = "Keep Me Logged In").grid(columnspan = 2) # 'columnspan' tells to take the width of 2 columns
# you can also use 'rowspan' in the similar manner
# Submit button
button = tkinter.Button(text = "Submit",command = search_display).grid(row = 5)
window.mainloop()
It is returning the labels like below:
Greetings 1234.Please enter your search term below.
G Hallo ashita.Please enter your search term below.v.
G Hallo ashita.Please enter your search term below..v.
Please check the error in the code.
Seems like you are making a new Label every time. You can edit a Label's text like so:
mylabel = tkinter.Label(root, text="First!")
mylabel["text"] = "Second!"
This will display "Second!" (after being packed). You can even change the text after the Label is packed.

How to make each button display a different image on click using tkinter (python)

I am creating a GUI with a ".grid" of buttons. And I want to make each of those buttons display a different image on press. So when I click button 1, it will bring up "image1", on the bottom of the buttons. When I click button 2, it will bring up "image2" and etc.
Through some research, I was able to make the buttons run a function that takes in a parameter through the method below. However, I can't seem to make the button display the image. rather, it just makes an empty white space underneath the buttons, when I press any buttons.
Disclaimer:
- I do not want there to be loads of images, there will only be 1 image, and it will change depending on what button i press.
Here's the code:
from tkinter import *
def funct(numimg):
image = PhotoImage(file="image"+str(numimg)+".png")
label = Label(image=image)
label.grid(row = row_no+1, column = 0, columnspan = num_of_cols)
def make_funct(number):
return (lambda: funct(number))
root= Tk()
row_no = -1
buttons = []
num_of_cols = 3
root.resizable(0, 0)
numfiles = 6
for x in range(0, numfiles):
if(x % num_of_cols is 0):
row_no+=1
buttons.append(Button(root, text = "Button "+str(x), bg = '#4098D3', width = 30,height = 13,command = make_funct(x)))
buttons[x].grid(row = row_no, column = x % num_of_cols)
root.mainloop()
So my question is, how do you make each individual button, display a different image when it is pressed? this program right here just leaves an empty white space in replace of the image, and the image is not shown.
There are two major problems with the code you posted.
The first is basically the same as in this question: Why does Tkinter image not show up if created in a function?. You should keep a reference to the PhotoImage object, else it will be garbage collected and it will not show.
The second is that you create a new Label on every button click. You should only make one Label and change the image with the label.config() method.
I would (without wrapping your GUI in a class, which might be a nicer solution) load all images on initialization, save them in a list as attribute of the label and only change the image upon a button click.
I also removed your make_funct function and replaced it with a lambda, which is the most used way to pass variables to a function on callback.
from tkinter import *
def funct(numimg):
label.config(image=label.images[numimg])
root= Tk()
row_no = -1
buttons = []
num_of_cols = 3
root.resizable(0, 0)
numfiles = 3
for x in range(0, numfiles):
if(x % num_of_cols is 0):
row_no+=1
buttons.append(Button(root, text = "Button "+str(x), bg = '#4098D3', width = 30,height = 13, command = lambda n=x: funct(n)))
buttons[x].grid(row = row_no, column = x % num_of_cols)
label = Label(root)
label.grid(row = row_no+1, column = 0, columnspan = num_of_cols)
label.images=[]
for x in range(0, numfiles):
label.images.append(PhotoImage(file="image"+str(x)+".png"))
root.mainloop()

Replacing Buttons linked to input boxes with checkboxes

This is my current code for selecting different options and have them appearing in the box (Minecraft ArmorStand Generator).
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
inputbox.insert(45, NoAI)
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
Button(text="Add NoAI",command=addNOAI,relief = FLAT, bg = "#eF651A", fg = "white", width= 25, height = 2).place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
What I'd like to to do is replace the buttons with tick boxes, to prevent the buttons being pressed multiple times. If they click the button, add the text, if they untick, remove it.. I'm not sure where to start with this so any hint in the right direction would be nice.
I've got a working solution, you can try it below.
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
inputbox.delete(0, 7) #delete the text
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
var = IntVar() #sets up variable for check button
c = Checkbutton(text="Add NoAI", command=addNOAI, variable=var) #defining check button variable and command
c.place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
The only problem is at the moment, you are deleting everything in the entry box (more accurately from position 0, to position 7). I assume that there will be multiple check buttons, all adding their own strings to the entry box.
As a solution, I would suggest extracting everything from the entry box, finding the string you want, taking it out, and putting everything back in again. Here's an example.
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
contents = inputbox.get() #gets all of contents
position = contents.find(NoAI) #finds the first position of desired string to remove
newcontents = contents[:position]+contents[position+7:] #gets string before the word, and after the word, and joins them
inputbox.delete(0, 'end') #clears input box for new entry
inputbox.insert(45, newcontents) #re-inserts the string
Here, when the user unchecks the box, the program finds the starting position of the string within the contents of the inputbox. Because you know how long the string will be (in this case 7), you can remove the string from the current contents of the inputbox, and place this inside a new variable. Now you have a new string, without the one that was unchecked, you can clear the inputbox, and put the new one in.
Hope this helps!

Categories

Resources