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
Related
I was trying to create a stock managing program as follows:
import mysql.connector as mc
from tkinter import*
import time
from datetime import date
mycon=mc.connect(host="localhost",user="root",passwd="1234",database="stock_manager")
cursor=mycon.cursor()
global stock_name
global stock_unit_price
global stocks_bought
def add():
global stock_name
global stock_unit_price
global stocks_bought
stock_name_label=Label(root,text="Enter Stock Name")
stock_name_entry=Entry(root,width=50)
stock_unit_price_label=Label(root,text="Enter unit price of stock")
stock_unit_price_entry=Entry(root,width=50)
stocks_bought_label=Label(root,text="Enter number of stocks bought: ")
stocks_bought_entry=Entry(root,width=50)
stock_name=stock_name_entry.get()
stock_unit_price=int(float(stock_unit_price_entry.get()))
stocks_bought=int(stocks_bought_entry.get())
stock_name_label.grid(row=2,column=0)
stock_name_entry.grid(row=3,column=0)
stock_unit_price_label.grid(row=4,column=0)
stock_unit_price_entry.grid(row=5,column=0)
stocks_bought_label.grid(row=6,column=0)
stocks_bought_entry.grid(row=7,column=0)
submit_stock_button=Button(root,text="Submit",command=submit)
submit_stock_button.grid(row=8,column=1)
def submit():
global stock_name
global stock_unit_price
global stocks_bought
submitted_label=Label(root,text="Submitted!")
total_investment=(stock_unit_price)*(stocks_bought)
date=date.today()
cursor.execute("insert into
all_stocks values(%s,%s,%s,%s,%s)"%(stock_name,stock_unit_price,stocks_bought,total_investment,date))
submitted_label.grid(row=9,column=1)
root=Tk()
title_label=Label(root,text="All Investments")
add_stock_button=Button(root,text="Add Stock",command=add)
title_label.grid(row=0,column=1)
add_stock_button.grid(row=1,column=0)
root.mainloop()
So what this program is supposed to do is let the user add a stock by clicking the button "add stock". It then reveals the entry fields as given in the add() function defined at the top. After inputting all the information, the user clicks submit. The program then sends a query to update a mysql database with the given information.
My problem is that I need "stock_unit_price" as float and "stocks_bought" as int so that the query is successful. But it appears that the entry field is giving a string value.
I tried converting the string into int like:
int('5.0')
And even tried like:
int(float('5.0'))
But nothing works. Pls help
You are using it wrongly .You cant get and assign at same time. I will suggest you to use StringVar . Please check the snippet
import tkinter as tk
root=tk.Tk()
root.geometry("300x100")
sk=tk.StringVar()
sb=tk.StringVar()
def Add():
stock_entry=int(stock_unit_price_entry.get())
stock_bought=int(stocks_bought_entry.get())
print(stock_entry+stock_bought)
sk.set("")
sb.set("")
stock_name_label = tk.Label(root,text = 'Enter unit price of stock')
stock_unit_price_entry = tk.Entry(root,textvariable = sk)
stocks_bought_label = tk.Label(root,text = 'Enter number of stocks bought:', )
stocks_bought_entry=tk.Entry(root, textvariable = sb)
submit_stock_button=tk.Button(root,text = 'Submit', command = Add)
stock_name_label.grid(row=0,column=0)
stock_unit_price_entry.grid(row=0,column=1)
stocks_bought_label.grid(row=1,column=0)
stocks_bought_entry.grid(row=1,column=1)
submit_stock_button.grid(row=2,column=1)
root.mainloop()
You will get integer as output
Or if you want to go by your method, you have to store your get value which is converted into integer in another variable and then you can use it.
stock_unit_price_entry=Entry(root,width=50)
stocks_bought_entry=Entry(root,width=50)
stock_unit=int(stock_unit_price_entry.get())
stock_bought=int(stocks_bought_entry.get())
print(stock_unit)
print(stock_bought)
This will also produce same integer output
I am trying to write a small program to use at work. It looks like this:
As you can see there are two dropdown menu's. When clicking on one of the buttons on the menu, filling in the other two Entry's (Korte beschrijving, datum) and clicking on Voeg incident toe (Add new incident) it should be saved as an entry in the database. I guess there are a few things not working and I'm getting two errors:
First I got the: "Option menu has no attribute get" error. Then I made two functions:
def drop_down_soort():
soort.get()
window.quit()
def drop_down_user():
user.get()
window.quit()
Now I am getting a new error:
This is the entire code: frontend and backend + error.
from tkinter import *
import backend
def alle_incidenten():
tekst_vak.delete(0,END)
for row in backend.alle():
tekst_vak.insert(END,row)
def drop_down_soort():
soort.get()
window.quit()
def drop_down_user():
user.get()
window.quit()
def voeg_toe():
backend.voeg_toe(drop_down_soort.get(),drop_down_user.get(),
beschrijving_tekst.get(),datum_tekst.get())
tekst_vak.delete(0,END)
tekst_vak.insert(END,(drop_down_soort.get(),drop_down_user.get(),
beschrijving_tekst.get(),datum_tekst.get()))
window=Tk()
window.wm_title("T I T A A N Incidenten Register")
soort = StringVar(window)
soort.set("Soort")
menu = OptionMenu(window, soort, "Incident", "RFC", "Opdracht")
menu.grid(row=0,column=0)
user = StringVar(window)
user.set("User")
user_menu = OptionMenu(window, user, "Jesse", "Jan", "Sirano", "Shannon",
"Niek", "Thomas", "Patrick")
user_menu.grid(row=1,column=0)
l3=Label(window,text="Korte beschrijving")
l3.grid(row=0,column=2)
l4=Label(window,text="Datum")
l4.grid(row=1,column=2)
beschrijving_tekst=StringVar()
l3=Entry(window,textvariable=beschrijving_tekst)
l3.grid(row=0,column=3)
datum_tekst=StringVar()
l4=Entry(window,textvariable=datum_tekst)
l4.grid(row=1,column=3)
tekst_vak=Listbox(window,height=10,width=35)
tekst_vak.grid(row=2,column=0,rowspan=6,columnspan=2)
scrollbar1=Scrollbar(window)
scrollbar1.grid(row=2,column=2,rowspan=10)
tekst_vak.configure(yscrollcommand=scrollbar1.set)
scrollbar1.configure(command=tekst_vak.yview)
b1=Button(window,text="Alle incidenten", width=14, bg='Gray',
command=alle_incidenten)
b1.grid(row=2,column=3)
b2=Button(window,text="Voeg incident toe", width=14, bg='Gray',
command=voeg_toe)
b2.grid(row=3,column=3)
b3=Button(window,text="Pas incident aan", width=14, bg='Gray')
b3.grid(row=4,column=3)
b4=Button(window,text="Verwijder incident", width=14, bg='Red')
b4.grid(row=5,column=3)
b5=Button(window,text="Sluit programma", width=14, bg='Orange')
b5.grid(row=6,column=3)
window.mainloop()
Backend:
import sqlite3
def verbind_met_database():
verbinding=sqlite3.connect("titaan_incidenten.db")
cur=verbinding.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS titaan_incidenten
(id INTEGER PRIMARY KEY, soort TEXT, user TEXT, beschrijving TEXT,
datum TEXT)")
verbinding.commit
verbinding.close()
def alle():
verbinding=sqlite3.connect("titaan_incidenten.db")
cur=verbinding.cursor()
cur.execute("SELECT * FROM titaan_incidenten")
rijen=cur.fetchall()
verbinding.close()
return rijen
def voeg_toe(soort,user,beschrijving,datum):
verbinding=sqlite3.connect("titaan_incidenten.db")
cur=verbinding.cursor()
cur.execute("INSERT INTO titaan_incidenten VALUES (NULL,?,?,?,?)",
(soort,user,beschrijving,datum))
verbinding.commit()
verbinding.close()
def pas_aan(id,soort,user,beschrijving,datum):
verbinding=sqlite3.connect("titaan_incidenten.db")
cur=verbinding.cursor()
cur.execute("UPDATE titaan_incidenten SET
soort=?,user=?,beschrijving=?,datum=? WHERE id=?",
(id,soort,user,beschrijving,datum))
verbinding.commit
verbinding.close()
def verwijder():
verbinding=sqlite3.connect("titaan_incidenten.db")
cur=verbinding.cursor()
cur.execute("DELETE FROM titaan_incidenten WHERE id=?",(id,))
ERROR:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\crisc\Anaconda3\lib\tkinter\__init__.py", line 1702, in __
call__
return self.func(*args)
File "frontend.py", line 18, in voeg_toe
backend.voeg_toe(drop_down_soort.get(),
drop_down_user.get(),beschrijving_tekst.get(),datum_tekst.get())
AttributeError: 'function' object has no attribute 'get'
For me, working with the dropdown menu's is something I never done before. So maybe the functions I created are not necessary. If somebody could give me some pointers on how to get that menu working, that would be great!
I edited it with the correct error and code. Sorry for the earlier confusion.
Thanks,
Cris
Based on the information you've provided, you're passing the wrong parameters to backend.voeg_toe() function. Rather than using drop_down_soort.get(), drop_down_user.get(), you should pass just these functions without .get() which does not make sense to your function, and return values inside the function.
Function bodies can contain one or more return statement. They can be situated anywhere in the function body. A return statement ends the execution of the function call and "returns" the result, i.e. the value of the expression following the return keyword, to the caller. If the return statement is without an expression, the special value None is returned. If there is no return statement in the function code, the function ends, when the control flow reaches the end of the function body and the value "None" will be returned.
for example:
def drop_down_soort():
test = soort.get() #not sure even if soort.get() is getting any value, but I suppose yes!!!!
window.quit()
return test
def drop_down_user():
test = user.get() #not sure even if user.get() is getting any value, but I suppose yes!!!!
window.quit()
return test
so then whenever you call backend.voeg_toe(drop_down_soort(),drop_down_user(),....) it will get the returning values of those two functions and will pass it as parameters.
I am serializing my GUI to save the information.But the problem is that it is only saving the last value entered not all of them.
So now the problem is that when I click on the + button, the row gets incremented but only the last name entered is saved. I want to save all of them
def increment(self):
current_row=1
MoreButton=Button(self.listFrame,text="+",command=entry_1(self))
MoreButton.grid(column=1,row=0)
def entry_1(self):
self.entryName=Entry(self.listFrame)
self.entryName.grid(column=1,row=current_row,sticky="EW")
self.entryName.get()
nameLabel=Label(self.listFrame,text="NAME")
nameLabel.grid(column=0,row=current_row)
save_button=Button(self.listFrame,text="save",command=save_data(self))
save_button.grid(column=2,row=0)
current_row=current_row+1
def save_data(self):
data={
"Name":self.entryName.get(),
}
with open("test.json","wb") as f:
dill.dump(data,f)
def load_data(self):
with open("test.json","rb") as f:
data=dill.load(f)
Sorry for the past incorrect answer. I think the problem is that your entry_1 function creates entry boxes with the same name. This means when you try getting the text in it, it gets the text of the last one only. Here is the full code I created that creates entry's with different names. It is in a class but I don't know how you want them set out.
from Tkinter import *
import dill
import sys
boxes=[]
no_of_boxes=0
root=Tk()
current_row=1
current_box=0
data={}
class Main(object):
#this is used every time a new entry is created
def save_data(self):
global current_box
global current_row
global no_of_boxes
###this it the key. It creates the entries
###in a list so that we can access them
###without the name
boxes.append(Entry(root))
boxes[-1].grid(column=1, row=current_row)
###
try:
data["Name"+str(current_box)]=boxes[-2].get()
except:
data["Name"+str(current_box)]=boxes[-1].get()
current_row+=1
current_box+=1
no_of_boxes+=1
print no_of_boxes
print boxes
print data
with open("test.json","wb") as f:
dill.dump(data,f)
#to save when closing
def save_close(self):
global current_box
global current_row
boxes.append(Entry(root))
boxes[-1].grid(column=1, row=current_row)
data["Name"+str(current_box)]=boxes[-2].get()
with open("test.json","wb") as f:
dill.dump(data,f)
print no_of_boxes
print boxes
print data
sys.exit()
def load_data(self):
with open("test.json","rb") as f:
data=dill.load(f)
def entry_1(self):
global current_row
global no_of_boxes
nameLabel=Label(root,text="NAME")
nameLabel.grid(column=0,row=current_row)
self.save_data()
app=Main()
MoreButton=Button(root,text="+",command=app.entry_1)
MoreButton.grid(column=1,row=0)
CloseButton=Button(root, text="Close and save", command=app.save_close)
CloseButton.grid(column=2, row=0)
root.mainloop()
The saved data is saved as "Name" plus current_box in data so to get it go data["Name1"] for entry box 1 and so on.
I hope this is a better answer than my last one!
P.S if you still have problems with other parts of this code, feel free to contact me on my website.
I'm trying to create a couple of functions which do things in a sequential order. First they need to open a new window and display a label, then they need to wait for some seconds, then they need to call another function. However, I'm struggling to get the functions to wait, all the methods I've tried (.after, .sleep, .wait_visibility) seem to be ignored and it just skips to the next function call without pausing.
Here's what I have (sorry if it's messy, I'm new to python):
from tkinter import *
import time
root =Tk()
root.geometry('600x600')
def scale_screen(event = None):
global s_screen
s_screen = Toplevel(root)
s_screen.title('Residual Inhibition Tester')
s_screen.geometry('600x600')
s_screen.transient(root)
s_screen.bind('<Return>', sel)
global var
var = IntVar()
scale = Scale(s_screen, variable = var, orient = HORIZONTAL, length = 1000)
scale.focus_set()
scale.pack(anchor=CENTER)
button = Button(s_screen, text="Select", command=sel)
button.pack(anchor=CENTER)
def sel(event = None):
label = Label(s_screen)
selection = "Value = " + str(var.get())
label.config(text = selection)
interval_screen()
def interval_screen():
global i_screen
i_screen = Toplevel(root)
i_screen.geometry('600x600')
i_screen.transient(root)
i_label = Label(i_screen, text = "Please Wait")
i_label.pack(anchor = CENTER)
s_screen.destroy()
i_screen.after(3000, masker_screen)
#time.sleep(3)
#i_screen.after(300,i_label.configure(text="Playing New Masker Noise"))
#root.wait_visibility(window = i_screen)
def masker_screen():
global m_screen
m_screen = Toplevel(root)
m_screen.geometry('600x600')
m_screen.transient(root)
m_label = Label(m_screen, text = "Playing New Masker Noise").pack(anchor = CENTER)
m_screen.after(3000, lambda: scale_screen(event = None))
i_screen.destroy()
b1 = Button(root, command = scale_screen).pack(anchor=CENTER)
root.bind('<Return>', scale_screen)
root.mainloop()
In this example, the program will run but just skip the interval_screen entirely and just do the masker_screen. I'm also not averse to just using one screen and using the .configure methods to change the label text if that's easier.
Thanks!
Without seeing all the ways you tried it, it's impossible to know what you did wrong. In general you should never call time.sleep and you should never call after with just a single argument. Also, when you use after with two arguments, the second argument must be a reference to a function.
The proper way to do this is to have your first function call your second function via after:
def interval_screen():
...
i_screen.after(3000, maker_screen)
def masker_screen():
...
m_screen.after(3000, lambda: scale_screen(event = None))
Note that in your updated question you're using after incorrectly:
m_screen.after(3000, scale_screen(event = None))
You're calling the function scale_screen(...) immediately, and giving the result of that to the after function. If you need to pass arguments to your function you must create another function that does not require arguments. The simplest way to do this is with lambda, though you can also use functools.partial or you can create your own function.
I'm having some issues with the following code. This is the first time that I'm working with a GUI and it's been a while since I've worked with python as well. When I try to execute the solfield function with the button, it yields no output.
from Tkinter import *
import math
master = Tk()
n = float()
I = float()
def solfield():
pass
label_coils = Label(text='Number of Coils Per Meter', textvariable=n)
label_coils.grid()
coils = Entry(master)
coils.grid()
label_current = Label(text='Current in Amps', textvariable=I)
label_current.grid()
current = Entry(master)
current.grid()
calculate_button = Button(text='Calculate', command=solfield())
calculate_button.grid()
label_bfield = Label(text='B Field in +z Direction')
label_bfield.grid()
label_result = Label(text='solfield')
label_result.grid()
master.title('Coil Gun Simulation')
master.mainloop()
def solfield():
mu0 = math.pi*4e-7
solfield = mu0*n*I
print solfield
Any other tips would be appreciated as well, as there will eventually be much more coding for me to do.
This has been solved. If anyone is interested, here is the code after several fixes were made:
from Tkinter import *
import math
master = Tk()
label_coils = Label(text='Number of Coils Per Meter')
label_coils.grid()
coils = Entry(master)
coils.grid()
label_current = Label(text='Current in Amps')
label_current.grid()
current = Entry(master)
current.grid()
def solfield():
mu0 = math.pi*4e-7
n = float(coils.get())
I = float(current.get())
fieldmag = mu0*n*I
print fieldmag
calculate_button = Button(text='Calculate', command=solfield)
calculate_button.grid()
label_bfield = Label(text='B Field in +z Direction')
label_bfield.grid()
label_result = Label(text='solfield')
label_result.grid()
master.title('Coil Gun Simulation')
master.mainloop()
The problem is here:
calculate_button = Button(text='Calculate', command=solfield())
To pass the function solfield itself as the command, just use its name:
calculate_button = Button(text='Calculate', command=solfield)
What you're doing is calling the function, and then passing the return value of that function as the command.
Since you defined solfield above as do-nothing function, that return value is None, so you're telling calculate_button that its command=None, and it's properly doing nothing.
Meanwhile, as SethMMorton pointed out (but then deleted):
You have two functions named solfield, and you are naming a variable solfield in one of your solfield functions. Remove the empty function (the one with pass), and using a different variable name in the remaining function.
This isn't causing your actual problem, but it's certainly adding to the confusion that makes it harder for you to find the problem. (For example, if you hadn't included the excess empty definition of solfield at all, you would have gotten a NameError in the incorrect line, which would have made things easier to debug.)
Putting it all together, what you should do is:
Get rid of the empty (pass-only) definition of solfield.
Move the real implementation of solfield up above the point where you build the GUI.
Don't name a local variable solfield within the function.
Pass just solfield, not solfield() as the command for calculate_button.