Why is it saying not defined? - python

first off im very new to programing Python 3.4.3, im trying to call 5 entrys and put them into and array and display that, but im stuck at GPA1 not defined, but it is defined in for the entry app
import sys
from tkinter import *
def save_data():
fileD.write("GPA1:\n")
fileD.write("%s\n" % GPA1.get())
fileD.write("GPA2:\n")
fileD.write("%s\n" % GPA2.get())
fileD.write("GPA3:\n")
fileD.write("%s\n" % GPA3.get("1.0", END))
app = Tk()
app.title('Student Grade Report')
gpa1=str(GPA1.get())
gp1=float(gpa1)
gpa2=str(GPA2.get())
gp2=float(gpa2)
gpa3=str(GPA3.get())
gp3=float(gpa3)
gpa4=str(GPA4.get())
gp4=float(gpa4)
gpa5=str(GPA5.get())
gp5=float(gpa5)
gpas=gp1+gp2+gp3+gp4+gp5
avg=gpas/5
def DisplayMsg():
DL=str("Dean's list")
AP=str("Academic Probation")
note=("No Message")
if (avg>3.5):
note=DL
else:
if (avg<2.0):
note=AP
return str(note)
classi = StringVar()
classi.set(None)
Label(app, text = "Classification:").pack()
Classification = StringVar()
Classification.set(None)
Radiobutton(app, variable = classi, text = "freshman", value = "freshman").pack()
Radiobutton(app, variable = classi, text = "sophmore", value = "sophmore").pack()
Radiobutton(app, variable = classi, text = "junior", value = "junior").pack()
Radiobutton(app, variable = classi, text = "senior", value = "senior").pack()
Label(app, text = "GPA1:").pack()
GPA1= StringVar()
GPA1.set(None)
GPA1= Entry(app,textvariable=Gpa1).pack()
Label(app, text = "GPA2:").pack()
GPA2= StringVar()
GPA2.set(None)
GPA2 = Entry(app)
Label(app, text = "GPA3:").pack()
GPA3= StringVar()
GPA3.set(None)
GPA3= Entry(app)
Label(app, text = "GPA4:").pack()
GPA4= StringVar()
GPA4.set(None)
GPA4= Entry(app)
Label(app, text = "GPA5:").pack()
GPA5= StringVar()
GPA5.set(None)
GPA5= Entry(app)
Button(app, text= "Message", command = DisplayMsg).pack()
Button(app, text = "Save", command = save_data).pack()
app.mainloop()

The program published above does have some problems with indentation, but I suspect that this happened when inserting into the editor.
Also, I suspect your error says Gpa1 undefined, not GPA1. This is because you assign a text variable to the Entry which is not initialized yet (i.e. it does not exist).
The way you pack() the widgets, make them appear vertically. If you want them in a spreadsheet like table, you have to use a packer such as .grid(), and specify the column and row where you want the widget to appear.
Here's the code, slightly cleaned up (Note that I had to use Tkinter to find the module - not tkinter):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from Tkinter import *
def save_data():
fileD.write("GPA1:\n")
fileD.write("%s\n" % GPA1.get())
fileD.write("GPA2:\n")
fileD.write("%s\n" % GPA2.get())
fileD.write("GPA3:\n")
fileD.write("%s\n" % GPA3.get("1.0", END))
app = Tk()
app.title('Student Grade Report')
gpa1=str(GPA1.get())
gp1=float(gpa1)
gpa2=str(GPA2.get())
gp2=float(gpa2)
gpa3=str(GPA3.get())
gp3=float(gpa3)
gpa4=str(GPA4.get())
gp4=float(gpa4)
gpa5=str(GPA5.get())
gp5=float(gpa5)
gpas=gp1+gp2+gp3+gp4+gp5
avg=gpas/5
def DisplayMsg():
DL=str("Dean's list")
AP=str("Academic Probation")
note=("No Message")
if (avg>3.5):
note=DL
elif (avg<2.0):
note=AP
return str(note)
def main():
app = Tk()
app.title('Student Grade Report')
classi = StringVar()
classi.set(None)
Label(app, text = "Classification:").pack()
Classification = StringVar()
Classification.set(None)
Radiobutton(app, variable = classi, text = "freshman", value = "freshman").pack()
Radiobutton(app, variable = classi, text = "sophmore", value = "sophmore").pack()
Radiobutton(app, variable = classi, text = "junior", value = "junior").pack()
Radiobutton(app, variable = classi, text = "senior", value = "senior").pack()
Gpa1 = None
Label(app, text = "GPA1:").pack()
GPA1= StringVar()
GPA1.set(None)
GPA1= Entry(app,textvariable = Gpa1).pack()
Label(app, text = "GPA2:").pack()
GPA2= StringVar()
GPA2.set(None)
GPA2 = Entry(app)
Label(app, text = "GPA3:").pack()
GPA3= StringVar()
GPA3.set(None)
GPA3= Entry(app)
Label(app, text = "GPA4:").pack()
GPA4= StringVar()
GPA4.set(None)
GPA4= Entry(app)
Label(app, text = "GPA5:").pack()
GPA5= StringVar()
GPA5.set(None)
GPA5= Entry(app)
Button(app, text= "Message", command = DisplayMsg).pack()
Button(app, text = "Save", command = save_data).pack()
app.mainloop()
main()
NOTE: Sorry! I added a NOTE to the original post, instead of my answer. Here's the correct note: You are not done yet! There are still issues to be resolved. Eg. The fileD.write won't work if you don't open the file first.

You need to fix all the problems described in jcoppens' very thorough answer, but the actual problem you're asking about is another one, that you also need to fix. (I'm assuming that your actual indentation and structure looks like what he's guessed in his answer. If this doesn't apply to your actual code… well, it applies to the code in his answer, which your code will hopefully be a lot like once you fix all the other problems.)
Inside save_data, you do this:
fileD.write("%s\n" % GPA1.get())
But there's no global variable named GPA1, and no local variable inside that function named GPA1.
There is a local variable inside main with that name, but that doesn't help; the whole point of local variables is that they're local—the only live inside one function.
You could fix this by making GPA1 global, by just adding global GPA1 to the top of your main function. This is the quickest solution, but usually not the best.
A better solution is to create a class, and make GPA1 an attribute of that class. Whichever Tkinter docs or tutorial you're using will probably have lots of good examples of this.
You could also change save_data to take GPA1 as a parameter, and then use functools.partial(save_data, GPA1) instead of just save_data, as a callback.
Or you could put all of these functions inside another function and make GPA1 a nonlocal variable instead of a global one. (I wouldn't recommend this, just including it for completeness…)
Of course whatever you do, you need to do the same for GPA2 or you'll just get the same error again two lines later.
Needless to say, there's no guarantee this is the last bug in your program.

Related

cant change title of program in tkinter

this is my main module(this is for a point of sale system)
from tkinter import *
from SettingsMenuPOS import *
from Globalvariables import *
root = Tk() #mainprogram
root.iconbitmap('D:/Gatlabs logo.ico')
opensettingsmenu = Button(root, text= "Open Settings", command = settingsmain)
root.title(Mname)
opensettingsmenu.grid(row= 0, column= 0)
enter_button = Button( root , text = "ENTER", padx = 20, pady = 10, command= EnterEvent)
enter_button.grid(row= 0, column= 1)
root.mainloop()
im importing a setting module which changes the title of the program
from Globalvariables import *
from tkinter import *
#Mart Name
#user accounts
def settingsmain():
settingmenu = Toplevel()
settingmenu.iconbitmap('D:/Gatlabs logo.ico')
global entryformartname
labelformartname = Label(settingmenu, text = "Enter name of your store")
entryformartname = Entry(settingmenu)
entryformartname.grid(row = 0, column = 0)
setmartname = Button(settingmenu, text = "setname", command = setname)
setmartname.grid(row= 0, column = 1)
settingmenu.mainloop()
def setname():
global Mname, entryformartname
Mname = entryformartname.get()
and im using a global variable Mname to display title of the program.
ive set the variable Mname = "Gatlabs" and the user can change it through entry.
but everytime i try to set the title it doesnt change :(
i hope to get better at coding but i suck. pls help im stuck
I think the problem here is that you are thinking that using -:
root.title(var)
would make it so that anytime you change the var the title will change, but actually its that the title is changed only once and to the value of the var at the time it was supplied.
So if you want it to change everytime you change your var, then rather put it in the same function as the one where you change your var.
def update_title(title) :
global var
var = title
root.title(title)
return
and now every time you change the title run this down with the desired title as the argument.
I hope this solves your problem.
Also i hope you're safe in the time of an ongoing pandemic.
Change setname function like this:
def setname():
global Mname, entryformartname, setmartname
Mname = entryformartname.get()
parent_name = setmartname.winfo_parent()
parent = setmartname._nametowidget(parent_name)
parent.title(Mname)
In your code, you just update Mname variable and window title stays unchanged.
Update:
You can get the master from button widget which is the window itself. Then, change its title.

Tkinter - Update changes in variables in previously generated widgets, from dictionary

I need to represent in "ventana2" the pairs entered in "ventana", so that a new frame appears when the key is new. When the key already exists in the dictionary, I need to change the old value in the frame created for that key previously (the new value is adding old and new).
I can not get the frames permanently related to my dictionary partner, through the key.
Thank you very much in advance, and sorry for my english.
Here is a summary of the code:
import tkinter as tk
ventana = tk.Tk()
ventana2 = tk.Tk()
name = tk.StringVar()
tk.Entry(ventana, textvariable=name, width=30).grid(row=0, column=1)
tk.Label(ventana, text = 'Nombre').grid(row=0, column=0)
value = tk.StringVar()
tk.Entry(ventana, textvariable=value, width=30).grid(row=1, column=1)
tk.Label(ventana, text = 'Celular').grid(row=1, column=0)
contactos={}
def intro():
nom = name.get()
if nom in contactos:
cel = contactos[nom] + float(value.get())
contactos[nom] = cel
else:
cel = float(value.get())
contactos[nom] = cel
create_widget()
def create_widget():
frame = tk.Frame(ventana2)
frame.pack()
nomb = tk.Label(frame, text=name.get()).pack(side=tk.LEFT)
telf = tk.Label(frame, text=contactos[name.get()]).pack(side=tk.RIGHT)
intro_btn = tk.Button(ventana, text='Intro', command = intro)
intro_btn.grid(row=2, column=0, columnspan=2, sticky = 'ew')
ventana.mainloop()
Labels created inside the create_widget() function is in the function scope. After the function ends all references to the label is lost. So you need to think of a way to save references to the label (suggest return statement) and associate them to the dictionary of contactos.
Update - relate a frame with a value
Save a reference to the object you wish to remember and the name you want to use to recall it in a list(or dict or tuple et.). Then append all that to your global list of widgets. For example:
nomb = tk.Label( ... )
widget_parameters = ['Nomb Label', nomb]
global_widget_list.append(widget_parameters)
Then you can search the global_widget_list for any widget you have named and get a referance to that widget.
I have included some example code to illustrate one way that you can accomplish that. Play around with it until you understand it and you will be able to implement it in your own application.
from tkinter import *
import time
root = Tk()
root.geometry('300x200')
global_widget_list = [] # List for holding all widgets
def make_label(): # Create label
text_label = Label(root,text='Text input')
text_label.pack()
global_widget_list.append(['Text Label',text_label]) # Add widget to list
def make_input(): # Create entry
inputvar = StringVar()
text_input = Entry(root,width=20,textvariable=inputvar)
text_input.pack()
global_widget_list.append(['Text Input',text_input,inputvar]) # Add widget to list
make_label() # Run functions to cretae GUI
make_input()
root.update() # Take care of updating GUI
time.sleep(3) # Wait so you have time to see the original, then change it
# Now, loop through all widgets and do what you want
for widget in global_widget_list:
widget_name = widget[0]
widget_id = widget[1]
print(widget_name, 'has identity', widget_id)
if widget_name == 'Text Label':
print('Changing test label text to: ALL CAPS')
widget_id.configure(text='ALL CAPS')
if widget_name == 'Text Input':
print('Changing text in entry to: SAMPLE')
var = widget[2]
var.set('SAMPLE')
root.update() # Take care of updating GUI
time.sleep(3) # Wait so you have time to see the changes, then change it
print('Removing Entry from application GUI')
global_widget_list[1][1].pack_forget() # Remove entry form GUI

Clear text from tkinter entry widget

I am using Tkinter entry widgets to allow users to input text to a GUI. The entry widgets have default text which I would like to clear with a single button press. I have the following code:
from Tkinter import *
def delete_entries(fields):
for field in fields:
field.delete(0,END)
def UserInput(status,name):
optionFrame = Frame(root)
optionLabel = Label(optionFrame)
optionLabel["text"] = name
optionLabel.pack(side=LEFT)
var = StringVar(root)
var.set(status)
w = Entry(optionFrame, textvariable= var)
w.pack(side = LEFT)
optionFrame.pack()
if __name__ == '__main__':
root = Tk()
fields = 'ExperimentNumber', 'OutputFileName', 'npts1', 'block1'
ExperimentNumber = UserInput("1", "Experiment number")
OutputFileName = UserInput("output.txt", "Output file name")
npts1 = UserInput("1000", "Number of points")
block1 = UserInput("8", "Block size")
Delete_button = Button(root, text = 'Clear all', command = delete_entries(fields))
Delete_button.pack()
I have tried creating fields with the list of variables that I want to delete (as above) and iterating over this in the function delete_entries(), however this returns an error because the entries in fields are strings. I have tried replacing fields with fields = ExperimentNumber for example, but this returns an error because ExperimentNumber hasn't yet been defined. Finally I tried putting ExperimentNumber within the delete function i.e.
def delete_entries():
ExperimentNumber.delete(0,End)
but this doesn't work because ExperimentNumber has the attribute NoneType (I don't understand why this is, because the delete_entries() function isn't called until after the ExperimentNumber Entry widget is created via the function UserInput). How can I go about deleting all the text in the Entry widgets? I have about 20 of these in my actual code and would like the user to be able to clear all the fields with one button press.
You are on the right track but you missed two little things. I added those two in your code and tried to explain with comments.
from Tkinter import *
def delete_entries():
for field in fields:
field.delete(0,END)
def UserInput(status,name):
optionFrame = Frame(root)
optionLabel = Label(optionFrame)
optionLabel["text"] = name
optionLabel.pack(side=LEFT)
var = StringVar(root)
var.set(status)
w = Entry(optionFrame, textvariable= var)
w.pack(side = LEFT)
optionFrame.pack()
return w
#this return is crucial because if you don't return your widget's identity,
#you can not use them in another function
if __name__ == '__main__':
root = Tk()
ExperimentNumber = UserInput("1", "Experiment number")
OutputFileName = UserInput("output.txt", "Output file name")
npts1 = UserInput("1000", "Number of points")
block1 = UserInput("8", "Block size")
#you are saying it does not work because of they are strings.
#then don't assign strings to fields. assign variables.
fields = ExperimentNumber, OutputFileName, npts1, block1
#since fields is defined in global scope, you don't need to use it as parameter
Delete_button = Button(root, text = 'Clear all', command = delete_entries)
Delete_button.pack()
root.mainloop()

Tkinter, Trying to generate Checkbutton in loop

I am trying to generate standalone Checkbuttons in loop but who has the same name Checkbutton works together. I don't know where is my mistake...
#!/usr/bin/env python
#-*-coding:utf-8-*-
import os
from Tkinter import *
import ttk
def checkBoxText(st):
if st == 0:
st="Disabled"
if st == 1:
st="Enabled"
return st
root = Tk()
winSt={1:1,2:1,3:0,4:0}
cbTexts={}
cbVariables={}
cb={}
cb_x={ "1":"0.0", "2":"0.0", "3":"0.6", "4":"0.6" }
cb_y={"1": "0.1", "2": "0.8", "3": "0.1", "4": "0.8"}
for i in sorted(winSt.keys()):
cbTexts[i] = StringVar()
cbTexts[i].set(checkBoxText(winSt[i]))
cbVariables[i] = IntVar()
cbVariables[i].set(winSt[i])
cb[i] = Checkbutton(root, text=cbTexts[i].get(), variable=cbVariables[i].get())
cb[i].place(relx=cb_x[str(i)], rely=cb_y[str(i)], relheight=0.1,relwidth=0.4)
mainloop()
The problem is in this line:
cb[i] = Checkbutton(..., variable=cbVariables[i].get())
When you use the variable attribute, you must give it a reference to a variable object, not the value contained in the object. Change the code to this:
cb[i] = Checkbutton(..., variable=cbVariables[i])
You're making a somewhat similar mistake with the checkbutton text. You are creating a StringVar, but then using the value of the StringVar for the checkbutton text instead of the actual variable. Syntactically that's correct when used with the text attribute, but it's doing more work than it needs to. You should either use the textvariable attribute, or simply not create a StringVar.
Here's how to use the textvariable attribute instead of the text attribute:
cb[i] = Checkbutton(root, textvariable=cbTexts[i], ...)
You don't need the StringVar at all if this text will never change. If that's the case, you can just do this and save a couple lines of code:
cb[i] = Checkbutton(root, text=checkBoxText(winSt[i]), ...)

How to set the text/value/content of an `Entry` widget using a button in tkinter

I am trying to set the text of an Entry widget using a button in a GUI using the tkinter module.
This GUI is to help me classify thousands of words into five categories. Each of the categories has a button. I was hoping that using a button would significantly speed me up and I want to double check the words every time otherwise I would just use the button and have the GUI process the current word and bring the next word.
The command buttons for some reason are not behaving like I want them to. This is an example:
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
v = tk.StringVar()
def setText(word):
v.set(word)
a = ttk.Button(win, text="plant", command=setText("plant"))
a.pack()
b = ttk.Button(win, text="animal", command=setText("animal"))
b.pack()
c = ttk.Entry(win, textvariable=v)
c.pack()
win.mainloop()
So far, when I am able to compile, the click does nothing.
You might want to use insert method. You can find the documentation for the Tkinter Entry Widget here.
This script inserts a text into Entry. The inserted text can be changed in command parameter of the Button.
from tkinter import *
def set_text(text):
e.delete(0,END)
e.insert(0,text)
return
win = Tk()
e = Entry(win,width=10)
e.pack()
b1 = Button(win,text="animal",command=lambda:set_text("animal"))
b1.pack()
b2 = Button(win,text="plant",command=lambda:set_text("plant"))
b2.pack()
win.mainloop()
If you use a "text variable" tk.StringVar(), you can just set() that.
No need to use the Entry delete and insert. Moreover, those functions don't work when the Entry is disabled or readonly! The text variable method, however, does work under those conditions as well.
import Tkinter as tk
...
entry_text = tk.StringVar()
entry = tk.Entry( master, textvariable=entry_text )
entry_text.set( "Hello World" )
You can choose between the following two methods to set the text of an Entry widget. For the examples, assume imported library import tkinter as tk and root window root = tk.Tk().
Method A: Use delete and insert
Widget Entry provides methods delete and insert which can be used to set its text to a new value. First, you'll have to remove any former, old text from Entry with delete which needs the positions where to start and end the deletion. Since we want to remove the full old text, we start at 0 and end at wherever the end currently is. We can access that value via END. Afterwards the Entry is empty and we can insert new_text at position 0.
entry = tk.Entry(root)
new_text = "Example text"
entry.delete(0, tk.END)
entry.insert(0, new_text)
Method B: Use StringVar
You have to create a new StringVar object called entry_text in the example. Also, your Entry widget has to be created with keyword argument textvariable. Afterwards, every time you change entry_text with set, the text will automatically show up in the Entry widget.
entry_text = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_text)
new_text = "Example text"
entry_text.set(new_text)
Complete working example which contains both methods to set the text via Button:
This window
is generated by the following complete working example:
import tkinter as tk
def button_1_click():
# define new text (you can modify this to your needs!)
new_text = "Button 1 clicked!"
# delete content from position 0 to end
entry.delete(0, tk.END)
# insert new_text at position 0
entry.insert(0, new_text)
def button_2_click():
# define new text (you can modify this to your needs!)
new_text = "Button 2 clicked!"
# set connected text variable to new_text
entry_text.set(new_text)
root = tk.Tk()
entry_text = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_text)
button_1 = tk.Button(root, text="Button 1", command=button_1_click)
button_2 = tk.Button(root, text="Button 2", command=button_2_click)
entry.pack(side=tk.TOP)
button_1.pack(side=tk.LEFT)
button_2.pack(side=tk.LEFT)
root.mainloop()
Your problem is that when you do this:
a = Button(win, text="plant", command=setText("plant"))
it tries to evaluate what to set for the command. So when instantiating the Button object, it actually calls setText("plant"). This is wrong, because you don't want to call the setText method yet. Then it takes the return value of this call (which is None), and sets that to the command of the button. That's why clicking the button does nothing, because there is no command set for it.
If you do as Milan Skála suggested and use a lambda expression instead, then your code will work (assuming you fix the indentation and the parentheses).
Instead of command=setText("plant"), which actually calls the function, you can set command=lambda:setText("plant") which specifies something which will call the function later, when you want to call it.
If you don't like lambdas, another (slightly more cumbersome) way would be to define a pair of functions to do what you want:
def set_to_plant():
set_text("plant")
def set_to_animal():
set_text("animal")
and then you can use command=set_to_plant and command=set_to_animal - these will evaluate to the corresponding functions, but are definitely not the same as command=set_to_plant() which would of course evaluate to None again.
One way would be to inherit a new class,EntryWithSet, and defining set method that makes use of delete and insert methods of the Entry class objects:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
class EntryWithSet(tk.Entry):
"""
A subclass to Entry that has a set method for setting its text to
a given string, much like a Variable class.
"""
def __init__(self, master, *args, **kwargs):
tk.Entry.__init__(self, master, *args, **kwargs)
def set(self, text_string):
"""
Sets the object's text to text_string.
"""
self.delete('0', 'end')
self.insert('0', text_string)
def on_button_click():
import random, string
rand_str = ''.join(random.choice(string.ascii_letters) for _ in range(19))
entry.set(rand_str)
if __name__ == '__main__':
root = tk.Tk()
entry = EntryWithSet(root)
entry.pack()
tk.Button(root, text="Set", command=on_button_click).pack()
tk.mainloop()
e= StringVar()
def fileDialog():
filename = filedialog.askopenfilename(initialdir = "/",title = "Select A
File",filetype = (("jpeg","*.jpg"),("png","*.png"),("All Files","*.*")))
e.set(filename)
la = Entry(self,textvariable = e,width = 30).place(x=230,y=330)
butt=Button(self,text="Browse",width=7,command=fileDialog).place(x=430,y=328)

Categories

Resources