Dynamically create combox and reset values TKinter - python

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

Related

How to insert a scrollbar to a menu?

I want to add another widget, in this case a scale widget, to a menu widget in tkinter.
Right now the only solutions I see are creating a new command and open a new window with the scale widget or creating the scale widget elsewhere. Both don't seem too appealing to me.
Any ideas how to archive this are welcome :)
You cant add a scrollbar to it, but I have coded something similar to this. Its a hacky way and maybe its hard to understand but I can try to explain.
Note as Bryan mentioned in the linked Thread, this seems to be a a Windows only solution.
import tkinter as tk
def my_first_function():
print('first')
def my_second_function():
print('second')
def check_for_scroll(event):
check = root.call(event.widget, "index","active")
if check == 0: #index of button up
scroll_up()
root.after(100,lambda e=event:check_for_scroll(e)) # check again after 100ms
if check == file_menu.index('end'):
scroll_down()
root.after(100,lambda e=event:check_for_scroll(e))
def scroll_up():
index_of_first_command=1
index_of_last_command=1
label_of_last_command = file_menu.entrycget(index_of_first_command, 'label')
try:
for i, k in enumerate(dict_of_commands):
if k == label_of_last_command:
previous_command_label = list(dict_of_commands)[i-1]
previous_command = list(dict_of_commands.values())[i-1]
if i != 0: #avoid to get the last as first
file_menu.delete(index_of_first_command) #first before pull down button
file_menu.insert_command(index_of_first_command,
label=previous_command_label,
command=previous_command)
except Exception as e:
print(e)
def scroll_down():
index_of_first_command=1
index_of_last_command=1
label_of_last_command = file_menu.entrycget(index_of_last_command, 'label')
try:
for i, k in enumerate(dict_of_commands):
if k == label_of_last_command:
next_command_label = list(dict_of_commands)[i+1]
next_command = list(dict_of_commands.values())[i+1]
file_menu.delete(index_of_first_command) #first after pull up button
file_menu.insert_command(index_of_last_command,
label=next_command_label,
command=next_command)
except:
pass
space = ' '
dict_of_commands = {'first' : my_first_function,
'second': my_second_function}
root = tk.Tk()
menubar = tk.Menu(root)
root.config(menu=menubar)
file_menu = tk.Menu(menubar,tearoff=0)
menubar.add_cascade(label='File', menu=file_menu)
file_menu.bind('<<MenuSelect>>', check_for_scroll)
file_menu.add_command(label=space+u'\u25B2'+space, font=["Arial", 8,'bold'])
file_menu.add_command(label='first', command=my_first_function)
file_menu.add_command(label=space+u'\u25BC'+space, font=["Arial", 8,'bold'])
root.mainloop()
So this code creates your window and a menubar on it as usal:
root = tk.Tk()
menubar = tk.Menu(root)
root.config(menu=menubar)
file_menu = tk.Menu(menubar,tearoff=0)
menubar.add_cascade(label='File', menu=file_menu)
file_menu.add_command(label=space+u'\u25B2'+space, font=["Arial", 8,'bold'])
file_menu.add_command(label='first', command=my_first_function)
file_menu.add_command(label=space+u'\u25BC'+space, font=["Arial", 8,'bold'])
root.mainloop()
Important for you, is this line here:
file_menu.bind('<<MenuSelect>>', check_for_scroll)
This line binds the event MenuSelect and it happens/triggers if your cursor hovers over a command of your menu. To this event I have bound a function called check_for_scroll and it looks like this:
def check_for_scroll(event):
check = root.call(event.widget, "index","active")
if check == 0: #index of button up
scroll_up()
root.after(100,lambda e=event:check_for_scroll(e)) # check again after 100ms
if check == file_menu.index('end'):
scroll_down()
root.after(100,lambda e=event:check_for_scroll(e))
The line below checks for the index of the command that has triggered the event. With this we check if its button of our interest like the first or last, with the arrows.
check = root.call(event.widget, "index","active")
if its the first for example this code here is executed:
if check == 0: #index of button up
scroll_up()
root.after(100,lambda e=event:check_for_scroll(e)) # check again after 100ms
it calls/triggers the function scroll_up and uses then the after method of tkinter to retrigger itself, like a loop. The scroll_up function is build like the scroll_down just in the opposite direction. Lets have a closer look:
def scroll_up():
index_of_first_command=1
index_of_last_command=1
label_of_last_command = file_menu.entrycget(index_of_first_command, 'label')
try:
for i, k in enumerate(dict_of_commands):
if k == label_of_last_command:
previous_command_label = list(dict_of_commands)[i-1]
previous_command = list(dict_of_commands.values())[i-1]
if i != 0: #avoid to get the last as first
file_menu.delete(index_of_first_command) #first before pull down button
file_menu.insert_command(index_of_first_command,
label=previous_command_label,
command=previous_command)
except Exception as e:
print(e)
In this function we need to know the first and the last position of commands, because we want to delete one and insert another on that position/index. To achieve this I had created a dictionary that contains the label and the the function of the command item of tkinter like below. (This could be created dynamically, but lets keep it for another question)
dict_of_commands = {'first' : my_first_function,
'second': my_second_function}
So we iterate over this enumerated/indexed dictionary in our function and check if the k/key/label is our item of interest. If true, we get the previous_command by listing the dictionary keys and get the extract the key before by this line:
next_command_label = list(dict_of_commands)[i+1]
similar to the value of the dictionary with this line:
next_command = list(dict_of_commands.values())[i+1]
After all we can delete one and insert one where we like to with this:
file_menu.delete(index_of_first_command) #first after pull up button
file_menu.insert_command(index_of_last_command,
label=next_command_label,
command=next_command)
I know that this code can improved by a lot but it seems hard enough to understand as it is. So dont judge me please.
I hope this solves your question, even if it isnt in the way you wanted to. But this code avoids you from hardly code a own menubar.
If there are questions left on my answer, let me know.

how does the Entry widget in tkinter work?

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

Python Tkinter entry not loading content correct

i am facing a problem. I'm runnig this code.
import tkinter as tk
root = tk.Tk()
def check():
if len(e.get().split("a")) > 1:
print("contains a")
e = tk.Entry(frame1)
e.grid(row=4,column=1,columnspan=2,padx = (10,10), pady=(5,10), sticky="w e")
e.bind("<Key>",check)
when i type "a" to the entry I wont get nothing printed. I'll get the result by tiping a second character. I think that it happens because the function gets executed before the content has actualy changed. I tried to add a timer on the beginning of the function but it does nothing.
I want get the result by entering the first "a". What should I do?
I think that it happens because the function gets executed before the content has actualy changed.
You're right. If you want the callback to be able to see the character you just typed, you should create a StringVar and bind to that instead of binding to a "<Key>" event on the widget.
import tkinter as tk
frame1 = tk.Tk()
def check(*args):
if "a" in s.get():
print("contains a")
s = tk.StringVar()
e = tk.Entry(frame1, textvariable=s)
s.trace("w", check)
e.grid(row=4,column=1,columnspan=2,padx = (10,10), pady=(5,10), sticky="w e")
frame1.mainloop()

Get a list of all options from OptionMenu

I want to get a list of all options from an OptionMenu widget in tkinter like so:
import tkinter
root = tkinter.Tk()
var = tkinter.StringVar(root)
var.set('OptionMenu')
optionMenu = tkinter.OptionMenu(root, var, 'foo1', 'foo2')
optionMenu.pack()
listOfAllOptions = optionMenu.getOptions()
# listOfAllOptions == ['foo1', 'foo2']
root.mainloop()
Is there a function that achieve that ?
If not what is the workaround?
You can get the menu associated with the optionmenu (eg: optionMenu["menu"]), and with that you can use menu methods to get the items. It takes several lines of code. But honestly, the easiest thing to do is put the values in a list that you attach to the widget (eg: optionMenu.items = the_list_of_values)
If you want to pull the data from the actual widget, you would do something like this:
menu = optionMenu["menu"]
last = menu.index("end")
items = []
for index in range(last+1):
items.append(menu.entrycget(index, "label"))
print "items:", items

Add tkinter's intvar to an integer

I'm having some trouble adding a value taken from an Entry box and adding it to an existing number. In this case, I want the value of the "change speed" box to be added to the robots current speed. When run, my code produces an error:
TypeError: unsupported operand type(s) for +=: 'int' and 'IntVar'.
Below is the code that produces the entry box:
change_speed_entry = ttk.Entry(main_frame, width=5) # Entry box for linear speed
change_speed_entry.grid()
data = tkinter.IntVar()
change_speed_entry['textvariable'] = data
And next is where I try to manipulate the result. This is a method within a class. All other methods of the class work correctly:
def changeSpeed(self, delta_speed):
self.speed += delta_speed
You need to first invoke the .get method of IntVar:
def changeSpeed(self, delta_speed):
self.speed += delta_speed.get()
which returns the variable's value as an integer.
Since I don't have your full code, I wrote a small script to demonstrate:
from Tkinter import Entry, IntVar, Tk
root = Tk()
data = IntVar()
entry = Entry(textvariable=data)
entry.grid()
def click(event):
# Get the number, add 1 to it, and then print it
print(data.get() + 1)
# Bind the entrybox to the Return key
entry.bind("<Return>", click)
root.mainloop()
When you run the script, a small window appears that has an entrybox. When you type a number in that entrybox and then click Return, the script gets the number stored in data (which will be the number you typed in), adds 1 to it, and then prints it on the screen.
You didn't show the code defining .speed or delta_speed, so I'm guessing here. Try:
self.speed += delta_speed.get()
^^^^^^
If delta_speed is an IntVar, .get() will retrieve its value as a Python int.

Categories

Resources