Python - tkinter how to use if statements with get function? - python

The title is not very easy to understand, I know, so let me explain it here.
Basically if I do
if variable.get() == "Select Website":
print("ok")
it will print out "ok", but if I change it from "Select Website" to "Fareham" as well as the option in the drop down box to "Fareham" it will not notice it changed. If I want it to notice it changed I would need to do a while loop, but that would stop the script running in the first place.
How can I make the script print out "ok" if variable is changed to "Fareham"?
Current Code:
import tkinter
sites = [
"Fareham",
"Hants",
"Southampton",
"Eastleigh",
"Havant",
"Gosport",
]
win = tkinter.Tk()
win.geometry("500x500")
variable = tkinter.StringVar(win)
variable.set("Select Website")
drop = tkinter.OptionMenu(win, variable, *sites)
drop.pack()
if variable.get() == "Fareham":
print("ok")
win.mainloop()

You can do this by associating a callback funtion to the drop down menu:
import tkinter
def your_callback(*args):
if args[0] == "Fareham":
print("ok")
sites = [
"Fareham",
"Hants",
"Southampton",
"Eastleigh",
"Havant",
"Gosport",
]
win = tkinter.Tk()
win.geometry("500x500")
variable = tkinter.StringVar(win)
variable.set("Select Website")
drop = tkinter.OptionMenu(win, variable, *sites, command = your_callback)
drop.pack()
win.mainloop()

Here's where some important info can be found:
http://effbot.org/tkinterbook/variable.htm
By setting an observer to the variable variable, it will check it everytime it changes.
def check(*args):
if variable.get() == 'Fareham':
print 'ok'
variable.trace(
'w', # 'w' checks when a variable is written (aka changed)
check # this is the function it should call when the variable is changed
)
Just put the code in place of your current if statement and it will work like a charm.

Related

Calling function from another Python file

I am trying to call a function from another Python file after a button is click. I have imported the file and used the FileName.fuctionName() to run the function. The problem is my exception keeps catching. I am guessing that the data from the function being called is not being grabbed.What I am trying to do is have a user fill out a Tkinter gui then click a button. Once the button is click the user will then be asked to scan their tag (rfid) and that data will then be sent to a firebase real time database which will store the user's inputted info along with the card_id and user_id that was created when the tag was scanned.
Im kinda at a loss because other than the exception catching I am not getting any other errors, any thoughts? I have posted the code below along with comments.
error : local variable 'user_id' referenced before assignment
from tkinter import *
#Second File
import Write
from tkcalendar import DateEntry
from firebase import firebase
data = {}
global user_id
# Firebase
firebase= firebase.FirebaseApplication("https://xxxxxxx.firebaseio.com/",None)
# button click
def sub ():
global user_id
#setting Variables from user input
name = entry_1.get()
last = entry_2.get()
number = phone.get()
try:
#Calling Function from other file
Write.scan()
if Write.scan():
#getting the New User Id
user_id= new_id
#User Info being sent to the Database
data = {
'Name #': name,
'Last': last,
'Number': number,
'Card #':user_id
}
results = firebase.post('xxxxxxxx/User',data)
except Exception as e:
print(e)
# setting main frame
root = Tk()
root.geometry('850x750')
root.title("Registration Form")
label_0 = Label(root, text="Registration form",width=20,font=("bold", 20))
label_0.place(x=280,y=10)
label_1 = Label(root, text="First Name",width=20,font=("bold", 10))
label_1.place(x=80,y=65)
entry_1 = Entry(root)
entry_1.place(x=240,y=65)
label_2 = Label(root, text="Last Name",width=20,font=("bold", 10))
label_2.place(x=68,y=95)
entry_2 = Entry(root)
entry_2.place(x=240,y=95)
phoneLabel = Label(root, text="Contact Number : ",width=20,font=("bold", 10))
phoneLabel.place(x=400,y=65)
phone = Entry(root)
phone.place(x=550,y=65)
Button(root, text='Submit',command = sub,width=20,bg='brown',fg='white').place(x=180,y=600)
root.mainloop()
Write.py file being Imported
import string
from random import*
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
reader = SimpleMFRC522()
#Function being called
def scan():
try:
#Creating user hash
c = string.digits + string.ascii_letters
new_id = "".join(choice(c) for x in range(randint(25,25)))
print("Please Scan tag")
#Writing to tag
reader.write(new_id)
if reader.write(new_id):
print("Tag Scanned")
else:
print("Scan Tag First")
print("Scanning Complete")
finally:
GPIO.cleanup()
I see that the value new_id in one file isn't going to influence the value with the same name in the other file, for a similar reason as for the first problem. In both places it appears, new_id is a local variable that only exists in the enclosing function.
Another issue I see is that you're calling Write.scan() twice in a row. Do you mean to be calling it twice? I expect not.
Also, you're testing the return value of Write.scan(), but that function doesn't return a value. So I think that the code in the if block in the first file will never run.
Globals are a bad idea in general, as they're easy to get wrong and they tend to obscure what the code is really doing. "Never say never", but I'll say that I very rarely find the need for a global variable in Python. In your case, I think it would be much better to have Write.scan() return the value of the new user id instead of passing it back as a global. Since you're testing the value of Write.scan(), maybe this is what you were thinking of doing already. Here are the changes I'd make to address these three issues and hopefully get your code working the way you want...
...
def sub ():
...
try:
#Calling Function from other file
new_id = Write.scan()
if new_id:
#getting the New User Id
user_id= new_id
...
...
def scan():
try:
...
new_id = "".join(choice(c) for x in range(randint(25,25)))
...
return new_id
finally:
GPIO.cleanup()
It's impossible to tell what your problem is, because there is no place in your code that references user_id and hence the error message you cite can't come from the code you provide. However, I see a pretty common mistake that your code appears to be making that could very well account for why you expect user_id to be defined somewhere in your code and yet it is not...
In your first block of code, the global user_id is not being set by the sub function. Rather, when the sub function calls user_id=new_id, it is creating and setting a variable that is local to that function. When that function ends, the result of that call is lost and the global user_id is still undefined.
What you want is to define user_id as being global inside of the sub() function. Just add global user_id anywhere near the top of the function definition.
Here's an example of what I'm talking about:
global x
def sub():
x = 3
sub()
print(x)
result:
Traceback (most recent call last):
File "t", line 7, in <module>
print(x)
NameError: global name 'x' is not defined
whereas:
global x
def sub():
global x
x = 3
sub()
print(x)
result:
3

Functions return error 'NameError: name 'context_word' is not defined' when using global variables

I've got some code that creates a context menu when the user selected a word in a tkinter text widget. The text widget automatically checks to see if the word is correct every time 'space' is pressed. if the selected word is incorrect, part of the context menu asks the user if they would like to correct the word. This should then get the selected word, loop through the text widget (using the search method) until it's found an instance of the word, then replace it with the correct word.
Here is my code (don't mind the 'WOOOO!', it just allows the user to use the context menu with out selecting a word)
from tkinter import *
root = Tk()
notepad = Text(root)
def replace_word():
global selected_word
global context_word
replace_start = notepad.search(selected_word, 1.0, END)
while replace_start:
replace_offset = '+%dc' % len(selected_word)
replace_end = replace_start + replace_offset
notepad.delete(replace_start, replace_end)
notepad.insert(replace_start, context_word)
replace_start = notepad.search(replace_start, 1.0, END)
def add_to_dictionary_context():
global selected_word
global context_word
global spell_dict
spell_dict.word_frequency.add(selected_word)
with open('dictionary_file_add.txt', 'a+') as f_context:
selected_word_to_add = selected_word + '\n'
f_context.write(selected_word_to_add)
def notepad_context_menu(event):
global spell_dict
context_menu = Menu(notepad_frame, tearoff = 0)
try:
selected_word = notepad.selection_get().strip('\n')
context_word = spell_dict.correction(selected_word)
if (selected_word not in spell_dict) and (selected_word != context_word): # Makes sure selected_word is incorrect
dictionary_context = Menu(context_menu, tearoff = 0)
context_menu.add_cascade(label = "Dictionary... ", menu = dictionary_context)
dictionary_context.add_command(label = context_word, command = replace_word)
dictionary_context.add_command(label = "Add to dictionary", command = add_to_dictionary_context)
context_menu.add_separator()
except:
print("WOOOO!")
context_menu.add_command(label = "Undo", command = undo)
context_menu.add_command(label = "Redo", command = redo)
context_menu.add_separator()
context_menu.add_command(label = "Cut", command = cut)
context_menu.add_command(label = "Copy", command = copy)
context_menu.add_command(label = "Paste", command = paste)
try:
context_menu.tk_popup(event.x_root, event.y_root)
finally:
context_menu.grab_release()
notepad.bind('<Button-3>', notepad_context_menu)
root.mainloop()
However, when I run this code, if I press the replace word button in the context menu it returns the error:
File "C:\Users\User\Documents\Python stuff\Other apps\Veteris\Scripts\Veteris_program.py", line 1058, in replace_word
replace_start = notepad.search(selected_word, 1.0, END)
NameError: name 'selected_word' is not defined
and if I press the 'Add to dictionary' button it returns:
File "C:\Users\User\Documents\Python stuff\Other apps\Veteris\Scripts\Veteris_program.py", line 1068, in replace_word
notepad.insert(replace_start, context_word)
NameError: name 'context_word' is not defined
Basically, my code isn't reading my global statements. My add_word_to_dictionary_context() and replace_word() functions only run after I've declared context_word and selected_word.
I've tried putting
selected_word = ''
context_word = ''
at the start, and no error is returned but nothing happens. I've also tried putting replace_word() and add_to_dictionary-context() after notepad_context_menu(), but it still spits the same errors.
I'm really stuck.
Thanks for the help :)
You will need to add the global statements to the functions where you assign (write, as it were) into the global functions, not into those which just read them.
Your code is writing them in the notepad_context_menu function:
selected_word = notepad.selection_get().strip('\n')
context_word = spell_dict.correction(selected_word)
and since you don't have declared them global in that function, they're just local variables which get "thrown away".
On the other hand, you don't need to declare spell_dict global in any of these functions, since you're not assigning into it.
That said, it'd be much better not to use global variables to begin with, e.g. by using anonymous lambda functions that capture those values and pass them in:
def replace_word(selected_word, context_word):
pass # ...
def add_to_dictionary_context(selected_word):
spell_dict.word_frequency.add(selected_word)
# ...
def notepad_context_menu(event):
# ...
selected_word = notepad.selection_get().strip("\n")
context_word = spell_dict.correction(selected_word)
# ...
dictionary_context.add_command(
label=context_word,
command=lambda: replace_word(selected_word, context_word),
)
dictionary_context.add_command(
label="Add to dictionary",
command=lambda: add_to_dictionary_context(selected_word),
)
I know this was answer already by AKX but as explained you should be posting a MRE. Though you have updated your question it is still not an MRE. You are missing the required import for spell checking and your notepad was never added to the screen.
Here is an example of what an MRE might look like including correction to make it work.
You needed to provide global for context_word and selected_word into your notepad_context_menu. They way functions look for variable is like this:
First it checks locally for the variable and if the variable is not defined locally it will then check the global namespace for the variable. So for functions that only work with the variable and do not define it we do not need the global defined in the function. However for functions that will be defining a variable IE var = something then we need to set the global in the function.
I also added global to your replace_word function as here I am resetting context_word and selected_word back to "" after replace is finished.
Note I removed all the parts that are not part of the testing for this issue. IE all the context_menu items unrelated to the spell check and the redundant replace_start in your function.
Lastly I made some PEP8 changes to your formatting.
Let me know if you have any questions:
import tkinter as tk
import spellchecker as sc
root = tk.Tk()
notepad = tk.Text(root)
notepad.pack()
spell_dict = sc.SpellChecker()
context_word = ''
selected_word = ''
def replace_word():
global context_word, context_word, selected_word
replace_start = notepad.search(selected_word, 1.0, "end")
replace_offset = '+%dc' % len(selected_word)
replace_end = replace_start + replace_offset
notepad.delete(replace_start, replace_end)
notepad.insert(replace_start, context_word)
context_word = '' # added to reset context and selected words.
selected_word = ''
def add_to_dictionary_context():
global selected_word
spell_dict.word_frequency.add(selected_word)
with open('dictionary_file_add.txt', 'a+') as f_context:
selected_word_to_add = selected_word + '\n'
f_context.write(selected_word_to_add)
def notepad_context_menu(event):
global spell_dict, selected_word, context_word
context_menu = tk.Menu(notepad, tearoff=0)
try:
selected_word = notepad.selection_get().strip('\n')
context_word = spell_dict.correction(selected_word)
if selected_word not in spell_dict and selected_word != context_word:
dictionary_context = tk.Menu(context_menu, tearoff=0)
context_menu.add_cascade(label="Dictionary... ", menu=dictionary_context)
dictionary_context.add_command(label=context_word, command=replace_word)
dictionary_context.add_command(label="Add to dictionary", command=add_to_dictionary_context)
context_menu.add_separator()
except:
print("WOOOO!")
try:
context_menu.tk_popup(event.x_root, event.y_root)
finally:
context_menu.grab_release()
notepad.bind('<Button-3>', notepad_context_menu)
root.mainloop()

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

tkinter radiobutton not updating variable

--UPDATE:
I changed
variable=self.optionVal.get()
to
variable=self.optionVal
But nothing changed.Also I wonder why it automatically call self.selected while compiling?
----Original:
I'm trying to get familiar with radiobutton, but I don't think I understand how radiobutton works. Here's a brief code for demonstration:
self.optionVal = StringVar()
for text, val in OPTIONS:
print(text,val)
radioButton = Radiobutton(self,
text=text,
value=val,
variable=self.optionVal.get(),
command = self.selected())
radioButton.pack(anchor=W)
def selected(self):
print("this option is :"+self.optionVal.get())
In my opinion this should work like once I choose certain button, and it prints out "this option is *the value*", however now what it does is once compiled, it prints out everything, and the self.optionVal.get() is blankspace, as if value wasn't set to that variable.
I wonder what happens to my code,
Many thanks in advance.
AHA! I beleive I've figured it out. I had the exact same issue. make sure you are assigning a master to the IntVar like self.rbv=tk.IntVar(master) #or 'root' or whatever you are using):
import Tkinter as tk
import ttk
class My_GUI:
def __init__(self,master):
self.master=master
master.title("TestRadio")
self.rbv=tk.IntVar(master)#<--- HERE! notice I specify 'master'
self.rb1=tk.Radiobutton(master,text="Radio1",variable=self.rbv,value=0,indicatoron=False,command=self.onRadioChange)
self.rb1.pack(side='left')
self.rb2=tk.Radiobutton(master,text="Radio2",variable=self.rbv,value=1,indicatoron=False,command=self.onRadioChange)
self.rb2.pack(side='left')
self.rb3=tk.Radiobutton(master,text="Radio3",variable=self.rbv,value=2,indicatoron=False,command=self.onRadioChange)
self.rb3.pack(side='left')
def onRadioChange(self,event=None):
print self.rbv.get()
root=tk.Tk()
gui=My_GUI(root)
root.mainloop()
try running that, click the different buttons (they are radiobuttons but with indicatoron=False) and you will see it prints correctly changed values!
You're very close. Just take out the .get() from self.optionVal.get(). The Radiobutton constructor is expecting a traced variable, you're giving it the result of evaluating that variable instead.
You need to:
Remove the .get() from the variable=self.optionVal argument in the constructor the button. You want to pass the variable, not the evaluated value of the variable; and
Remove the parenthesis from command=self.selected() and use command=self.selected instead. The parenthesis says "call this function now and use the return value as the callback". Instead, you want to use the function itself as the callback. To better understand this, you need to study closures: a function can return a function (and, if that was the case, that would be used as your callback).
EDIT: A quick reminder, also: Python is not compiled, but interpreted. Your callback is being called while the script is being interpreted.
def view(interface):
choice = interface.v.get()
if choice == 0:
output = "0"
elif choice == 1:
output = "1"
elif choice == 2:
output = "2"
elif choice == 3:
output = "3"
else:
output = "Invalid selection"
return tk.messagebox.showinfo('PythonGuides', f'You Selected {output}.')
class Start:
def __init__(self):
self.root = tk.Tk()
self.root.geometry('500x500')
self.root.resizable(False, False)
self.root.title('find out the degree of severity')
self.v = tk.IntVar()
dolori_ossa = {"nessun dolore": 0,
"dolori articolari": 1,
"frattura composta": 2,
"frattura scomposta": 3}
for (txt, val) in dolori_ossa.items():
tk.Radiobutton(self.root,
text=txt,
variable=self.v,
value=val,
command=lambda:view(self)
).pack()

First letters of a Tkinter input

My program should check if the first three letters of the input word are similar to a predefined word.
I've made a GUI with Tkinter and want to get the letters of the input field.
Somehow I can't implement it in like I would do without Tkinter.
That's how I do it just for the shell:
text = raw_input('Enter a word: ')
if (text[0] + text[1] + text[2] == 'sag'):
print "sagen"
else:
print "error"
So, when I input the word "sagst" it checks the first three letters and should put out "sagen". Works fine.
I learned that e.g. inputfield.get() gets the input of the entry "inputfield".
But how can I check the first letters of that "inputfield"?
A small selection:
from Tkinter import*
root = Tk()
def check():
if (text[0] + text[1] + text[2] == 'sag'):
print "True"
else:
print "False"
inputfield = Entry(root)
inputfield.pack()
but = Button(root,text='Check!', command = check)
but.pack()
text = inputfield.get()
root.mainloop()
Does not work...
I hope you can understand my question and will answer soon. (Sorry for my bad english and my bad Python skills) ;-)
Thanks!
Your check function will have to retrieve the textfield after the button has been pressed:
def check():
text = inputfield.get()
print text.startswith('sag')
I've changed your test a little, using .startswith(), and directly printing the result of that test (print will turn boolean True or False into the matching string).
What happens in your code is that you define inputfield, retrieve it's contents (obviously empty), and only then show the TKInter GUI window by running the mainloop. The user never gets a chance to enter any text that way.
You can also check this without the need for a button (Now it will check whenever the user presses "Enter"):
from Tkinter import *
root = Tk()
def check(*event):
text = inputfield.get()
print text.startswith('sag')
inputfield = Entry(root)
inputfield.bind('<Return>',check)
inputfield.pack()
root.mainloop()
You can also do other things to have your widget validate the entry as you type. (The link is old, but it also points to newer features that allow you to do this without subclassing).
You're not actually putting the value in the input field into the text variable.
I renamed the value from text to input_text because it was confusing to me. I also changed from using text[0] + text[1] + text[2] to using startswith(). This will keep you from getting IndexErrors on short strings, and is much more pythonic.
from Tkinter import*
root = Tk()
def check():
input_text = inputfield.get()
if input_text.startswith('sag'):
print "True"
else:
print "False"
inputfield = Entry(root)
inputfield.pack()
input_text = inputfield.get()
print input_text # Note that this never prints a string, because it only prints once when the input is empty.
but = Button(root, text='Check!', command=check)
but.pack()
root.mainloop()
The key change is that the check function needs to actually get the value in the inputfield.
Here is a version which uses an Entry widget which validates its contents as the user types (so the user does not have to click a button or even press Return).
import Tkinter as tk
class MyApp(object):
'''
http://effbot.org/zone/tkinter-entry-validate.htm
http://effbot.org/tkinterbook/entry.htm
http://www.tcl.tk/man/tcl8.5/TkCmd/entry.htm#M-validate
'''
def __init__(self, master):
vcmd = (master.register(self.validate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(master, validate = 'key',
validatecommand = vcmd)
self.entry.pack()
self.entry.focus()
def validate(self, action, index, value_if_allowed,
prior_value, text, validation_type, trigger_type, widget_name):
dtype = {'0':'delete', '1':'insert', '-1':'other'}[action]
n = min(3, len(value_if_allowed))
valid = False
if dtype == 'insert':
if value_if_allowed[:n] == 'sag'[:n]: valid = True
else: valid = False
else: valid = True
print(valid)
return True
root = tk.Tk()
app = MyApp(root)
root.mainloop()

Categories

Resources