1 I chosed values from OptionMenu
2 clicked button
But callback function parse printed default values of OptionMenu.
What I did wrong?
My code
from tkinter import *
from functools import partial
def parse(shop, city):
print(f"Parse {city} {shop}") # prints "all shops" but i expected "magnit" because i chosed it
master = Tk()
variable_shop = StringVar(master)
variable_shop.set("all shops") # default value
w = OptionMenu(master, variable_shop, "all shops", "5ka", "magnit", "three").pack()
variable_city = StringVar(master)
variable_city.set("all cities") # default value
w2 = OptionMenu(master, variable_city, "all cities", "Moscow", "Saint Petersburg").pack()
callback = partial(parse, variable_shop.get(), variable_city.get())
b1 = Button(text="Run with values from OptionMenu", command=callback).pack()
mainloop()
Your callback line runs when the program boots, so it saves the values at boot. You need to move those get() calls someplace that gets run later, when the user clicks the button. Putting them in the parse function makes sense, and also removes the need for partial.
from tkinter import *
def parse():
city = variable_city.get()
shop = variable_shop.get()
print(f"Parse {city} {shop}")
master = Tk()
variable_shop = StringVar(master)
variable_shop.set("all shops") # default value
OptionMenu(master, variable_shop, "all shops", "5ka", "magnit", "three").pack()
variable_city = StringVar(master)
variable_city.set("all cities") # default value
OptionMenu(master, variable_city, "all cities", "Moscow", "Saint Petersburg").pack()
Button(text="Run with values from OptionMenu", command=parse).pack()
mainloop()
Consider this line of code:
callback = partial(parse, variable_shop.get(), variable_city.get())
It is functionally identical to this:
shop = variable_shop.get()
city = variable_city.get()
callback = partial(parse, shop, city)
In other words, you're calling the get methods about a millisecond after creating it, rather than waiting until the user has clicked the button.
You don't need to use partial at all. Just have the callback call a regular function, and have the function retrieve the values. This will make the code easier to write, easier to understand, and easier to debug.
def parse():
shop = variable_shop.get()
city = variable_city.get()
print(f"Parse {city} {shop}")
...
b1 = Button(text="Run with values from OptionMenu", command=parse)
b1.pack()
Related
I've got some code in python using tkinter which retrieves the name of a room and uses that to to insert into an SQL database the room name and which site it belongs to. However when I run the code its not retrieving the room name from the entry box.
Can anyone help?
def addroom():
global screen14
global roomsinsite
roomsinsite = StringVar()
screen14 = Tk()
screen14.geometry("300x250")
screen14.title("Insert rooms")
Label(screen14, text = "Insert room name:", bg = "LightSkyBlue1", width = "300", height = "2").pack()
Label(screen14, text = "").pack()
roomsinsite_entry = Entry(screen14, textvariable = roomsinsite)
roomsinsite_entry.pack()
Button(screen14, text = "Register room", width = "12", height = "1", command = insertroom).pack()
def insertroom():
sitename4_info = sitename2.get()
print(sitename4_info)
roomname1_info = roomsinsite.get()
print(roomname1_info)
cursor = cnn.cursor()
# SQL to select the siteID and insert rooms for that paticular site.
siteID_fetch3 = "SELECT siteID FROM Sites WHERE siteName = %s"
cursor.execute(siteID_fetch3, [sitename4_info])
siteID_fetch3 = cursor.fetchall()
# print out the values retrieved
print(siteID_fetch3[0][0])
insertRooms = "INSERT INTO `rooms`(`siteID_fk2`, `roomname`) VALUES (%s,%s)"
insertRooms_val = (siteID_fetch3[0][0], roomname1_info)
cursor.execute(insertRooms, insertRooms_val)
# print out the rows inserted.
print(cursor.rowcount)
cnn.commit()
You are probably having more than one Tk in your code, which means your StringVar does not know which Tk to belong to. So here there are three possible solutions:
Avoid using more than one Tk and replace all child windows with Toplevel, so:
screen14 = Toplevel()
roomsinsite = StringVar()
If you are adamant that you want to use more than one instance of Tk then you can specify master for each StringVar, like:
screen14 = Tk()
roomsinsite = StringVar(master=screen14)
To be honest, I wouldn't use StringVar with entry widgets except when I want to use trace, here if the only purpose of using StringVar is for getting the value of the entry widget, then remove it and use get() method of the entry widget, like:
roomname1_info = roomsinsite_entry.get()
The combination of first and third method seems like best practice, if you ask me. Also here, even if you are not using more than one Tk, one of the above methods would certainly solve the problem(as far as something is inputted inside the entry and then the insertroom() is called).
I created 12 Entry boxes using a for loop with a default value of N/A. Any change in the text of entry is detected through .trace method.
I want to use reset button to make all the text on the Entry Boxes back to N/A
from tkinter import *
root = Tk()
t_diesel_price_EnF_variable = ["JanVar", "FebVar", "MarVar", "AprVar","MayVar","JuneVar","JulyVar","AugVar","SeptVar", "OctVar", "NovVar", "DecVar"]
t_diesel_price_EnF_values = ["N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A"]
def EnFChanging(*events):
for EnF in range(0,len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_values[EnF]=t_diesel_price_EnF_variable[EnF].get()
try:
t_diesel_price_EnF_values[EnF] = float(t_diesel_price_EnF_values[EnF])
except ValueError:
pass
print(t_diesel_price_EnF_values)
for EnF in range(0,len(t_diesel_price_EnF_values)):
t_diesel_price_EnF_variable[EnF] = StringVar(root , value = "N/A")
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10).pack()
t_diesel_price_EnF_variable[EnF].trace("w",EnFChanging)
def ChangeText():
for EnF in range(0, len(t_diesel_price_EnF_values)):
t_diesel_price[EnF].delete(0,END)
t_diesel_price[EnF].insert(0,"N/A")
return
b1 = Button(root, text = "Reset" , command = ChangeText).pack()
root.mainloop()
When I press the button it gives an error t_diesel_price[EnF].delete(0,END)
TypeError: 'NoneType' object is not subscriptable
What should I do now, Please ignore the basic errors of programming as I am a Mechanical Engineer with not a programming back ground. And I have to make a lot of other boxes too for my energy calculator.
You trying treat t_diesel_price as an Entry (and as a list) when your variable is None.
First of all I suggest you to install some IDE (e.g. PyCharm) and place break points to see whats wrong with variable!
Your problem occures because you create and pack your widget in one line! So t_diesel_price is None because pack() always returns None (link).
Just split your declaration and packing to:
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10)
t_diesel_price.pack()
After that it's works for me, except this fact that t_diesel_price is last created entry and the value changes only in it. So I assume that you need another list to iterate over entries:
...
# another fresh list
t_diesel_price_EnF_entries = list()
...
# declare entry
t_diesel_price = Entry(root, textvariable=t_diesel_price_EnF_variable[EnF], width=10)
# pack entry
t_diesel_price.pack()
# append entry to list
t_diesel_price_EnF_entries.append(t_diesel_price)
...
def ChangeText():
# iterate over entries
for diesel_price in t_diesel_price_EnF_entries:
diesel_price.delete(0,END)
diesel_price.insert(0,"N/A")
...
Alternatively you can iterate over StringVar's if you don't wanna store your entries at all:
def ChangeText():
# iterate over stringvars
for EnF in range(len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_variable[EnF].set('N/A')
And you can make it more readable as in example with entry iterating:
def ChangeText():
# iterate over stringvars
for string_var in t_diesel_price_EnF_variable:
string_var.set('N/A')
Cheers!
I'm writing a program for my class that allows you to make a recipe, save it and edit it even after closing the program. You obviously need a text file to do this.
I am using an OptionMenu (Tkinter, Python 3.3.3), but I cannot figure out how to keep updating it to have the first option in the list I have made in my text file. So how do I do that?
My code is thus:
###########################################
###########################################
### RECIPE BOOK TASK ##### By 18166 #######
###########################################
###########################################
from tkinter import *
def script ():
#### MAIN ####
fake_window = Tk()
new_recipe_window = fake_window
start_window = fake_window
start_window.title("Recipe Book Task")
#### MAIN ####
## DATA FILE ##
global datafile
datafile = open("StoredRecipes.txt", "a+")
## DATA FILE ##
### Functions ###
def close (x): ## Close Original Window ##
global start_window
global new_recipe_window
(x).withdraw()
def new_recipe ():
new_recipe_window = Tk() ## Making new window ##
new_recipe_window.title("New Recipe")
close(start_window)
recipe_name_label = Label(new_recipe_window, text="Recipe Name: ") ## Making new recipe label ##
recipe_name_label.grid(row=0, column=0)
recipe_name_box = Entry(new_recipe_window) ## Making new recipe entry ##
recipe_name_box.grid(row=0, column=1)
num_people_label = Label(new_recipe_window, text="Number of people: ") ## Making number of people label ##
num_people_label.grid(row=1, column=0)
num_people_box = Entry(new_recipe_window) ## Making number of people entry ##
num_people_box.grid(row=1, column=1)
item_label = Label(new_recipe_window, text="Items: ") ## Making item label ##
item_label.grid(row=2, column=0)
item_box = Entry(new_recipe_window) ## Making item entry ##
item_box.grid(row=2, column=1)
quantity_label = Label(new_recipe_window, text="Quantity: ") ## Making quantity label ##
quantity_label.grid(row=3, column=0)
quantity_box = Entry(new_recipe_window) ## Making quantity entry ##
quantity_box.grid(row=3, column=1)
unit_label = Label(new_recipe_window, text="Unit: ") ## Making unit label ##
unit_label.grid(row=4, column=0)
unit_box = Entry(new_recipe_window) ## Making unit entry ##
unit_box.grid(row=4, column=1)
def write ():
a = recipe_name_box.get()
b = num_people_box.get()
c = item_box.get()
d = quantity_box.get()
e = unit_box.get()
line = (a, b, c, d, e)
datafile.write(str(line) + "\n")
datafile.close()
saved_recipes.config(a)
close(new_recipe_window)
script()
finish_button = Button(new_recipe_window, text="Save and Finish", command=write) ## Making finish button ##
finish_button.grid(row=5, column=0, sticky=S)
# Dropdown Box #
default = StringVar(start_window, 'Recipe 1')
default.set("Select Your Recipe")
saved_recipes = OptionMenu(start_window, default, "Hi")
saved_recipes.grid(row=0, column=1)
# Dropdown Box #
# New Recipe Button #
new_recipe = Button(start_window, text="New Recipe", command=new_recipe)
new_recipe.grid(row=0, column=0)
# New Recipe Button #
script()
(Sorry for the block, I think all is useful to answering possibly?)
I believe you have two different options.
One option you could do is set up a timer to check the text file every couple of seconds, see if it's changed at all, and update your OptionMenu accordingly. You can find more info on how to do this here, but in a nutshell, you'd want your code to look something like:
def recheck(root, option_menu, file_name):
with open(file_name) as my_file:
lines = my_file.readlines():
# `lines` is a list where each item is a single line
# do any checks and updates you need here.
root.after(1000, recheck, root, option_menu, file_name)
# schedule the function to run again after 1000 milliseconds.
def script():
# set up your gui
start_window.after(1000, recheck, start_window, option_menu, "StoredRecipies.txt")
Note: you can find more info on the with statement here: http://effbot.org/zone/python-with-statement.htm
The downside of this is that the update will be a little laggy -- you'll end up rechecking the file only once a second, so the update won't be instantaneous.
Alternatively, you could use something like Watchdog. It's a 3rd party library that you can set up to "watch" a particular file and run a function whenever the file changes. It's much more responsive in that you'll call the function only if the file actually changes, but it might end up being more complicated since you need to figure out how to make it work with Tkinter. I'm going to guess that your code will look roughly like this:
import os.path
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
def setup_observer(option_menu, filename):
normalized_filename = os.path.normpath(input_filename)
class MyEvent(FileSystemEventHandler):
def on_modified(self, event):
if os.path.normpath(event.src_path) == normalized_filename:
# update your option menu
observer = Observer()
observer.schedule(MyEvent(), '.', recursive=False)
return observer
def script():
# setup gui
observer = setup_observer(option_menu, "myfile.txt")
start_window.mainloop()
To add elements to an OptionList, you can use the following method (from http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html)
datafile = open("StoredRecipes.txt", "r")
for line in datafile.readlines():
saved_recipes['menu'].add_command(label=line,
command=lambda temp = line: saved_recipes.setvar(saved_recipes.cget("textvariable"), value = temp))
Which uses (has to use) a closure and an anonymous function -- definitely nothing you should deal with on your level of experience (guessing from the structure of your code).
This snippet adds a command for each line in your file. Because an OptionMenu is something that executes things when elements are selected, you have to provide a command for each line. Right now this is just setting the displayed text to the selected line.
To accomplish this, it uses an anonymous function (lambda) that sets the textvariable of the OptionMenu to the current line.
I have a problem with the following code. Now I'm very new to programming in general, and most of the code is copied off the internet, and I adjusted it so it would work the way I want it to. So if there is no easy way of solving it, that's ok. Maybe you can just point out some topics of programming or python, that I should read about.
I try to explain it anyways. I have defined the function query(), that makes some changes to sqlite databases. The input is a list. That function works just fine if I use it just by itself.
Now I'm trying to have an Interface, where I can define, what should be inside that list, depending on what checkboxes are checked. Then I want to execute the function with that specific list, when I press a button. The checkboxes are generated just fine, the button, too. Also, when I check or uncheck the buttons, it updates the list just fine, and it shows the new updated list in the interpreter.
The problem is, that the button does not work:
1. It doesn't use the new updated list, instead it uses an empty list ()
2. When I input a predefined list, that is not empty, it automatically runs query() without me even clicking the button.
I may not have explained this well, but I hope you understand what my problem is.
Thanks for the help
`
def chkbox_checked():
for ix, item in enumerate(cb):
opt[ix]=(cb_v[ix].get())
print opt
def query(opt):
import sqlite3
connection = sqlite3.connect("gather.sqlite")
cursor1 = connection.cursor()
cursor1.execute('Drop table IF EXISTS matches')
cursor1.execute('CREATE TABLE matches(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matches"')
for i in range(0, len(opt)):
a=opt[i]
cursor1.execute('INSERT INTO matches (date, team1, team2, league) SELECT * FROM gather WHERE team1=? or team2=? or league=?', (a,a,a,))
cursor1.execute('Drop table IF EXISTS matchessorted')
cursor1.execute('CREATE TABLE matchessorted(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matchessorted"')
cursor1.execute('INSERT INTO matchessorted (date, team1, team2, league) SELECT * FROM matches ORDER BY date')
connection.commit()
import Tkinter as tk
from Tkinter import *
opt = []
root = tk.Tk()
mylist = [
'name1',
'name2',
'name3'
]
cb = []
cb_v = []
for ix, text in enumerate(mylist):
cb_v.append(tk.StringVar())
off_value=0
cb.append(tk.Checkbutton(root, text=text, onvalue=text,offvalue=off_value,
variable=cb_v[ix],
command=chkbox_checked))
cb[ix].grid(row=ix, column=0, sticky='w')
opt.append(off_value)
cb[-1].deselect()
label = tk.Label(root, width=20)
label.grid(row=ix+1, column=0, sticky='w')
button1 = Button(root, text = "Calculate", command = query(opt))
button1.grid(column=1, row=0, sticky=W)
root.mainloop()
`
A couple of points about how to structure your code: You need to write a function that populates the list based on your selection. It can return a list call 'options' and when you want to execute the code inside the query, you call the function that constructs the options list. The query function will have a statement like this:
options = get_options() #assuming the function that populates the options is called get_options
and then you execute the query function's code.
button1 = Button(root, text = "Calculate", command = query(opt))
This calls query(opt) immediately, before you create your Button, and passes the result of that call (None) to the Button constructor as the command argument. What you really want is a function that, when called, executes query(opt). Something like this:
def calculate_clicked():
query(opt)
button1 = Button(root, text = "Calculate", command = calculate_clicked)
or this:
button1 = Button(root, text = "Calculate", command = lambda : query(opt))
I am trying a piece of python based tkinter code with following objective:
(Please go through the objective, then I will take an example to explain what exactly i require and then in the end will post the script I have written)
Reads from a config file, which is implemented using configparser module.
Based on options read from this file it automatically generates widget.
These widgets are restricted to only labels and entry box as of now.
Every entry box is associated with a variable. It is hence needed to generate a variable
automatically whenever a entry box is declared.
Now when the user enters any value in the entry box, and presses calculate button a list is
generated with combination of values entered by user( in a specific format).
Example:
Let the configparser file has following content:
[widget]
label = ani_label,sham_label
entry = ani,sham
The list generated for this case will be like this:
out_list = ['-ani','< ani >','-sham','< sham >']
< ani > means value stored in ani variable
And below is the code that i have tried.
from Tkinter import *
from Tkinter import Tk
import Tkinter as tk
import ttk
import ConfigParser
import sys
############ Initialize ###############################
parser_read = ConfigParser.ConfigParser()
parser_read.read('option_read.config')
config_list = {}
config_list['label'] = parser_read.get('widget','label').split(',')
config_list['entry'] = parser_read.get('widget','entry').split(',')
######
def calculate():
#Will include the list generation part
pass
#######
root = Tk()
root.title("NRUNTEST GUI VERSION 1")
#
menuframe = ttk.Frame(root)
menuframe.grid(column=0,row=0)
menuframe.columnconfigure(0,weight=1)
menuframe.rowconfigure(0,weight=1)
#
mainframe_label = ttk.Frame(root)
mainframe_label.grid(column=1,row=0)
mainframe_label.columnconfigure(0,weight=1)
mainframe_label.rowconfigure(0,weight=1)
#
mainframe_entry = ttk.Frame(root)
mainframe_entry.grid(column=2,row=0)
mainframe_entry.columnconfigure(0,weight=1)
mainframe_entry.rowconfigure(0,weight=1)
#
general_label= Label(menuframe,text="Please Enter the Values Below").grid(column=1,row=0,sticky=(E))
compiler_label= ttk.Label(menuframe,text="Compiler")
compiler_label.grid(column=1,row=1,sticky=W)
#
calculate_button = ttk.Button(menuframe, text="Calculate", command=calculate).grid(column=1,row=2,sticky=(W,E))
#Automatic Widget declaration ###
for x in config_list['label']:
x = ttk.Label(mainframe_label,text=x).grid()
for x in config_list['entry']:
#print x
var = lambda: sys.stdout.write(x)
x = ttk.Entry(mainframe_entry,textvariable = x).grid()
root.mainloop()
The content of option_read.config is
[widget]
label : animesh_label,sharma
entry : animesh_entry,sharma
STATUS:
I can create the required widgets automatically. But I am not able to create the variables dynamically to store the entry box values.
Once the variable has been calculated, I can write the calculate function on my own.
Please advice how i can proceed.
If you have any better way to meet my requirements, please do suggest.
Also do ping me if you require any more inputs or my query is not clear.
The easiest way to do this, IMO, is to use a dict to store the references to the dynamically created variables. You could use the label as the key. For example:
vars = {}
for x in config_list['entry']:
vars[x] = StringVar()
entry = ttk.Entry(mainframe_entry, textvariable=vars[x])
entry.grid()
By the way... are you aware that if you do something like x=ttk.Entry(...).grid(...), x does not contain a reference to the widget? It contains the result of the call to grid, which is None.