My code isn't getting the values I insert into an entry - python

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).

Related

Obtain values from tkinter OptionMenu

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()

Changing entry text in tkinter, if the entry is created from list

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!

python 'function' has no attribute

I've been pushing this error about all day trying to solve it and decided i need help, i'm guessing its telling me that 'countrynames' is either not defined, not populated or has any values, yet it is used previously in the class 'mysqlbusiness' ok, i am trying to display a simple line of text that shows what item has been selected from a drop down list, the list is populated by a tuple, and a command to apply is selected by a radio button, the message is a polite note !
The TKinter error is this:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "S:\python\jon\wrt_toolkit_v5\toolbox_v7.py", line 141, in sendGift
name = mysqlbusiness.mysqlConnect.countrynames[idx]
AttributeError: 'function' object has no attribute 'countrynames'
The code snippet is this,
class mysqlbusiness:
def mysqlConnect():
import pymysql
sqlUsr = MysqlUsr.get()
sqlpwd = Mysqlpwd.get()
conn = pymysql.connect(host='192.168.0.27', user= sqlUsr, passwd=sqlpwd, db='information_schema')
cursor = conn.cursor()
cursor.execute("SELECT SCHEMA_NAME FROM SCHEMATA")
schema_names = cursor.fetchall()
schema_tuple = tuple(schema_names)
countrynames = (schema_tuple)
cnames = (countrynames)
lbox.insert(END, *schema_names)
# Colorize alternating lines of the listbox
for i in range(0,len(cnames),2):
lbox.itemconfigure(i, background='#CEECF5')
# create a vertical scrollbar to the right of the listbox
yscroll = tk.Scrollbar(command=lbox.yview, orient=tk.VERTICAL)
yscroll.grid(row=15, column=6, sticky='ns')
lbox.configure(yscrollcommand=yscroll.set)
lbox.selection_set(0)
conn.close()
# Called when the user double clicks an item in the listbox, presses
# the "apply" button, or presses the Return key. In case the selected
# item is scrolled out of view, make sure it is visible.
#
# Figure out which schema is selected, which command (connect or delete) is selected with the radiobuttons, and provide feedback.
def listConnection(*args):
idxs = lbox.curselection()
if len(idxs)==1:
idx = int(idxs[0])
lbox.see(idx)
name = mysqlbusiness.mysqlConnect.countrynames[idx]
# Gift sending left as an exercise to the reader
sentmsg.set("%s %s" % (gifts[gift.get()], name))
lbox.bind('<Double-1>', listConnection)
mainframe.bind('<Return>', listConnection)
You are making several mistakes:
You are using a class but defined only functions. Don't use classes to create namespaces, Python is not Java and does not require classes to be used.
Your mysqlConnect() function doesn't return anything. You cannot refer to local names in functions.
You didn't actually call the mysqlConnect() function.
You are mixing data retrieval with updating the UI; better to split that out to separate functions.
You are not extracting the column from the rows; each row result from the database is a tuple with columns (one in this case).
You are using several variable names for the same result set; there is no need really to make the results a tuple, for example.
There should be no need to re-bind the event handlers every time listConnection is called.
Corrected code:
import pymysql
def countrynames():
sqlUsr = MysqlUsr.get()
sqlpwd = Mysqlpwd.get()
conn = pymysql.connect(host='192.168.0.27', user=sqlUsr,
passwd=sqlpwd, db='information_schema')
cursor = conn.cursor()
cursor.execute("SELECT SCHEMA_NAME FROM SCHEMATA")
countrynames = [row[0] for row in cursor]
conn.close()
return countrynames
def set_countrynames_lbox(countrynames)
lbox.insert(END, *countrynames)
# Colorize alternating lines of the listbox
for i in range(0, len(countrynames), 2):
lbox.itemconfigure(i, background='#CEECF5')
# create a vertical scrollbar to the right of the listbox
yscroll = tk.Scrollbar(command=lbox.yview, orient=tk.VERTICAL)
yscroll.grid(row=15, column=6, sticky='ns')
lbox.configure(yscrollcommand=yscroll.set)
lbox.selection_set(0)
# Called when the user double clicks an item in the listbox, presses
# the "apply" button, or presses the Return key. In case the selected
# item is scrolled out of view, make sure it is visible.
#
# Figure out which schema is selected, which command (connect or delete) is selected with the radiobuttons, and provide feedback.
def listConnection(*args):
idxs = lbox.curselection()
if len(idxs)==1:
idx = int(idxs[0])
lbox.see(idx)
cnames = countrynames()
# optional, set the lbox again with
# set_countrynames_lbox(cnames)
name = cnames[idx]
# Gift sending left as an exercise to the reader
sentmsg.set("%s %s" % (gifts[gift.get()], name))
# moved out of the functions
lbox.bind('<Double-1>', listConnection)
mainframe.bind('<Return>', listConnection)

Using list defined in one function in another function. Python 2.7

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))

Dynamic Widget creation based of configparser file

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.

Categories

Resources