I generate dynamically combo box and place all the values inside the combox box using
list values.
When I clicked , clearall button, I want to set all the combox box values to "None"
I tried lot of ways. But it shows error message "AttributeError: 'str' object has no attribute 'set'"
below is my code
from tkinter import *
from tkinter import messagebox as mb
from tkinter import ttk
from tkinter.ttk import Combobox
import tkinter as tk
root = Tk()
root.title("my application")
sizex = 500
sizey = 500
posx = 50
posy = 50
root.wm_geometry("%dx%d+%d+%d" % (sizex, sizey, posx, posy))
value1=['country1','country2','country3','country4','country5',"None"]
comblist =['combo_box1','combo_box2','combo_box3','combo_box4']
y2=80
for comb in comblist:
comb= ttk. Combobox(root, values=value1, state="readonly")
comb.set("country1")
comb.place(x=200,y=y2)
y2=y2+30
def clearvalues():
res = mb.askquestion('ClearValues', "Want to clear all the setting ?")
if res == 'yes' :
for cmb1 in comblist:
cmb1.set("None")
else :
mb.showinfo('Return', 'Cancelled')
Button_clearall = Button(root,text="CLEARALL",command = clearvalues)
Button_clearall.place(x=100, y=10)
root.mainloop()
You are setting comblist to a list of strings. If you want to iterate over comboboxes you need to iterate over the comboboxes, not strings.
There are many ways to do this. In your specific case it's probably easiest to just get rid of the original definition of comblist and replace it with a list of comboxes.
comblist = []
for i in range(4):
comb= ttk. Combobox(root, values=value1, state="readonly")
comblist.append(comb)
...
With that, your code to iterate over comblist will work:
for cmb1 in comblist:
cmb1.set("None")
In the function clearvalues(): You are iterating through the comblist variable and not the actual Combobox's that you have created. That's why "AttributeError: 'str' object has no attribute 'set'" is being returned. You are iterating through a string inside of the lists.
Creating Combobox's through iteration is not advised as accessing them later on is difficult. I would recommend creating 5 individual ComboBox's and going from there.
Edit: Just to make this a little clearer for you. If you change the function clearvalues(): to look like this:
def clearvalues():
res = mb.askquestion('ClearValues', "Want to clear all the setting ?")
if res == 'yes':
for cmb1 in comblist:
print(type(cmb1))
else:
mb.showinfo('Return', 'Cancelled')
Now run the program and click yes to clear all settings. You will see in your terminal that the cmb1 type is a string and not a ComboBox
I'm still a beginner with Tkinter and I'm not quite sure how the Entry widget work. I can't seem to get the value I enter I tried binding the root window to this function but I can't figure out why it's not working.
def get_value(event):
current_obj = root.focus_get()
if (current_obj in entries):
text = current_obj.get()
data.append(text)
You can use get to get the value from the entry.
First you define the entry like this:
e = tk.Entry()
e.pack()
Then you can have a static function which gets the value of the entry by calling entry.get()
def get_entry_value(entry)
entry.get()
Alternatively, if you have multiple entries in your app which are all contained in some iterable:
def get_entries(self, event=None):
data = list()
for e in self.entries:
data.append(e.get())
return data
I wrote a subclass with tkinter widgets. In a for-loop i place few of them in a Frame. This Frame also contains a Label and a Entry.
Now i want to destroy all of my subclass widgets but NOT the Label and the Entry.
I tried it like this:
for child in self.frame.winfo_children():
if child.winfo_class() == "???":
[...]
But I wasnt able to figure out what i have to use, so i will use ??? as a placeholder for this.
I place them in a rule with this Code:
db.execute("SELECT * FROM UsedSystems")
rows = db.fetchall()
i = 0
for row in rows:
image_path = activepath+rows[i][0]
name = rows[i][1]
performance = rows[i][2]
project = rows[i][3]
date = rows[i][4]
self.e10 = CustomWidget(self.frame, image_path, name, performance, project, date)
self.e10.grid(row=1+i,column=0, columnspan=2)
i+=1
Try using the isinstance built-in function to check the class (as shown below):
for child in self.frame.winfo_children():
if not (isinstance (child, Label) or isinstance (child, Entry)):
child.destroy ()
This will destroy any widget if they are not a Label and Entry. However, it cannot distinguish between different Label widgets (for example) and will leave BOTH.
I've been searching around and i am not able to find a proper explanation of the syntax of OptionMenu within Tkinter.
how would i get the current chosen option with in the OptionMenu?
def homeTeamOption(self, frame, sortedList):
def func():
print(homeTeamName)
return
homeTeam = tk.StringVar(frame)
returnValueAwayTeam = []
options = sortedList
homeTeamName = tk.StringVar()
drop = OptionMenu(frame, homeTeamName, *options, command=func())
drop.place(x=200, y= 100, anchor="nw")
To get the value of the OptionMenu you need to get the value of the associated variable. In your case it would be:
homeTeamName.get()
If you want to do this via the command, you must set the option to a reference to the function:
drop = OptionMenu(...command=func)
i'm trying to build multiple option menus sharing the same "base item list". A multiple selection of one item in different menus should not be possible, so all menus have to be updated when an item is selected in one of the available menus.
from tkinter import *
# for example 5 fields
number_of_fields = 5
starting_list = ["item1","item2","item3","item4","item5"]
entry_list = []
option_list = []
option_var = []
def quit():
raise SystemExit()
# if an item is selected in one of the
# menus run this function
def reset_menu(sel_item):
# for each field
for field in range(number_of_fields):
new_list = []
selection = option_var[field].get()
# look for selected items in all menus
# and build new list which contains all
# items from the starting_list minus the
# items which are already selected
# keep the one selected (for a menu itself)
for option in starting_list:
marker = 0
for j in range(number_of_fields):
if(str(option_var[j].get()) == str(option)):
marker = 1
if(marker == 0):
new_list.append(str(option))
else:
pass
if(str(selection) == str(option)):
new_list.append(str(option))
# print new generated item list
# just to be sure it works so far
print("field",field,"new list=",new_list)
# NOW HERE SOMETHING IS WRONG I GUESS
# empty menu
option_list[field]["menu"].delete(0, "end")
# add new menu items
for item in new_list:
option_list[field]['menu'].add_command(label=item, command=lambda value=item:option_var[field].set(value))
root = Tk()
root.title("OptionMenu")
# menu variable for each field
for i in range(number_of_fields):
option_var.append(StringVar(root))
# initial value for each field
for i in range(number_of_fields):
option_var[i].set("")
# create menu for each field
for i in range(number_of_fields):
option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))
# create entry for each field
for i in range(number_of_fields):
entry_list.append(Entry(root))
# build gui
for i in range(number_of_fields):
entry_list[i].grid(row=int(i),column=0,sticky=N+S+W+E)
option_list[i].grid(row=int(i), column=1,sticky=N+S+W+E)
button = Button(root, text="OK", command=quit)
button.grid(row=number_of_fields,column=1,sticky=N+S+W+E)
mainloop()
Now everthing seems to be fine until i try to update the menus. The new menu item lists are generated correctly (see print statement) and the menus have the right items, but after selected one menu, the only menu that changes its selected state is the last one. Any ideas?
Regards Spot
I found your question because I too was trying to complete the same task. After doing a bit of poking around in dir(tkinter), I have found a solution, which you have inspired me to create an account to post.
I have left your original comments in the code for sections that I left unchanged.
First, your code for generating your options is unnecessarily cluttered. Instead of manually populating the list from empty, it seems cleaner to remove items from the full list.
You are currently using tkinter.OptionMenu(). If you instead use tkinter.ttk.OptionMenu(), it has a method called set_menu(*values) that takes any number of values as its arguments and sets the choices of that menu to be those arguments.
If you make the switch, there one thing to note - ttk's OptionMenu does not allow its default value to chosen in the dropdown, so it's recommended to make that value blank, as I have done in the declaration for starting_list.
In order to persist the blank option, I added an additional blank option, in order for it to be selectable. This way, if you mistakenly choose the wrong selection, you can revert your choice.
from tkinter import *
from tkinter.ttk import *
# for example 5 fields
number_of_fields = 5
starting_list = ["","item1","item2","item3","item4","item5"]
entry_list = []
option_list = []
option_var = []
def quit():
raise SystemExit()
# if an item is selected in one of the
# menus run this function
def reset_menu(sel_item):
# for each field
for field in range(number_of_fields):
new_list = [x for x in starting_list]
selection = option_var[field].get()
# look for selected items in all menus
# and build new list which contains all
# items from the starting_list minus the
# items which are already selected
# keep the one selected (for a menu itself)
for option in starting_list[1:6]:
#add selectable blank if option is selected
if (str(selection) == str(option)):
new_list.insert(0,"")
for j in range(number_of_fields):
if(str(selection) != str(option) and str(option_var[j].get()) == str(option)):
new_list.remove(option)
# print new generated item list
# just to be sure it works so far
print("field",field,"new list=",new_list)
#set new options
option_list[field].set_menu(*new_list)
root = Tk()
root.title("OptionMenu")
# menu variable for each field
for i in range(number_of_fields):
option_var.append(StringVar(root))
# initial value for each field
for i in range(number_of_fields):
option_var[i].set("")
# create menu for each field
for i in range(number_of_fields):
option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))
# create entry for each field
for i in range(number_of_fields):
entry_list.append(Entry(root))
# build gui
for i in range(number_of_fields):
entry_list[i].grid(row=int(i),column=0,sticky=N+S+W+E)
option_list[i].grid(row=int(i), column=1,sticky=N+S+W+E)
button = Button(root, text="OK", command=quit)
button.grid(row=number_of_fields,column=1,sticky=N+S+W+E)
mainloop()
Something you may want to look into is making your option generation a bit more efficient. Right now, for n options, you're looping through your menus n^2 times. I would suggest looking at passing the value that was just selected in the callback instead of searching each menu to see what was previously selected.
As an additional minor note, your "OK" button causes a crash. I'm not sure if that was intentional behavior, a quirk in my system, or something else.
I hope this helps!
its been a while and ive found a possible solution for my problem...here is the code:
from tkinter import *
from tkinter import _setit
# for example 5 fields
number_of_fields = 5
starting_list = ["choose","item1","item2","item3","item4","item5"]
entry_list = []
option_list = []
option_var = []
def quit():
raise SystemExit()
# print entry_field text and selected option_menu item
def output():
print("---------------------------------------")
for nr,item in enumerate(entry_list):
if(item.get() != ""):
print(item.get() + " --> " + option_var[nr].get())
print("---------------------------------------")
# if an item is selected in one of the
# menus run this function
def reset_menu(*some_args):
for field in range(number_of_fields):
new_list = []
selection = option_var[field].get()
for option in starting_list[1:]:
marker = 0
for j in range(number_of_fields):
if(str(option_var[j].get()) == "choose"):
continue
if(str(option_var[j].get()) == str(option)):
marker = 1
if(marker == 0):
new_list.append(str(option))
else:
pass
if(str(selection) == str(option)):
new_list.append(str(option))
option_list[field]["menu"].delete(0, "end")
option_list[field]["menu"].insert(0, "command", label="choose", command=_setit(option_var[field], "choose"))
# add new menu items
for i in range(len(new_list)):
option_list[field]["menu"].insert(i+1, "command", label=new_list[i], command=_setit(option_var[field], new_list[i]))
root = Tk()
root.title("OptionMenu")
# menu variable for each field
for i in range(number_of_fields):
option_var.append(StringVar(root))
# initial value for each field
for i in range(number_of_fields):
# set "choose" as default value
option_var[i].set("choose")
# trace each variable and call "reset_menu" function
# if variable change
option_var[i].trace("w", reset_menu)
# create menu for each field
for i in range(number_of_fields):
option_list.append(OptionMenu(root, option_var[i], *starting_list))
# create entry for each field
for i in range(number_of_fields):
entry_list.append(Entry(root))
entry_list[i].insert(0, "entry"+str(i))
# build gui
for i in range(number_of_fields):
entry_list[i].grid(row=int(i), column=0, sticky=N+S+W+E)
option_list[i].grid(row=int(i), column=1, sticky=N+S+W+E)
button1 = Button(root, text="OK", command=quit)
button2 = Button(root, text="PRINT", command=output)
button1.grid(row=number_of_fields, column=0, sticky=N+S+W+E)
button2.grid(row=number_of_fields, column=1, sticky=N+S+W+E)
mainloop()
This solution also runs under python 2.7, just change "from tkinter ..." to "from Tkinter ...".
Please take a look at the smarter solution sephirothrr has posted (see post above)!
Regards
Spot