Can Tkinter OptionMenu have variable size and options? - python

I am connecting to an email server and getting the number and names of 'folders' that are currently in use. Is it possible for *values (from tutorial code (1)) to be a list of StringVar objects? I have a list of StringVar objects that I want to dynamically update into the OptionMenu based on input from a server. So I am adding a dynamic list of StringVar objects and then trying to have them populate in the OptionMenu. I think the problem is that StringVar are objects and not text values, which is why it's not populating the OptionMenu. Is there a way to fix this?
Tutorial Code (1):
from Tkinter import *
# the constructor syntax is:
# OptionMenu(master, variable, *values)
OPTIONS = [
"egg",
"bunny",
"chicken"
]
master = Tk()
variable = StringVar(master)
variable.set(OPTIONS[0]) # default value
w = apply(OptionMenu, (master, variable) + tuple(OPTIONS))
w.pack()
mainloop()
Populating a list of StringVar objects (from another tutorial):
if resp == 'OK':
for mbox in data:
flags, separator, name = self.parse_mailbox(bytes.decode(mbox))
fmt = '{0} : [Flags = {1}; Separator = {2}'
fmt = '{0}'
if not "/ [Gmail]" in fmt.format(name):
temp = tk.StringVar()
temp.set(fmt.format(name))
self.list.append(temp)
Adding to OptionMenu Code:
variable = tk.StringVar(self)
variable.set("INBOX") #defaultvalue
tk.Label(self, text="Choose Mailbox: ").grid(row=1,sticky='W',padx=30,pady=10, columnspan=1)
self.mailboxes = tk.OptionMenu(self, variable, *self.controller.list)
#self.mailboxes = apply(OptionMenu, (self, variable) + tuple(self.controller.list))
self.mailboxes.grid(row=1,column=1,sticky='W',pady=10, columnspan=1)
Thank you for reading.
Edit Updated 'append' code to get StringVar() value and place into list
if not "/ [Gmail]" in fmt.format(name):
temp = tk.StringVar()
temp.set(fmt.format(name))
self.list.append(temp.get())
Also updated OptionMenu initilization
self.mailboxes = tk.OptionMenu(self, self.variable, *self.controller.list)
Here is the error message:
[1]: https://i.stack.imgur.com/a1Uye.png

Related

Use loop to create x number of tkinter OptionMenu

I am attempting to create an arbitrary number of optionmenus, but have trouble when trying to pull the StringVar() selected by each optionmenu.
Goal: Create an arbitrary number of optionmenus with consecutive names (up to that arbitrary number) and consecutive variable names keeping track of the current optiomenu value
For example if an optionmenu is as follows:
import tkinter as tk
from tkinter import *
root = tk.Tk()
root.geometry()
dropdown1 = StringVar()
Dropdownoptions = [
"option1",
"option2",
"option3"
]
dropdownfirst = tk.OptionMenu(root, dropdown1, *Dropdownoptions)
dropdownfirst.grid(column=0, row=0)
root.mainloop()
If using a dictionary I do not know how to pull the values out of each Optionmenu. When looking at other questions about the use of dictionaries to create variables, most answers boil down to "Learn How to Use Dictionaries" instead of answering the questions.
There was a very similar problem posted in Tkinter Create OptionMenus With Loop but sadly it is not applicable in my case.
New code with grid and non-working button:
import tkinter as tk
def erase_option():
for (name, var) in options.items():
print(var.get())
# print(options['optionmenu4'])
# This just places label over dropdown, doesnt successfully take place for removal
labelforemoval = tk.Label(text=" ")
labelforemoval.grid(column=0, row=4)
labelforemoval.grid_forget()
root = tk.Tk()
Dropdownoptions = [
"option1",
"option2",
"option3"
]
maxval = 10
options = {}
for om, x in zip(range(maxval), range(maxval)):
name = f"optionmenu{om}"
var = tk.StringVar()
options[name] = var
name = tk.OptionMenu(root, var, *Dropdownoptions)
name.grid(column=0, row=x)
button = tk.Button(root, text="Erase 5th option", command=erase_option)
button.grid(column=0, row=maxval)
root.mainloop()
Give each optionmenu its own StringVar instance. Save those instances in a list or dictionary. To get the values, iterate over the list.
The following code creates a dictionary named options. It creates a series of variables and optionmenus in a loop, adding each variable to this options dictionary. The function print_options iterates over the list printing out the key and value for each option.
import tkinter as tk
def print_options():
for (name, var) in options.items():
print(f"{name}: {var.get()}")
root = tk.Tk()
Dropdownoptions = [
"option1",
"option2",
"option3"
]
options = {}
for om in range(10):
name = f"Option {om}"
var = tk.StringVar(value="")
options[name] = var
om = tk.OptionMenu(root, var, *Dropdownoptions)
om.pack()
button = tk.Button(root, text="Print Options", command=print_options)
button.pack(side="bottom")
root.mainloop()

Is there a way for the Entry widget to not mimic the Checkbutton's value?

Right off the bat here is my code so far (please ignore the variable names. I am still learning how to use Python):
root = Tk()
testvariable = 0
bruh = 0
test = Checkbutton(root, variable = testvariable, )
test.pack()
test1 = Entry(root,textvariable = bruh, text = "0", width = 4)
test1.pack()
root.mainloop()
I notice that when I select the Checkbutton to turn it off or on, the Entry widget automatically changes its value to whatever the Checkbutton's value is. Is there a way to prevent this?
When setting variables in tkinter make sure to use the built-in types (https://docs.python.org/3/library/tkinter.html#coupling-widget-variables).
For the Entry widget you can directly use the get method to assign its value to a variable. As for the Checkbutton widget, make sure to assign it an "IntVar" type to correctly deal with its value passing. I've demonstrated how to do both of the above in the code below.
import tkinter as tk
root = tk.Tk()
checkbox_var = tk.IntVar()
testvariable = 0
bruh = 0
test = tk.Checkbutton(root, variable=checkbox_var)
test.pack()
test1 = tk.Entry(root)
test1.pack()
def testOutput():
testvariable = checkbox_var.get()
bruh = test1.get()
print("Checkbox is", testvariable)
print("Entry is", bruh)
button = tk.Button(root, text="Test Button", command=testOutput)
button.pack()
root.mainloop()
They mimic each other because both of their textvariable attributes are the same value. The short answer is to give them each different values for textvariable.
Also, you should be setting that attribute to an instance of a tkinter variable such as StringVar or IntVar. However you rarely need to use that attribute with Entry widgets, since the widget itself gives you methods for getting and setting the value.

storing checkboxes into a list tkinter

I am fairly new to tkinter was able to get the following code.
from tkinter import *
from tkinter import filedialog
master = Tk()
v = IntVar()
v.set(1)
var1 = IntVar()
var2 = IntVar()
var3 = IntVar()
CategorySubmit= IntVar()
my_list = [{"Kitchen":var1},{"Electric Supply":var2},{"Books":var3}]
def get_boxes(value):
#store the checked boxes for later use
pass
def get_value(value):
#print(value)
if value=="Category":
for widget in master.winfo_children():
widget.destroy()
row =1
for i in my_list:
Checkbutton(master, text=list(i.keys())[0], variable=list(i.values())[0]).pack()
row+=1
Button(master, text="Submit", variable=CategorySubmit,command=get_boxes).pack()
#save the check boxes made by user into a list then quit master
else:
file_chosen =filedialog.askopenfilename(title='Please Select Input File', filetypes=[('Excel files', ('.xlsx', '.csv', '.xls', 'xlsm'))])
print(f"Done: {file_chosen}")
master.destroy()
MODES = [
("Choice 1","ID"),
("Choice 2","Category"),
("Choice 2","Full"),
]
choice_made= StringVar()
choice_made.set('Choice 1')
for text,mode in MODES:
Radiobutton(master,text=text,variable=choice_made,value=mode).pack()
but = Button(master,text="Submit",command=lambda: get_value(choice_made.get()))
but.pack()
master.mainloop()
print(file_chosen) #gives undefined error ?
I need to store the values in checkboxes into a list so I can use later. also I have an error when for the variable name outside the master.mainloop() , it gives NameError: name 'file_chosen' is not defined,
My idea is when "Choice 1" or "Choice 3" are picked a fileprompt is given so I can continue with my script later on, else if "Choice 2" 3 checkboxes that I store in a list
The file_chosen variable is local inside the get_value() function. To extend its scope you have to declarate as global:
def get_value(value):
global file_chosen

Python GUI login using Tkinter

I'm creating an interface that first prompts the user to login to the database. I'm having trouble with the get method for an Entry object. For the 'Login' button command, I use lambda since I am calling the login function which takes arguments. Since I'm passing Entry objects into my login function I call user.get() and pw.get() in those functions.
Upon running this code however, it says that user.get(), pw.get() are Nonetype objects with no attribute get. I don't understand why the entries are Nonetype since the logintoDB should be called after the buttons are created.
Thanks for the help.
Below is my code:
import Tkinter as tk
import tkFileDialog as tkfd
import tkMessageBox as tkmb
import xlrd
def openFile():
#returns an opened file
fname = tkfd.Open(filetypes = [("xls files","*.xls")])
fpath = fname.show()
if fname:
try:
TLA_sheet = xlrd.open_workbook(fpath).\
sheet_by_name('TLA - TOP SKUs')
tk.Button(root, text = "Import TLAs", command = lambda: importTLAtoDB(TLA_sheet)).pack()
tkmb.showinfo("Success!", "Spreadsheet successfully loaded. \n\
Click Import TLAs to load TLA info into RCKHYVEDB database.")
except:
tkmb.showerror("Error", "Failed to read file\n '%s'\n\
Make sure file is a type .xls" % fpath)
def enter(event):
return logintoDB
def logintoDB(user, pw):
#request login for database access
print user.get(), pw.get()
try:
db = MySQLdb(config.server_link, user.get(), pw.get(), config.database)
tkmb.showinfo("Success!","Database login successful.\n\
Click Browse to load TLA spreadsheet.")
tk.Button(root, text = "Browse", command = openFile, width = 10).pack()
return True
except:
tkmb.showerror("Error", "Login failed. Try again.")
return False
#GUI setup
root = tk.Tk()
root.title("TLA Database Tool")
user_label = tk.Label(root, text = "username").pack()
user = tk.Entry(root, textvariable = tk.StringVar()).pack()
pw_label = tk.Label(root, text = "password").pack()
pw = tk.Entry(root, show = "*", textvariable = tk.StringVar()).pack()
login_button = tk.Button(root, text = "Login", command = lambda: logintoDB(user,pw)).pack()
root.mainloop()
The following lines are wrong in your code:
user = tk.Entry(root, textvariable = tk.StringVar()).pack()
pw = tk.Entry(root, show = "*", textvariable = tk.StringVar()).pack()
clearly the variables user and pw above point to the Entry widget objects, not to the textvariable associated with those objects.
Rather you should set a new variable using the textvariable attribute and pass it as a parameter using your lambda operator.
Here's a simple example to fetch text from Text widget.
from Tkinter import *
root = Tk()
svalue = StringVar() # defines the widget state as string
w = Entry(root,textvariable=svalue) # adds a textarea widget
w.pack()
def act():
print "you entered"
print '%s' % svalue.get()
foo = Button(root,text="Press Me", command=act)
foo.pack()
root.mainloop()
Notice how I set a separate variable svalue and passed it to to the textvariable attribute of the Entry widget.
In reply to your comment
In fact now that I closely look at your widget creation, you are creating the widget and packing it in the same line and then keeping a reference to it like:
usr = Entry().pack()
Since pack() returns null, you are left with a null value for usr.
Instead do this:
usr = Entry(root,textvariable=svalue) # create here
usr.pack() # now pack
Now usr will have a reference to the Entry widget.
In fact I got your entire program to work simply by changing the lines to:
user = tk.Entry(root, textvariable = tk.StringVar())
user.pack()
pw_label = tk.Label(root, text = "password").pack()
pw = tk.Entry(root, show = "*", textvariable = tk.StringVar())
pw.pack()

Entry string not being added to listbox

Could anyone help me debug this? The listbox isn't updating, and I'm not sure if the entry text (ment) is even transferring to the method.
def NewTask():
ment = StringVar()
top = Toplevel()
top.title("Add New Task")
top.minsize(300,300)
top.maxsize(300,300)
label_newtask = Label(top, text = "Entry New Task:", font = ("Purisa",20))
label_newtask.pack()
button_newtask = Button(top, text="Enter", command= NewTaskCount)
button_newtask.pack()
entry_newtask = Entry(top, textvariable=ment)
entry_newtask.pack()
def NewTaskCount():
ment = StringVar()
mtext = ment.get()
listbox.insert(END, mtext)
return
Your problem is that your stringvar ment is a local variable that is only visible within the scope of NewTask. In NewTaskCount, you are creating a new StringVar -- which is initially blank -- and immediately getting the value of that new variable. You need to make it a global variable, or use an object-oriented approach so that you can use an instance variable.

Categories

Resources