Tkinter buttons not performing command [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 4 years ago.
I am trying to create a counter for Jeopardy so that my Mom and I can keep score. Currently the program that I am creating is assigning the values to variables and then not performing the command when I press the button in the window. I am running Python 2.7.13.`
import Tkinter as tk
root = tk.Tk()
root.title("Jeopardy Scores")
def ChangeScore(User,Value):
if User == 1:
Score = int(JScore.get())
JScore.set(Score + Value)
#J = JScore.get()
#print J
#SayHi()
else:
Score = int(MScore.get())
MScore.set(Score + Value)
#M = MScore.get()
#print M
#SayHi()
#def SayHi(*args):
#print 'hi'
MainFrame = tk.Frame(root)
MainFrame.grid(column=0, row=0)
MainFrame.columnconfigure(0, weight=1)
MainFrame.rowconfigure(0, weight=1)
JScore = tk.StringVar()
MScore = tk.StringVar()
JScore.set(0)
MScore.set(0)
JL = tk.Label(MainFrame, text = "Joey's Score", padx = 10, pady = 2)
JL.config(bg = 'blue', fg = 'yellow', font = ('Arial',30, 'bold'))
JL.grid(column = 0, row = 0)
ML = tk.Label(MainFrame, text = "Mom's Score", padx = 10, pady = 2)
ML.config(bg = 'blue', fg = 'yellow', font = ('Arial',30, 'bold'))
ML.grid(column = 1, row = 0)
JSS = tk.Label(MainFrame, textvariable=JScore ,padx = 122)
JSS.config(bg = 'blue', fg = 'yellow', font = ('Arial',30, 'bold'))
JSS.grid(column = 0, row = 1)
MSS = tk.Label(MainFrame, textvariable = MScore,padx = 122)
MSS.config(bg = 'blue', fg = 'yellow', font = ('Arial',30, 'bold'))
MSS.grid(column = 1, row = 1)
for i in range(1,6):
Score = tk.IntVar()
Score.set(i*200)
Score1 = 200*i
JButton = tk.Button(MainFrame, textvariable = Score, command =
ChangeScore(1,Score1))
JButton.grid(column = 0, row = 1+i)
MButton = tk.Button(MainFrame, textvariable = Score, command =
ChangeScore(2,Score1))
MButton.grid(column = 1, row = 1+i)
JButton = tk.Button(MainFrame, text = '400', command = ChangeScore(1,400))
JButton.grid(column = 0, row = 7)
root.mainloop()
The code runs and produces this Window
Note that no buttons have been pressed when the picture was taken. It appears that all the buttons are 'being pressed' when the code runs and then nothing happens when i press the buttons afterwards.
I have no experience with Tkinter beyond the small information that has allowed me to do this and I have a bit more experience with Python. I am mainly doing this as an excerise for myself to improve my coding and to acutally use for Jeopardy!. Any help would be appreciate

Here the command parameter for Button should be a callable. You should not call the function yourself and pass the return value to it. Instead, you provide a function that is to be called later.
So change you code to things like
command=lambda: ChangeScore(1, 400)
to create a lambda to be called later will solve the problem.

Related

Create event log list from tkinter button presses

This is my first venture into tkinter programming, so far I have the following code that increases or decreases a number by pressing either button. As you may notice I have started adding an update definition that I'd like to update a results table with the label value each time a button is pressed. I've recently found the lambda expression to add two commands to each button press but can't find an example to build the list:
import tkinter as tk
window = tk.Tk()
def increase():
value = int(lbl_value["text"])
lbl_value["text"] = f"{value + 1}"
def decrease():
value = int(lbl_value["text"])
lbl_value["text"] = f"{value - 1}"
def update():
result_table = []
window.rowconfigure(0, minsize = 100, weight = 1)
window.columnconfigure([0,1,2], minsize = 100, weight = 1)
btn_decrease = tk.Button(master = window, text = "-", command = lambda:[decrease(), update()], bg = 'red', fg = 'white')
btn_decrease.grid(row = 0, column = 0, sticky = "nsew")
lbl_value = tk.Label(master = window, text = "0")
lbl_value.grid(row = 0, column = 1)
btn_increase = tk.Button(master = window, text = "+", command = lambda:[increase(), update()], bg = 'green', fg = 'white')
btn_increase.grid(row = 0, column = 2, sticky = "nsew")
window.mainloop()
, bg = 'black', fg = 'white')
btn_decrease.grid(row = 0, column = 0, sticky = "nsew")
lbl_value = tk.Label(master = window, text = "0")
lbl_value.grid(row = 0, column = 1)
btn_increase = tk.Button(master = window, text = "+", command = increase, bg = 'red', fg = 'white')
btn_increase.grid(row = 0, column = 2, sticky = "nsew")
window.mainloop()
I'd like to add a graph of the count to the display ultimately. Any help greatly appreciated.
Matt

tkinter throws error using a variable for button name to set state

I want to pass a button name to tkinter as a variable. This method works for Text fields, but not for buttons in my code.
I am building a gui app and I want to have a generic clear function that zaps a text entry field and resets a button from NORMAL to DiSABLED.
There are multiple buttons and fields, hence the desire to make this generic.
For the code I have, the buttons are present with the exception of clear all.
I am setting the variable w_button to the specific name of the (existing) button based on what is passed to the function.
def switch_clear(elem_type):
if elem_type == 'scn':
w_button = 'b_clear_scn'
clear_field = 'scn_file_entry'
print ('scenario')
elif elem_type == 'sol2':
w_button = 'b_clear_sol2'
clear_field = 'sol2_file_entry'
print ('sol2')
elif elem_type == 'mdl':
w_button = 'b_clear_mdlList'
clear_field = 'mdlList_file_entry'
print ('mdl')
elif elem_type == 'all':
print ('clear all TBD')
return()
if w_button["state"] == NORMAL:
clear_field.delete(0, END)
w_button["state"] = DISABLED
return()
Here's what happens:
C:\utils>my_frame3.py
scenario
Traceback (most recent call last):
File "C:\utils\my_frame3.py", line 127, in <module>
b_clear_scn = Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
File "C:\utils\my_frame3.py", line 100, in switch_clear
if w_button["state"] == NORMAL:
TypeError: string indices must be integers
C:\utils>
I realize I could duplicate and push the clear operations into the if/elif statements, and I may have to live with that but - is it possible to reference buttons as variables? How can I do this?
Complete code as requested below. The preview is a mess despite using the code widget, but in my file lines are correctly formatted (notepad++). Tx
import os, sys
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
root2 = Tk()
root2.title('Model Processing')
root2.geometry('{}x{}'.format(512, 400))
# colors
color1 = 'light cyan'
color2 = 'gold'
color3 = 'RosyBrown1'
color4 = 'lavender'
color5 = 'linen'
bg_color = 'azure'
# number of items to process; until >1 OK is disabled; if >1 OK is enabled TBD
l_to_do = [] # items to process; scn, sol2, modelList, installed models
# functions/commands
def callback():
print ('A button was clicked.')
return;
def my_exit():
result = messagebox.askquestion("Cancel Model Processing", "Are you sure?", icon='warning')
if result == 'yes':
root2.quit()
else: # user changed mind
pass
def choose_import_file(str_ftype):
which_field = ''
which_clear_btn = ''
ftype = str_ftype
mdl_opts = {}
if ftype == 'scn':
title_msg = 'Choose a scenario file'
mdl_opts['filetypes'] = [('Supported types', ('.scn')),
('scenario files',('.scn'))]
which_field = scn_file_entry
which_clear_btn = 'b_clear_scn'
elif ftype == 'sol2':
title_msg = 'Choose a SOL2 file'
mdl_opts['filetypes'] = [('Supported types', ('.txt')),
('sol2 files',('.txt'))]
which_field = sol2_file_entry
elif ftype == 'mdllist':
title_msg = 'Choose a ModelList file'
print ('TBD: ModelList file')
file_input = filedialog.askopenfilename(title = title_msg, **mdl_opts)
if file_input == '':
print ('error or cancelled by user')
return
else:
f_inp_file = os.path.basename(file_input)
f_inp_file_base = str(file_input.split('.')[0])
f_inp_file_ext = str.lower(str(file_input.split('.')[1]))
f_inp_d_name = os.path.dirname(file_input)
print('File chosen:', f_inp_file_base)
# populate scenario file field
which_field.insert(INSERT,file_input)
which_clear_btn["state"] = NORMAL
# define appropriate clear button active
# define_clear_btn.configure(state = ACTIVE)
return
def switch_clear(elem_type):
if elem_type == 'scn':
if b_clear_scn["state"] == NORMAL:
scn_file_entry.delete(0, END)
b_clear_scn["state"] = DISABLED
elif elem_type == 'sol2':
f b_clear_sol2["state"] == NORMAL:
clear_field = 'sol2_file_entry'
b_clear_sol2["state"] = DISABLED
elif elem_type == 'mdl':
if b_clear_mdlList["state"] == NORMAL:
clear_field = 'mdlList_file_entry'
b_clear_mdlList["state"] = DISABLED
elif elem_type == 'all':
print ('clear all TBD')
return()
return()
# create all of the main containers
first_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
second_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
third_frame = Frame(root2, bg=color5, width=512, height=90, pady=10)
fourth_frame = Frame(root2, bg=color5, width = 512, height = 90, pady=10)
# layout all of the main containers
root2.grid_rowconfigure(3, weight=1)
root2.grid_rowconfigure(2, weight=1)
root2.grid_rowconfigure(1, weight=1)
root2.grid_columnconfigure(0, weight=1)
first_frame.grid(row=0, sticky="ew")
second_frame.grid(row=1, sticky="ew")
third_frame.grid(row=2, sticky="ew")
fourth_frame.grid(row = 3, sticky="e")
# create the widgets for the first frame
#scn_label = Label(first_frame, text = 'Scenario file')
scn_file_entry = Entry(first_frame, background=bg_color, width = 50)
b_choose_scn = Button(first_frame, text = "Choose a scenario..", command = lambda: choose_import_file('scn'), height = 2)
b_clear_scn = Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
# layout the widgets in the first frame
#scn_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
scn_file_entry.grid(row = 0, column = 1, padx = (10,10))
b_choose_scn.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_scn.grid(row=2, column=0, padx = (10,10), sticky=W)
# second frame
# sol2_label = Label(second_frame, text = 'Sol2 file')
sol2_file_entry = Entry(second_frame, background=bg_color, width = 50)
b_choose_sol2 = Button(second_frame, text = "Choose SOL2 file..", command = lambda: choose_import_file('sol2'), height = 2)
b_clear_sol2 = Button(second_frame, text = "Clear SOL2", command = switch_clear('sol2'), height = 2, state=DISABLED)
# layout the widgets in the second frame
# sol2_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
sol2_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_sol2.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_sol2.grid(row=2, column=0, padx = (10,10), sticky=W)
# third frame
# mdlList_label = Label(third_frame, text = 'ModelList.txt file')
mdlList_file_entry = Entry(third_frame, background=bg_color, width = 50)
b_choose_mdlList = Button(third_frame, text = "Choose ModelList.txt file..", command = callback, height = 2)
b_clear_mdlList = Button(third_frame, text = "Clear ModelList", command = callback, height = 2, state=DISABLED)
# layout the widgets in the third frame
#mdlList_label.grid(row = 0, column = 0, padx = (10,10), pady=5, sticky = 'ns')
mdlList_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_mdlList.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_mdlList.grid(row=2, column=0, padx = (10,10), sticky=W)
#####################################################################
# create bottom widgets
#####################################################################
clear_all = Button(fourth_frame, text='Clear All', padx = '5', command = callback, height = 2, state=DISABLED)
ok_btn = Button(fourth_frame, text='OK', padx = '5', command = callback, height = 2, state=DISABLED)
cancel_btn = Button(fourth_frame, text='Cancel', height = 2, padx = '12', command = my_exit)
#####################################################################
# layout the bottom widgets
#####################################################################
clear_all.grid(row = 0, column = 2, sticky = 'e')
ok_btn.grid(row = 0, column = 3, sticky = 'e')
cancel_btn.grid(row = 0, column = 4, sticky = 'e')
# commands/bindings
root2.mainloop()
I believe this is what you are trying to do. The below is fully commented with explanations. switch_clear becomes entirely unnecessary with this method. The buttons directly clear their corresponding entry in their command, and Entry validation takes care of the corresponding button state.
Note: I wrote all of this before you posted your full code
import tkinter as tk
root = tk.Tk()
root.geometry('400x400')
#toggle the state of buttons based on entry text length
def toggle_button(name, text):
global btn_switch
btn_switch[name]['state'] = 'normal' if len(text) else 'disabled'
return True
#to hold the buttons so they are easy to position
#just for this example
buttons = tk.Frame(root)
buttons.pack(side='top', anchor='nw')
#create a tcl wrapper for the validate command
vcmd = tk.Widget.register(root, toggle_button)
#mock-up of your entries ~ validate on key press. send widget name and full text to vcmd
scn_file_entry = tk.Entry(root, width=20)
scn_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
scn_file_entry.pack(side='left', anchor='nw')
sol2_file_entry = tk.Entry(root, width=20)
sol2_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
sol2_file_entry.pack(side='left', anchor='nw')
mdlList_file_entry = tk.Entry(root, width=20)
mdlList_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
mdlList_file_entry.pack(side='left', anchor='nw')
#mock-up of your buttons ~ delete the entry text in a lambda and let entry validation handle the button state
b_clear_scn = tk.Button(buttons, text="scn", state='disabled')
b_clear_scn.configure(command=lambda: scn_file_entry.delete(0, 'end'))
b_clear_scn.pack(side='left', anchor='nw')
b_clear_sol2 = tk.Button(buttons, text="sol2", state='disabled')
b_clear_sol2.configure(command=lambda: sol2_file_entry.delete(0, 'end'))
b_clear_sol2.pack(side='left', anchor='nw')
b_clear_mdlList = tk.Button(buttons, text="mdl", state='disabled')
b_clear_mdlList.configure(command=lambda: mdlList_file_entry.delete(0, 'end'))
b_clear_mdlList.pack(side='left', anchor='nw')
#create a dictionary of 'widget name':corresponding button, for toggle_button to reference
btn_switch = {
f'{scn_file_entry}':b_clear_scn,
f'{sol2_file_entry}':b_clear_sol2,
f'{mdlList_file_entry}':b_clear_mdlList,
}
root.mainloop()

tkinter, Image does not show up unless some function added

I have written a code for a basic game but the image and shape don't show up unless I add something like item.pack() or win.mainloop() [which doesn't really make sense] but then the lines below it don't run.
When I don't have anything, the buttons show up but the image doesn't show up.
import tkinter as tk
import random
from tkinter import messagebox
win = tk.Tk()
my_label = tk.Label(win, text="Color of the Baloon Game")
my_label.pack()
my_canvas = tk.Canvas(win, width=400, height=600)
my_canvas.pack()
background_image=tk.PhotoImage(file = "CS_Game_menu.png")
background_label = tk.Label(my_canvas, image=background_image)
background_label.photo = background_image
background_label.grid(row = 0, rowspan = 10, column = 0, columnspan = 10)
def drawCircle():
color = "green"
x1 = 265
y1 = 80
diameter = 90
my_canvas.destroy()
circle_button.destroy()
quit_button.destroy()
my_label.destroy()
my_label1 = tk.Label(win, text="What is the Color of the Baloon?", font="Purisa")
my_label1.pack()
my_canvas1 = tk.Canvas(win, width=400, height=600)
my_canvas1.pack()
image1 = r"CS_Game_baloon.png"
photo1 = tk.PhotoImage(file=image1)
item = my_canvas1.create_image(200, 350, image=photo1)
shape = my_canvas1.create_oval(x1, y1, x1 + diameter, y1 + diameter+20, fill=color)
item.pack()
game1_button = tk.Button(my_canvas1, text = "Green")
game1_button.grid(row= 8, column = 3)
game1_button["command"] = lambda: messagebox.showinfo("Congratulations!", "Correct Answer!")
game2_button = tk.Button(my_canvas1, text = "Blue")
game2_button.grid(row= 8, column = 5)
game2_button["command"] = lambda: messagebox.showinfo("Sorry!", "Incorrect Answer!")
game3_button = tk.Button(my_canvas1, text = "Red")
game3_button.grid(row= 8, column = 7)
game3_button["command"] = lambda: messagebox.showinfo("Sorry", "Incorrect Answer!")
circle_button = tk.Button(win, text="New Game")
circle_button.pack()
circle_button["command"] = drawCircle
quit_button = tk.Button(win, text="Quit")
quit_button.pack()
quit_button['command'] = win.destroy
You are using both the create_... methods and grid methods on your canvas object. It won't behave as you expected.
To achieve what you want, you can create a Frame, put your buttons in it, and then use create_window method on your canvas:
def drawCircle():
...
shape = my_canvas1.create_oval(x1, y1, x1 + diameter, y1 + diameter+20, fill=color)
frame = tk.Frame(my_canvas1)
game1_button = tk.Button(frame, text = "Green")
game1_button.grid(row= 8, column = 3)
game1_button["command"] = lambda: messagebox.showinfo("Congratulations!", "Correct Answer!")
game2_button = tk.Button(frame, text = "Blue")
game2_button.grid(row= 8, column = 5)
game2_button["command"] = lambda: messagebox.showinfo("Sorry!", "Incorrect Answer!")
game3_button = tk.Button(frame, text = "Red")
game3_button.grid(row= 8, column = 7)
game3_button["command"] = lambda: messagebox.showinfo("Sorry", "Incorrect Answer!")
my_canvas1.create_window(200,500,window=frame)
And of course, add win.mainloop() to the bottom of your program if you haven't already.

python tkinter - over writing label on button press

def button_pressed(item_name, item_price):
global lbl
for v1, v2 in zip(item_name, item_price):
item_values = '{} {}'.format(v1, v2)
sv = StringVar()
lbl = Label(shop_window, height="2", textvariable=sv, anchor = NW).grid(columnspan = 4)
sv.set(item_values)
# Create initial shopping cart window
shop_window = Tk()
shop_window.title('Welcome to the Outlet')
shop_window.geometry("1200x900")
shop_window.resizable(0, 0)
introduction_text = Label(shop_window, text = 'Welcome to the Shopping Outlet', font = ('Arial', 30))
electronics_button = Button(shop_window, text = 'Buy Electronics', font = ('Arial', 18), command = lambda:button_pressed(electronics_name, electronics_price))
books_button = Button(shop_window, text = 'Buy Books', font = ('Arial', 18), command = lambda:button_pressed(books_name, books_price))
kitchen_button = Button(shop_window, text = 'Buy Kitchen', font = ('Arial', 18), command = lambda:button_pressed(kitchen_name, kitchen_price))
monitors_button = Button(shop_window, text = 'Buy Kitchen', font = ('Arial', 18), command = lambda:button_pressed(monitors_name, monitors_price))
introduction_text.grid(row = 0, column = 0, columnspan = 4, sticky = N )
electronics_button.grid(row = 2, column = 0)
books_button.grid(row = 2, column = 1)
kitchen_button.grid(row = 2, column =2)
monitors_button.grid(row = 2, column = 3)
I've created this tk window to display a shopping list of 10 items per category. Each category has two list, item_name/item_price (they have been scraped off amazon.)
When I run the program I can press the button and the list will display properly but if I press it again it adds new labels to the end of the previously made labels. My questions would be how do I make the program overwrite previous labels for example. Press "Buy Electronics" creates my labels as required, but pressing "Buy Books" after adds more labels. I want to over write the "Buy Electronics" labels. I figured it would be some kind of global lbl but unsure.
You could create a frame for the labels
f = Frame(shop_window)
f.grid(row=3, column=0, columnspan=4)
Then in the callback function destroy all children of the frame and create new ones
def button_pressed(item_name, item_price):
for widget in f.winfo_children():
widget.destroy()
for v1, v2 in zip(item_name, item_price):
item_values = '{} {}'.format(v1, v2)
Label(f, height="2", text=item_values).pack()
see the on the example with function have created, to do that you need to overwrite before positioning the current one there by using lbl["text"] = sv
def button_pressed(item_name, item_price):
global lbl
for v1, v2 in zip(item_name, item_price):
item_values = '{} {}'.format(v1, v2)
# sv = StringVar()
lbl["text"] = sv
sv.set(item_values)
Then create you Label inside root and mainloop
shop_window = Tk()
sv = StringVar()
lbl = Label(shop_window, height="2", textvariable=sv, anchor = NW)
lbl.grid(columnspan = 4)
shop_window = Tk()

Multiple Buttons to change the colours of multiple labels TKINTER, PYTHON?

So I have multiple buttons
and i need the buttons of each name:
i.e. Violet button to turn the LABEL above it into violet,
and purple button to turn the above LABEL purple.
AND the reset button resets them all to grey.
AND if someone could fix my code so that the spacing of the "RESET" button is between purple and Blue (but still a row down), that'd be greatly appreciated.
WHAT MY CODE DOES NOW:
It turns all of the boxes all the colours.
How do I make it so when I press the one button, the one label changes colour AND I wish to do this in one function if possible (i've already thought of making multiple functions and thought this would be nicer).
Import the Tkinter functions
from tkinter import *
# Create a window
the_window = Tk()
# Give the window a title
the_window.title('MULTI Button Colour')
#Variables
window_font = ('Arial', 8)
button_size = 10
label_size = 7
margin_size_width = 10
margin_size_height = 2
label_violet = Label(the_window, padx = margin_size_width, pady = margin_size_height, bg = 'grey', width = label_size)
label_violet.grid(row = 0, column = 0)
label_purple = Label(the_window, padx = margin_size_width, pady = margin_size_height, bg = 'grey', width = label_size)
label_purple.grid(row = 0, column = 1)
label_blue = Label(the_window, padx = margin_size_width, pady = margin_size_height, bg = 'grey', width = label_size)
label_blue.grid(row = 0, column = 2)
label_green = Label(the_window, padx = margin_size_width, pady = margin_size_height, bg = 'grey', width = label_size)
label_green.grid(row = 0, column = 3)
def change_colour():
if violet_button:
label_violet['bg'] = 'violet'
if purple_button:
label_purple['bg'] = 'purple'
if blue_button:
label_blue['bg'] = 'blue'
if green_button:
label_green['bg'] = 'green'
# if reset_button:
# label_violet['bg'] = 'grey'
# label_purple['bg'] = 'grey'
# label_blue['bg'] = 'grey'
# label_green['bg'] = 'grey'
violet_button = Button(the_window, text = 'Violet', width = button_size,
font = window_font, command = change_colour)
purple_button = Button(the_window, text = 'Purple', width = button_size,
font = window_font, command = change_colour)
blue_button = Button(the_window, text = 'Blue', width = button_size,
font = window_font, command = change_colour)
green_button = Button(the_window, text = 'Green', width = button_size,
font = window_font, command = change_colour)
reset_button = Button(the_window, text = 'RESET', width = button_size,
font = window_font, command = change_colour)
#----------------------------------------------------------------
violet_button.grid(row = 1, column = 0, padx = margin_size_width, pady = margin_size_height)
purple_button.grid(row = 1, column = 1, padx = margin_size_width, pady = margin_size_height)
blue_button.grid(row = 1, column = 2, padx = margin_size_width, pady = margin_size_height)
green_button.grid(row = 1, column = 3, padx = margin_size_width, pady = margin_size_height)
reset_button.grid(row = 2, column = 1, pady = margin_size_height)
# Start the event loop to react to user inputs
the_window.mainloop()
This should do what you want:
def get_function(cmd):
def change_colour():
if 'violet' == cmd:
label_violet['bg'] = 'violet'
if 'purple' == cmd:
label_purple['bg'] = 'purple'
if 'blue' == cmd:
label_blue['bg'] = 'blue'
if 'green' == cmd:
label_green['bg'] = 'green'
if 'reset' == cmd:
label_violet['bg'] = 'grey'
label_purple['bg'] = 'grey'
label_blue['bg'] = 'grey'
label_green['bg'] = 'grey'
return change_colour
violet_button = Button(the_window, text = 'Violet', width = button_size,
font = window_font, command = get_function('violet'))
purple_button = Button(the_window, text = 'Purple', width = button_size,
font = window_font, command = get_function('purple'))
blue_button = Button(the_window, text = 'Blue', width = button_size,
font = window_font, command = get_function('blue'))
green_button = Button(the_window, text = 'Green', width = button_size,
font = window_font, command = get_function('green'))
reset_button = Button(the_window, text = 'RESET', width = button_size,
font = window_font, command = get_function('reset'))
For the issue with the reset button, just specifie the columnspan argument of the grid function:
reset_button.grid(row = 2, column = 1, pady = margin_size_height, columnspan = 2)
You have a lot of code in there that you copy / paste. Computers are really good at repeating things with a few variables changed. You could argue that that's ALL a computer is good at. So by doing that yourself, you are doing the computer's job. Also, you are making more work for your future self if you want to change something later. It's much better to put things in a single location, so that you can make a single change later instead of changing for every label. I can see you thought about this a little already since you have so many constants. Taking that one step further is to make a "constant" widget that all your instances copy from. It's a little advanced, but here's how you would do that:
import tkinter as tk
# Constants (aka variables that don't change during the program run)
BUTTON_SIZE = 10
FONT=('Arial', 8)
class Corey(tk.Frame):
'''a new widget that combines a Label and a Button'''
instances = []
def __init__(self, master=None, color='grey', **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.color = color
self.lbl = tk.Label(self, bg=color) # set initial color
self.lbl.grid(sticky='nsew') # sticky makes the label as big as possible
btn = tk.Button(self, text=color.title(), width=BUTTON_SIZE, command=self.colorize, font=FONT)
btn.grid()
self.instances.append(self)
def colorize(self):
self.lbl.config(bg=self.color)
#classmethod
def reset(cls):
for widget in cls.instances:
widget.lbl.config(bg='grey')
# Create a window
the_window = tk.Tk()
# Give the window a title
the_window.title('MULTI Button Colour')
# make some Corey widgets
colors = 'violet', 'purple', 'blue', 'green'
for col, color in enumerate(colors):
widget = Corey(the_window, color)
widget.grid(row=0, column=col, padx=10, pady=2)
reset_button = tk.Button(the_window, text = 'RESET', width=BUTTON_SIZE, command=Corey.reset, font=FONT)
reset_button.grid(row=1, column=0, columnspan=len(colors), pady=4)
the_window.mainloop()
The immediate advantage is that in order to add or subtract colors from my version, you only have to change one line. Also, if you want to change the appearance, say add a relief or change the button to a checkbox, again you only have to change a single line. Subclasses like this are a huge part of GUI design, I recommend you jump into this as soon as you can.

Categories