I am new to tkinter and am trying to create a GUI with a Combobox. If a certain value ('new subgroup') is chosen, other widgets should appear (label_4, label_5 and label_6). If any other value of the combobox is being chosen, label_7 should appear.
The issue that I am having right now, is that this works only the first time! If I change my mind, and chose another value than first chosen, the labels that appeared through my first choice still stay, with the second choice just appearing on top of it.
How can I only see the labels I want to see, even after having changed my mind and chosen another value from the combobox?
The code is somewhat long, but the important part starts at input_3, the rest is just for a bit of context ;).
from tkinter import *
from tkinter import ttk
root = Tk()
#configure the root
root.title('Add nodes')
root.columnconfigure(0, weight = 1)
root.rowconfigure(0, weight = 1)
#add frame on the root
frame_add = ttk.Frame(root)
frame_add.columnconfigure(0, weight = 1)
frame_add.columnconfigure(1, weight = 1)
frame_add.columnconfigure(2, weight = 1)
frame_add.rowconfigure(0, weight = 1)
frame_add.rowconfigure(1, weight = 1)
frame_add.rowconfigure(2, weight = 1)
frame_add.rowconfigure(3, weight = 1)
frame_add.rowconfigure(4, weight = 1)
frame_add.grid(column = 0, row = 0)
#first row
label_1 = ttk.Label(frame_add, text = 'Label (name of the .json file):')
label_1.grid(column = 0, row = 0, sticky = 'E')
label = ttk.Entry(frame_add)
label.insert(0, 'write label here')
label.grid(column = 1, row = 0, columnspan= 2, pady = 5)
#second row
label_2 = ttk.Label(frame_add, text = 'Node (name of the node \
in the graph database):')
label_2.grid(column = 0, row = 1, sticky = 'E')
Node = ttk.Entry(frame_add)
Node.insert(0, 'write node here')
Node.grid(column = 1, row = 1, columnspan = 2, pady = 5)
#third row
label_3 = ttk.Label(frame_add, text = 'Define subgroup of node:')
label_3.grid(column = 0, row = 2, sticky = 'E')
selected_colour = StringVar()
input_3 = ttk.Combobox(frame_add, textvariable = selected_colour, )
input_3['state'] = 'readonly'
input_3['values'] = ['requirement', 'property',
'measurement_calculate', 'measurement_value',
'measurement_signal_characterization',
'measurement_technology', 'calculate_value',
'measured_variables_expression', 'main_function',
'part_function', 'partial_solution',
'test_bed_module', 'electronics',
'mechanical_structure', 'new subgroup']
width = len(max(input_3['values'], key = len)) + 2
input_3['width'] = width
input_3.grid(column = 1, row = 2, columnspan = 2, pady = 5)
#after having chosen a value from input_3, this shows (depending on the value):
label_4 = ttk.Label(frame_add, text = 'Please insert the \
properties and corresponding values of the new subgroup:')
label_5 = ttk.Entry(frame_add)
label_5.insert(4, 'Category')
label_6 = ttk.Entry(frame_add)
label_6.insert(1, 'Carateristic to save')
label_7 = ttk.Entry(frame_add)
label_7.insert(1, 'Test')
#event to read the choice on input 3 and show the different widgets
def retrieve(event):
if 'new subgroup' == selected_colour.get():
label_4.grid(column = 0, row = 3, columnspan = 3)
label_5.grid(column = 0, row = 4, pady = 5)
label_6.grid(column = 1, row = 4, pady = 5)
label_7.destroy()
else:
label_4.destroy()
label_5.destroy()
label_6.destroy()
label_7.grid(column = 0, row = 3, pady = 5)
input_3.bind('<<ComboboxSelected>>', retrieve, "+")
root.mainloop()
Thanks everyone for your help!
Related
This question already has answers here:
I'm getting a TypeError. How do I fix it?
(2 answers)
TypeError: 'function' object is not subscriptable - Python
(4 answers)
Closed 6 months ago.
I'm working on a tkinter GUI API-based application.
I'm attempting to modularize my code in which each function has a specific task to do and some functions just call other functions.
In my case, I bind the search button to the function, search, whose job is to call other API-based functions, such as full_name.
When I did this, an error is thrown, saying that in search full_name() TypeError: 'Label' object is not callable
what's interesting is that
I see no mention of creating instances of Label classes in full_name()
when I bind the search button to function, full_name(), it works just fine. However, I want to bind it to a function that will call all API-based functions, and so far, it's not working
Here's the code below:
#MER for frames placement through .Grid()
#import necessary modules
from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
from tkinter import messagebox
import requests
import json
root = Tk()
root.title("F1 Desktop Application")
root.geometry("500x600")
root.configure(bg="white")
#generate 2022 drivers-list [can scale to drivers-list by changing the]
drivers_list_request = requests.get("http://ergast.com/api/f1/2022/drivers.json")
#initialize empty-list
drivers_list = []
drivers_list_object = json.loads(drivers_list_request.content)
for elements in drivers_list_object["MRData"]["DriverTable"]["Drivers"]:
drivers_list.append(elements["givenName"] + " " + elements["familyName"])
#NEED TO UNDERSTAND PRECEDENCE BETWEEN WIDTH, HEIGHT SETTING, VS SPANS. STICKY MAKES PERFECT SENSE
top_frame = LabelFrame(root, width = 300, height = 125)
top_frame.grid(row = 0, column = 0, columnspan = 2)
top_frame.rowconfigure(0, minsize = 75)
top_frame.columnconfigure(0, minsize = 300)
# Update the Entry widget with the selected item in list
def check(e):
v= entry_box.get()
if v=='':
hide_button(menu)
else:
data=[]
for item in drivers_list:
if v.lower() in item.lower():
data.append(item)
update(data)
show_button(menu)
def update(data):
# Clear the Combobox
menu.delete(0, END)
# Add values to the combobox
for value in data:
menu.insert(END,value)
def fillout(event):
try:
entry_box.delete(0,END)
entry_box.insert(0,menu.get(menu.curselection()))
hide_button(menu)
#handle a complete deletion of entry-box via cursor double tap
except:
pass
def hide_button(widget):
widget.grid_remove()
def show_button(widget):
widget.grid()
def full_name():
user_input = entry_box.get()
lower_user_input = user_input.lower().split(" ")[1]
response = requests.get("http://ergast.com/api/f1/drivers/{}.json".format(lower_user_input))
print(response.status_code)
response_object = json.loads(response.content)
name = response_object["MRData"]["DriverTable"]["Drivers"][0]["givenName"] + " " + response_object["MRData"]["DriverTable"]["Drivers"][0]["familyName"]
print(name)
def search():
full_name()
#once works, then make API request
header_label = Label(top_frame, text = "F1 2022 Drivers App", font = ("Arial bold",14), bg = "white")
header_label.grid(row = 0, column = 0, padx = 30)
search_button = Button(top_frame, text = "search" , command = search)
search_button.grid(row = 1, column = 1)
entry_box= Entry(top_frame)
entry_box.grid(row = 1, column = 0)
entry_box.bind('<KeyRelease>',check)
menu= Listbox(top_frame, height = 4)
menu.grid(row = 2, column = 0)
menu.bind("<<ListboxSelect>>",fillout)
left_frame = LabelFrame(root, width = 250, height = 225, borderwidth = 0, highlightthickness = 2)
left_frame.grid(row = 1, column = 0, sticky = NW, padx = 10, pady = 15)
#left_frame.grid_propagate(False)
left_frame.rowconfigure(0, minsize = 100)
left_frame.columnconfigure(0, minsize = 200)
#left_frame_content = LabelFrame(, width = 125, height = 70)
#left_frame_content.grid(row = 1, column = 1, sticky = W)
basic_info = Label(left_frame, text = "Basic Info ", font = ("Arial bold",14))
basic_info.grid(row = 0, column = 0)
full_name = Label(left_frame, text = "Full Name :")
full_name.grid(row = 1, column = 0, sticky = W)
driver_code = Label(left_frame, text = "Driver Code : ")
driver_code.grid(row = 2, column = 0, sticky = W)
nationality = Label(left_frame, text = "Nationality : ")
nationality.grid(row = 3, column = 0, sticky = W)
bottom_left_frame = LabelFrame(root, width = 250, height = 225)
bottom_left_frame.grid(row = 2, column = 0, sticky = N, padx = 10)
bottom_left_frame.rowconfigure(0, minsize = 50)
bottom_left_frame.columnconfigure(0, minsize = 200)
F1_career = Label(bottom_left_frame, text = "F1 Career ", font = ("Arial bold",14))
F1_career.grid(row = 0, column = 0)
wins = Label(bottom_left_frame, text = "Wins :")
wins.grid(row = 1, column = 0, sticky = W)
wins.configure(text = "Wins :" + " 7")
poles = Label(bottom_left_frame, text = "Poles :")
poles.grid(row = 2, column = 0, sticky = W)
test = Label(bottom_left_frame, text = "test")
test.grid(row = 2, column = 1)
podiums = Label(bottom_left_frame, text = "Podiums :")
podiums.grid(row = 3, column = 0, sticky = W)
podiums.configure(text = "Podiums :" + "Lewis Hamilton")
drivers_championships = Label(bottom_left_frame, text = "Championships :")
drivers_championships.grid(row = 4, column = 0, sticky = W)
bottom_right_frame = LabelFrame(root, width = 250, height = 225)
bottom_right_frame.grid(row = 2, column = 1, sticky = W)
bottom_right_frame.rowconfigure(0, minsize = 50)
bottom_right_frame.columnconfigure(0, minsize = 150)
hide_button(menu)
root.mainloop()
Feel free to ask for more clarity. I would be grateful if anyone has a reason/solution
You have used the same name for a label:
full_name = Label(left_frame, text = "Full Name :")
So it will override the full_name().
Use another name for the label, for example full_name_label:
full_name_label = Label(left_frame, text = "Full Name :")
full_name_label.grid(row = 1, column = 0, sticky = W)
# Generates a frame that also contains all the necessary widgets for all mounts in a list
# Takes the parent window the frame will be part of (TK()),
# the row (int) and column (int) the new frame will be located on the parent window,
# the name of the frame we're creating (string),
# and a list of all the mounts to go into the frame (list of string),
# the complement_checkbox (tkinter.CheckButton())
# Returns (tkinter.LabelFrame)
def generate_widgets(master, frame_name, mount_list, complement_checkbox):
new_frame = LabelFrame(master, text = frame_name, font=("Arial Narrow", 18), padx = 5, pady = 5)
new_frame.grid(row = 0, column = 0, padx = 10, pady = 10, sticky = N + S + E + W)
# We have row weight be equal to the number of mounts per frame
master.rowconfigure(0, weight = len(mount_list))
master.columnconfigure(0, weight = 1)
label_widgets = {}
image = {}
icon_label = {}
attempts_string = {}
spin_widgets = {}
button_widgets = {}
for i in range(len(mount_list)):
full_name = mount_list[i]
mount_name = full_name.split(' - ')[1]
label_widgets[i] = Label(new_frame, text = mount_list[i], font = ("Arial Narrow", 12))
label_widgets[i].grid(row = i, column = 0, sticky = W)
image[i] = PhotoImage(file = "Assets\\" + mount_name + ".png")
icon_label[i] = Label(new_frame, image = image[i])
icon_label[i].grid(row = i, column = 1, sticky = E)
attempts_string[i] = StringVar()
attempts_string[i].set(load_attempts(mount_name))
spin_widgets[i] = Spinbox(new_frame, from_ = 0, to = 1024, width = 5, textvariable = attempts_string[i])
spin_widgets[i].grid(row = i, column = 2, sticky = E)
button_widgets[i] = Button(new_frame,
text = "Calculate",
# j = i saves the current value of i into j when lambda is defined,
# if we don't have this line, the command will always use the value of i when the
# command is called, which will be the last row (i-th)
command = lambda j = i: open_and_save(mount_list[j],
mount_list[j].split(' - ')[1],
spin_widgets[j].get(),
complement_checkbox.get()))
button_widgets[i].grid(row = i, column = 3, sticky = E)
new_frame.rowconfigure(i, weight = 1)
# Column 0 is label, column 1 is spinbox, column 2 is button
new_frame.columnconfigure(0, weight = 1)
new_frame.columnconfigure(1, weight = 1)
new_frame.columnconfigure(2, weight = 1)
new_frame.columnconfigure(3, weight = 1)
return new_frame
I'm using the above code to create a new frame and fill it iteratively with widgets. So the problem I'm having right now is that the images I'm trying to display with icon_label[i] isn't working, and is instead just a blank space. I'm quite sure why this is happening, as when I try adding an image to the frame after the function has already run, the image display. I also noticed that if I make images{} and icon_label{} global, some of the images display, but not all (some frames have none, some only have the last few, or only the last). So I'm just wondering what is going on?
I'm using tkinter in my app and I set a frame with this code:
from tkinter import *
from tkinter import ttk
from tkinter import simpledialog
class View:
def __init__(self,tab):
self.tab1 = tab
self.widgets()
def proxy_checkbox_clicked(self, event=None):
if self.proxyVar.get() == 0:
self.proxyIPTextEdit.config(state=DISABLED)
self.proxyPortTextEdit.config(state=DISABLED)
self.proxyUserTextEdit.config(state=DISABLED)
self.proxyPassTextEdit.config(state=DISABLED)
else:
self.proxyIPTextEdit.config(state=NORMAL)
self.proxyPortTextEdit.config(state=NORMAL)
self.proxyUserTextEdit.config(state=NORMAL)
self.proxyPassTextEdit.config(state=NORMAL)
def widgets(self):
self.mainFrame = Frame(self.tab1)
self.mainFrame.grid(column = 0, row = 0, padx = 0, pady = 0)
# Set Add Frame
self.loginFrame = Frame(self.mainFrame)
self.loginFrame.grid(column = 0, row = 0, padx = 0, pady = 0)
label = Label(self.loginFrame, text = "Name:")
label.grid(column = 0, row = 0, sticky = 'W')
self.nameTextEdit = Entry(self.loginFrame, width = 20)
self.nameTextEdit.grid(column = 1, row = 0)
label = Label(self.loginFrame, text = "Phone:")
label.grid(column = 0, row = 1, sticky = 'W')
self.phoneTextEdit = Entry(self.loginFrame, width = 20)
self.phoneTextEdit.grid(column = 1, row = 1)
self.proxyVar = IntVar()
self.proxyCheckBox = Checkbutton(self.loginFrame, text="Use Proxy", variable=self.proxyVar, command=self.proxy_checkbox_clicked)
self.proxyCheckBox.grid(column = 0, row = 2)
self.proxyFrame = Frame(self.loginFrame)
self.proxyFrame.grid(column = 0, row = 3)
label = Label(self.proxyFrame, text = "IP:")
label.grid(column = 0, row = 0, sticky = 'W')
self.proxyIPTextEdit = Entry(self.proxyFrame, width = 20)
self.proxyIPTextEdit.grid(column = 1, row = 0)
label = Label(self.proxyFrame, text = "Port:")
label.grid(column = 0, row = 1, sticky = 'W')
self.proxyPortTextEdit = Entry(self.proxyFrame, width = 20)
self.proxyPortTextEdit.grid(column = 1, row = 1)
label = Label(self.proxyFrame, text = "User:")
label.grid(column = 0, row = 2, sticky = 'W')
self.proxyUserTextEdit = Entry(self.proxyFrame, width = 20)
self.proxyUserTextEdit.grid(column = 1, row = 2)
label = Label(self.proxyFrame, text = "Password:")
label.grid(column = 0, row = 3, sticky = 'W')
self.proxyPassTextEdit = Entry(self.proxyFrame, width = 20)
self.proxyPassTextEdit.grid(column = 1, row = 3)
self.proxyIPTextEdit.config(state=DISABLED)
self.proxyPortTextEdit.config(state=DISABLED)
self.proxyUserTextEdit.config(state=DISABLED)
self.proxyPassTextEdit.config(state=DISABLED)
self.addButton = Button(self.loginFrame, text="Login", width = 10)
self.addButton.grid(column = 0, row = 4)
When i run it i get this frame, any idea what is the problem? What is missing?
Your problem is that self.proxyFrame fills column 1, of self.loginFrame. By definition, anything in column 2 must be to the right of column 1. One simple solution is to have that frame span two columns - both column 1 and column 2. With that, self.proxyFrame won't force column 1 to be as wide as the whole frame.
self.proxyFrame.grid(column = 0, row = 3, padx=20, pady=20, columnspan=2)
As for the checkbutton... widgets by default are centered in the space allocated to them by grid. If you want it to be left-aligned, add sticky="w" when adding it to the grid.
self.proxyCheckBox.grid(column = 0, row = 2, sticky="w")
The problem i'm having is that it even though it executes, the tkinter window remains blank. and as a side issue the window doesn't refresh to update the Actions checkbutton.
from tkinter import *
Pieces = {}
Actions =[]
class GameCreation(Frame):
def __init(self,master):
super(GameCreation,self).__init__(master)
self.grid()
self.CreatePiece()
#Creating pieces function
def CreatePiece(self):
Label(self,text ="What piece are we working with?").grid(row =0,
column = 0,
sticky = W)
self.piece_entry = Entry(self)
self.piece_entry.grid(row =0,
column = 1,
sticky = W)
Label (self, text = "Tick all the actions which the piece has").grid (row =1,
column = 0,
sticky = W)
self.Actions = BooleanVar()
self.Actions.set(None)
column = 0
row = 4
for action in Actions:
Checkbutton(self,text = action, variable = self.checkButton, value = action).grid( row = row,
column = column,
sticky = W)
if column == 5:
row +=1
column = 0
else:
column +=1
Button(self,text = "Add action", command = self.AddAction).grid(row = 1,
column = 0,
sticky = W)
self.action_entry = Entry(self)
self.action_entry.grid(row = 1, column = 1, sticky = W)
Button (self, text = "Create piece and it's actions", command = Add_to_dict).grid(row =2,
column = 0,
sticky = W)
self.Add_dict = Text(self, width =10, height = 2, wrap = WORD)
self.Add_dict.grid( row = 3, column = 0, columnspan = 4)
This function should add to the Actions list
def addAction(self):
action = self.action_entry.get()
Actions.append(action)
This function should just print the name of the piece and the actions chosen for it
def Add_to_dict(self):
actions = Actions.get()
piece = piece_entry.get()
rules = piece, ":", actions
self.Add_dict.delete(0.0,END)
self.Add_dict.insert(0.0,rules)
You need to use __init__, not __init
Also, if you're using BooleanVar, you must set it to True or False, not None:
self.Actions.set(False)
Also, you have a method named addAction but you are calling it like self.AddAction, and you have a method named Add_to_dict that you are calling like Add_to_dict rather than self.Add_to_dict.
And finally, you don't appear to be creating an instance of GameCreation anywhere.
I'm not sure what I'm doing wrong, but it seems that my radiobutton only shows one option instead of two options(which is what I originally wanted).
PLAYERS = [("Black", "black"),
("White", "white")]
def first_player(self) -> str:
self.firstplayer = tkinter.StringVar(value = 'black')
self._player_text = tkinter.StringVar()
self._player_text.set('Which player moves first: ')
player_label = tkinter.Label(
master = self.root_window, textvariable = self._player_text,
background = 'yellow', height = 1, width = 20, font = DEFAULT_FONT)
player_label.grid(row=1, column = 0, padx = 10, pady=90, sticky = tkinter.W+tkinter.N)
for text,mode in PLAYERS:
first = tkinter.Radiobutton(self.root_window, text = text,
variable = self.firstplayer , value = mode)
first.grid(row = 1, column = 0, padx = 300, pady = 90, sticky = tkinter.W + tkinter.N)
return self.firstplayer.get()
You are placing both radiobuttons in the same place: row 1, column 0. The first one it thus invisible because it is under the second one. If you want the label and two radiobuttons on the same row but different columns, the solution is to give them all the same row, and different columns.
There are many ways to accomplish this, I'll show you one:
column = 1
for text,mode in PLAYERS:
first = tkinter.Radiobutton(self.root_window, text = text,
variable = self.firstplayer , value = mode)
first.grid(row = 1, column = column, sticky = tkinter.W + tkinter.N)
column += 1