Automatically switching checkboxes on tkinter - python-3.x

I would like to switch on the checkboxes (0, 2, 4) automatically with the click of a button. I have the following code. For some reason it dont work. Please help me.
from tkinter import *
class Error(Frame):
def Widgets(self):
for i in range(len(self.X)):
self.X[i] = Checkbutton(self, text="%d"%(i,))
self.X[i].grid(row=i, sticky=W)
self.X[i].configure(variable = ("var_%d"%(i,)))
self.button = Button(self, text = "set", command = self.test)
self.button.grid(row=5, sticky=W)
def test(self):
for i in range(len(self.X)):
if i == 0 or i == 2 or i == 4:
set (("var_%d"%(i,))) == 1
def __init__(self,initial):
super(Error,self).__init__(initial)
self.X = [{},{},{},{},{}]
self.grid()
self.Widgets()
Window = Tk()
Tool = Error(Window)
Window.mainloop()

The way to handle checkboxes is to associate each box with a variable which reflects wether the box is checked or not.
For an array of checkboxes it is convenient to store these variables in a list. The way I would do it is to create an empty list and then append variables as I go along.
In the function test() I use enumerate in the for-loop as this is the recommended way to generate an index of the list.
from tkinter import *
class Error(Frame):
def __init__(self, master):
super(Error,self).__init__(master)
self.box_list = [] # List to holld checbox variables
self.grid()
self.Widgets()
def Widgets(self):
for i in range(5):
var = BooleanVar() # Create variable to associate with box
cb = Checkbutton(self, text="%d"%(i,))
cb.grid(row=i, sticky=W)
cb.configure(variable=var)
self.box_list.append(var) # Append checkbox variable to list
self.button = Button(self, text = "set", command = self.test)
self.button.grid(row=5, sticky=W)
def test(self):
for i, var in enumerate(self.box_list):
if i == 0 or i == 2 or i == 4:
var.set(True)
Window = Tk()
Tool = Error(Window)
Window.mainloop()

Related

Two tkinter Objects act differently when bound on the same event

I am creating instances of two different objects (features_object and line_object) on a canvas. The idea is to connect several of the features by lines (). Later on when features are moved around the lines should follow.
At the moment my issue is that, when I apply the delete method on the lines I can delete them (Lines created between features by left mouse click. Line deleted by right mouse double click). However with the similar method of the feature class nothing happens! I cannot delete the features! The features seem invisible on the canvas! Is there any solution to that?
Thanks a lot!
from tkinter import Tk, Frame, Canvas, Text, END, TRUE, BOTH, CURRENT
click = 0
class line_object:
all_connections = []
def __init__(self):
self.connections_data = []
def create_line(self, master, start_elem, end_elem, tag_start, tag_end):
self.master = master
self.start_elem = start_elem
self.end_elem = end_elem
self.tag_start = tag_start
self.tag_end = tag_end
self.start_x = self.master.coords(self.start_elem)[0]
self.start_y = self.master.coords(self.start_elem)[1]
self.end_x = self.master.coords(self.end_elem)[0]
self.end_y = self.master.coords(self.end_elem)[1]
self.line_tag = (f'Connector_{self.start_elem}_{self.end_elem}', 'connection', 'drag')
self.master.delete(self.line_tag)
self.line_id = self.master.create_line(self.start_x, self.start_y, self.end_x, self.end_y, fill="red", width=6, tags=(self.line_tag, self.tag_start, self.tag_end))
self.master.tag_lower(self.line_id)
self.bind_events_lines()
def bind_events_lines(self):
self.master.bind('<Double Button-3>', self.deleteLine)
def deleteLine(self, event=None):
actual = self.master.find_withtag(CURRENT)
self.master.delete(actual)
class features_object():
def __init__(self):
self.features_data = []
def create_feature(self, master, feature_text, feature_xpos, feature_ypos, feature_color):
self.master = master
self.feature_text = feature_text
self.feature_xpos = feature_xpos
self.feature_ypos = feature_ypos
self.feature_color = feature_color
self.feature_frame = Frame(self.master, bg=self.feature_color, border=20)
self.feature_text = Text(self.feature_frame, font=("Helvetica", 12), relief='flat', width=20, height=3, selectbackground=self.feature_color, selectforeground="black", exportselection=TRUE)
self.feature_text.grid(row=0, column=0, padx=5, pady=5, sticky='w')
self.feature_text.config(wrap='word')
self.feature_text.insert(END, feature_text)
self.feature_tags = 'drag'
self.feature_id = self.master.create_window(self.feature_xpos, self.feature_ypos, window=self.feature_frame, tags=self.feature_tags)
self.master.itemconfigure(self.feature_id, tags=[self.feature_id])
self.bind_events_features()
def bind_events_features(self):
self.feature_frame.bind('<Button-1>', self.draw_line_with_bind)
self.feature_frame.bind('<Double Button-3>', self.deleteFeature)
def deleteFeature(self, event):
actual = self.master.find_withtag(CURRENT)
self.master.delete(actual)
def draw_line_with_bind(self, event):
global click, start_x, start_y, end_x, end_y, elem_start, tag_start
if click == 0:
elem_start = self.feature_id
tag_start = f'{elem_start}'
click = 1
elif click == 1:
elem_end = self.feature_id
tag_end = f'{elem_end}'
lin = line_object()
self.connection = lin.create_line(draw_canvas, elem_start, elem_end, tag_start, tag_end)
click = 0
window = Tk()
window.geometry("1000x800")
frame_start = Frame(window)
frame_start.pack(expand=TRUE, fill=BOTH)
draw_canvas = Canvas(frame_start)
draw_canvas.pack(expand=TRUE, fill=BOTH)
features_object().create_feature(draw_canvas, 'feature A', 100, 100, 'red')
features_object().create_feature(draw_canvas, 'feature B', 300, 300, 'green')
features_object().create_feature(draw_canvas, 'feature C', 500, 500, 'magenta')
def read_id(event):
currently_clicked = draw_canvas.find_withtag(CURRENT)
print(f'Currently clicked instance {currently_clicked}')
draw_canvas.bind('<Button-2>', read_id)
if __name__ == '__main__':
window.mainloop()

How to get the values of all the OptionMenu widgets within a frame inside a canvas in Tkinter

I'm writing a minimalist image tagging app that will list out all the image files in a specific location alongside a dropdown menu to select the options for tagging the image. Once the images are tagged, I need to save the changes to a JSON file and I've got a button for that. How can we read all the options selected so that it can be written into a file?
Following is the code so far:
from tkinter import N, RIGHT, Button, OptionMenu, Scrollbar, StringVar, Tk, Canvas, Frame, Label
class App:
def __init__(self):
self.root = Tk()
self.tags = ['Apples', 'Oranges', 'Berries']
self.GetRows()
self.SaveButton()
self.root.mainloop()
def GetRows(self):
self.canvas = Canvas(self.root)
self.scroll_y = Scrollbar(self.root, orient="vertical", command=self.canvas.yview)
self.frame = Frame(self.canvas)
lst = [f"A01{str(i)}.JPG" for i in range(100)]
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# put the frame in the canvas
self.canvas.create_window(0, 0, anchor='nw', window=self.frame)
# make sure everything is displayed before configuring the scrollregion
self.canvas.update_idletasks()
self.canvas.configure(scrollregion=self.canvas.bbox('all'),
yscrollcommand=self.scroll_y.set)
self.canvas.pack(fill='both', expand=True, side='left')
self.scroll_y.pack(fill='y', side='right')
def SaveState(self):
pass
def SaveButton(self):
self.save_button = Button(self.root, text="Save Changes", padx=50, pady=10, command=self.SaveState)
self.save_button.pack(side=RIGHT)
if __name__ == '__main__':
App()
The SaveState method is what will be used to write the selections so far into a file.
Thanks in advance!
In order to make OptionMenu results available try modifying your code so that
all StringVars are accessible outside of GetRows.
def GetRows(self):
...
# Define label as a list
self.label = []
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# Save StringVar reference
self.label.append(label)
...
def SaveState(self):
self.data = dict()
# Retrieve results into dictionary
for i, a in enumerate(self.label):
self.data[f"A_{i}"] = a.get()
print(self.data)
Then use json.dump(self.data, a_file) to save it

Tkinter buttons not changing back to the correct color after state changing to active

I am making this PDF tool, and I want the buttons to be disabled until a file or files are successfully imported. This is what the app looks like at the launch:
Right after running the callback for the import files button, the active state looks like this:
I want the colors of the buttons to turn maroon instead of the original grey. They only turn back to maroon once you hover the mouse over them. Any thoughts for how to fix this? Here is the callback for the import button:
def import_callback():
no_files_selected = False
global files
files = []
try:
ocr_button['state'] = DISABLED
merge_button['state'] = DISABLED
status_label.pack_forget()
frame.pack_forget()
files = filedialog.askopenfilenames()
for f in files:
name, extension = os.path.splitext(f)
if extension != '.pdf':
raise
if not files:
no_files_selected = True
raise
if frame.winfo_children():
for label in frame.winfo_children():
label.destroy()
make_import_file_labels(files)
frame.pack()
ocr_button['state'] = ACTIVE
merge_button['state'] = ACTIVE
except:
if no_files_selected:
status_label.config(text='No files selected.', fg='blue')
else:
status_label.config(text='Error: One or more files is not a PDF.', fg='red')
status_label.pack(expand='yes')
import_button = Button(root, text='Import Files', width=scaled(20), bg='#5D1725', bd=0, fg='white', relief='groove',
command=import_callback)
import_button.pack(pady=scaled(50))
I know this was asked quite a while ago, so probably already solved for the user. But since I had the exact same problem and do not see the "simplest" answer here, I thought I would post:
Just change the state from "active" to "normal"
ocr_button['state'] = NORMAL
merge_button['state'] = NORMAL
I hope this helps future users!
As I understand you right you want something like:
...
ocr_button['state'] = DISABLED
ocr_button['background'] = '#*disabled background*'
ocr_button.bind('<Enter>', lambda e:ocr_button.configure(background='#...'))
ocr_button.bind('<Leave>', lambda e:ocr_button.configure(background='#...'))
merge_button['state'] = DISABLED
merge_button['background'] = '#*disabled background*'
merge_button.bind('<Enter>', lambda e:ocr_button.configure(background='#...'))
merge_button.bind('<Leave>', lambda e:ocr_button.configure(background='#...'))
...
...
ocr_button['state'] = ACTIVE
ocr_button['background'] = '#*active background*'
ocr_button.unbind('<Enter>')
ocr_button.unbind('<Leave>')
merge_button['state'] = ACTIVE
merge_button['background'] = '#*active background*'
merge_button.unbind('<Enter>')
merge_button.unbind('<Leave>')
...
If there are any errors, since I wrote it out of my mind or something isnt clear, let me know.
Update
the following code reproduces the behavior as you stated. The reason why this happens is how tkinter designed the standart behavior. You will have a better understanding of it if you consider style of ttk widgets. So I would recommand to dont use the automatically design by state rather write a few lines of code to configure your buttons how you like, add and delete the commands and change the background how you like. If you dont want to write this few lines you would be forced to use ttk.Button and map a behavior you do like
import tkinter as tk
root = tk.Tk()
def func_b1():
print('func of b1 is running')
def disable_b1():
b1.configure(bg='grey', command='')
def activate_b1():
b1.configure(bg='red', command=func_b1)
b1 = tk.Button(root,text='B1', bg='red',command=func_b1)
b2 = tk.Button(root,text='disable', command=disable_b1)
b3 = tk.Button(root,text='activate',command=activate_b1)
b1.pack()
b2.pack()
b3.pack()
root.mainloop()
I've wrote this simple app that I think could help all to reproduce the problem.
Notice that the state of the button when you click is Active.
#!/usr/bin/python3
import sys
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__()
self.parent = parent
self.init_ui()
def cols_configure(self, w):
w.columnconfigure(0, weight=0, minsize=100)
w.columnconfigure(1, weight=0)
w.rowconfigure(0, weight=0, minsize=50)
w.rowconfigure(1, weight=0,)
def get_init_ui(self, container):
w = ttk.Frame(container, padding=5)
self.cols_configure(w)
w.grid(row=0, column=0, sticky=tk.N+tk.W+tk.S+tk.E)
return w
def init_ui(self):
w = self.get_init_ui(self.parent)
r = 0
c = 0
b = ttk.LabelFrame(self.parent, text="", relief=tk.GROOVE, padding=5)
self.btn_import = tk.Button(b,
text="Import Files",
underline=1,
command = self.on_import,
bg='#5D1725',
bd=0,
fg='white')
self.btn_import.grid(row=r, column=c, sticky=tk.N+tk.W+tk.E,padx=5, pady=5)
self.parent.bind("<Alt-i>", self.switch)
r +=1
self.btn_ocr = tk.Button(b,
text="OCR FIles",
underline=0,
command = self.on_ocr,
bg='#5D1725',
bd=0,
fg='white')
self.btn_ocr["state"] = tk.DISABLED
self.btn_ocr.grid(row=r, column=c, sticky=tk.N+tk.W+tk.E,padx=5, pady=5)
r +=1
self.btn_merge = tk.Button(b,
text="Merge Files",
underline=0,
command = self.on_merge,
bg='#5D1725',
bd=0,
fg='white')
self.btn_merge["state"] = tk.DISABLED
self.btn_merge.grid(row=r, column=c, sticky=tk.N+tk.W+tk.E,padx=5, pady=5)
r +=1
self.btn_reset = tk.Button(b,
text="Reset",
underline=0,
command = self.switch,
bg='#5D1725',
bd=0,
fg='white')
self.btn_reset.grid(row=r, column=c, sticky=tk.N+tk.W+tk.E,padx=5, pady=5)
b.grid(row=0, column=1, sticky=tk.N+tk.W+tk.S+tk.E)
def on_import(self, evt=None):
self.switch()
#simulate some import
self.after(5000, self.switch())
def switch(self,):
state = self.btn_import["state"]
if state == tk.ACTIVE:
self.btn_import["state"] = tk.DISABLED
self.btn_ocr["state"] = tk.NORMAL
self.btn_merge["state"] = tk.NORMAL
else:
self.btn_import["state"] = tk.NORMAL
self.btn_ocr["state"] = tk.DISABLED
self.btn_merge["state"] = tk.DISABLED
def on_ocr(self, evt=None):
state = self.btn_ocr["state"]
print ("ocr button state is {0}".format(state))
def on_merge(self, evt=None):
state = self.btn_merge["state"]
print ("merge button state is {0}".format(state))
def on_close(self, evt=None):
self.parent.on_exit()
class App(tk.Tk):
"""Main Application start here"""
def __init__(self, *args, **kwargs):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_style()
self.set_title(kwargs['title'])
Main(self, *args, **kwargs)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self, title):
s = "{0}".format('Simple App')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
self.destroy()
def main():
args = []
for i in sys.argv:
args.append(i)
kwargs = {"style":"clam", "title":"Simple App",}
app = App(*args, **kwargs)
app.mainloop()
if __name__ == '__main__':
main()

How to retrieve value from selected radiobutton after root.mainloop()?

I am looking to write a pop-up window which asks the user to select a specific option, and if the option does not exist, to add it. However, I am having trouble retrieving the value of the selected option (that is, the key from the dict). My code --summarized-- so far:
import tkinter as tk
class Category():
def __init__(self):
self.categories = {1:"Coffee",2: "Tesco"}
def index(...):
# code ... #
root = tk.Tk()
v = tk.IntVar()
# I was thinking this would help:
def quit():
global irow
irow = v.get()
print("Irow is",irow)
root.quit()
tk.Label(root, text="Choose category:").pack()
for key, cat in self.categories.items():
tk.Radiobutton(root, text=cat, variable=v, value=key).pack()
tk.Radiobutton(root, text="Other", variable=v, value=key+1).pack()
# I want to add a text box here so user can add the "Other"
tk.Button(root, text="Close", command=quit)
irow = v.get()
print(irow)
root.mainloop()
print(irow)
# code which uses irow #
Executing this code yields:
0
Irow is 0
0
regardless of what button I select. I expect irow to be 2 is I were to select Tesco or 1 if I selected coffee (or 3 if I selected other). Any guidance would be very much appreciated.
Typically, mainloop only exits after all of the widgets have been destroyed. Therefore, you can't directly get values from the widgets at this point. The simplest solution is to save the value to a global variable, or an instance variable if you're using classes.
For example, in your case you could do this:
def quit():
self.irow = v.get()
root.quit()
Then, after mainloop exists you can access self.irow
...
root.mainloop()
print(self.irow)
Thank you to Bryan Oakley for the answer. I have made 4 changes so it runs on the Linux terminal: 1) I changed the class from Tk() to Toplevel();
2) I put the dialog into a class for cleanliness' sake; 3) I changed self.quit() to self.destroy(); & 4) I changed mainloop() to wait_window().
class AddCategory(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self)
self.v = tk.IntVar()
self.parent = parent
tk.Label(self, text="Choose category:").pack()
for key, cat in self.parent.categories.items():
tk.Radiobutton(self, text=cat, variable=self.v, value=key).pack()
tk.Radiobutton(self, text="Other", variable=self.v, value=key+1).pack()
self.e = tk.Entry(self,text="")
self.e.pack()
tk.Button(self, text="Close", command=self.quit).pack()
def quit(self):
self.parent.key = self.v.get()
self.parent.cat = self.e.get()
print(self.v.get())
print(self.e.get())
self.destroy()
Note that parent is the class from which I am executing the "AddCategory" dialogue. I invoke it as follows:
class Category():
def __init__(self):
self.cat = self.key = self.desc = []
self.categories = {1:"Coffee",2: "Tesco"}
self.descriptions = {}
def index(...):
# code ... #
dialog = AddCategory(self) # I pass the parent onto the dialog
dialog.wait_window()
print(self.key)
print(self.cat)
This works because self.parent inside of AddCategory is a soft copy of parent.

I am having trouble making an input text box that goes through a function and outputs test to an output textbox with Tkinter

im new to Python3.6.2
So I want a program that takes an input (Via text box from tkinter) and outputs a word in my custom "language"
with this function
def Mescre(n):
Words = (n)
Mes = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'ektnopzcamjqwyuxsbfdiglhrv')
print(Words.translate(Mes))
and here's what i want the window to look like
from tkinter import*
root = Tk()
Mescre = Label(root, text="Input:")
English = Label(root , text="Output:")
label1.grid(row=0, sticky=E)
label2.grid(row=1, sticky=E)
entry1 = Entry(root)
entry2 = Entry(root)
entry1.grid = (row=0, column=1)
entry2.grid = (row=1, column=1)
root.mainloop()
if "hello" was in the Input text box, i want the output to be "coqqu" in the Output text box.
See my example below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.sv = StringVar()
self.Mes = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'ektnopzcamjqwyuxsbfdiglhrv')
self.entry = Entry(self.root, textvariable = self.sv)
self.label = Label(self.root)
self.entry.pack()
self.label.pack()
self.sv.trace("w", self.callback)
def callback(self, *args):
self.label.configure({"text": self.entry.get().translate(self.Mes)})
root = Tk()
App(root)
root.mainloop()
Here we define a StringVar() to be the value of the attribute textvariable for the Entry widget.
We then assign a callback to a trace() on the variable so that whenever the variable is updated (When someone types in the Entry) we call callback().
Within callback() we use configure() on the Label widget in order to set the text to equal the post translation version of the value of the Entry widget.
This creates a "live updating" translation effect.
Here's a basic example:
import tkinter as tk
root = tk.Tk()
def Mescre():
val = textfield.get()
Words = (val)
Mes = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'ektnopzcamjqwyuxsbfdiglhrv')
print(Words.translate(Mes))
textfield = tk.Entry(root)
textfield.pack()
button = tk.Button(root, command=Mescre, text='Push')
button.pack()
root.mainloop()
Updated:
import tkinter as tk
root = tk.Tk()
def Mescre():
val = textfield.get()
Words = (val)
Mes = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'ektnopzcamjqwyuxsbfdiglhrv')
translation = Words.translate(Mes)
#print(translation)
outputfield.delete(0, tk.END)
outputfield.insert(0, translation)
textfield = tk.Entry(root)
textfield.pack()
outputfield = tk.Entry(root)
outputfield.pack()
button = tk.Button(root, command=Mescre, text='Push')
button.pack()
root.mainloop()

Resources