I've made a gui with a variable amount of buttons and entries, dependent on earlier user inputs. I want to store the new entries in a list. My loop however will only append the last entry created in the loop. How can I get each button to append it's respective user entry?
def getFuseType():
(Fuse.fuseType).append(e.get()
print(Fuse.fuseType)
for i in range(1,int(Site.NoFuses)+1):
l = Label(FuseWindow, text = "Fuse Type")
e = Entry(FuseWindow)
l.grid(row = 1, column = i+3)
e.grid(row = 2, column = i+3)
b = Button(FuseWindow, text = "ok", command = getFuseType)
b.grid(row = 3, column = i+3)
Fuse GUI
see the image I've uploaded, The top right 'OK' button appends the entry. I want the top Left button to also append it's respective entry.
The problem is that the function isn't creating a closure around the e variable so they only affect the most recent (last) object assigned to e. If you really want to do this you'll need to use a defaulted arg..
for i in range(1,int(Site.NoFuses)+1):
l = Label(FuseWindow, text = "Fuse Type")
e = Entry(FuseWindow)
l.grid(row = 1, column = i+3)
e.grid(row = 2, column = i+3)
def getFuseType(e=e):
(Fuse.fuseType).append(e.get())
print(Fuse.fuseType)
b = Button(FuseWindow, text = "ok", command = getFuseType)
b.grid(row = 3, column = i+3)
Get getFuseType out of the for, it is defined every loop iteration and hence you only get the last create entry e
def getFuseType(ent):
Fuse.fuseType.append(ent)
print(Fuse.fuseType)
for i in range(1,int(Site.NoFuses)+1):
l = Label(FuseWindow, text = "Fuse Type")
e = Entry(FuseWindow)
ent = e.get()
l.grid(row = 1, column = i+3)
e.grid(row = 2, column = i+3)
b = Button(FuseWindow, text = "ok", command = lambda ent = ent:getFuseType(ent))
b.grid(row = 3, column = i+3)
Related
I have a tkinter window class that I've made and my delete function is not working properly.
my_window = tk.Tk()
class QuoteForm():
def __init__(self,master):
self.file_data = ''
self.master = master
self.master.rowconfigure(0, weight=1)
self.master.rowconfigure(1, weight= 1)
self.master.rowconfigure(2, weight = 1)
master.geometry('600x400')
master.resizable(False,False)
#create the frames
self.directory_frm = tk.Frame(master=master)
self.directory_frm.grid(row=0) #this is the frame for the directory
self.add_on_frm = tk.Frame(master=master)
self.add_on_frm.grid(row=1) #this is the frame for add-ons input
self.button_frm = tk.Frame(master=master)
self.button_frm.grid(row=2) #this is the frame for
#creates buttons, entries, labels
self.load_directory_frame() #creates and grids the directory button
self.load_add_on_frame() #creates and grids the entry buttons and labels
self.load_button_frame() #creates and grids the buttons
my_window.mainloop()
def load_add_on_frame(self):
vcmd = (self.master.register(self.validate_ent), '%S')
#create inputs and labels for add-ons
self.trip_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd, name='trip_ent')
self.trip_ent.grid(column= 1, row = 0)
self.raw_cutouts_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.raw_cutouts_ent.grid(column= 3, row = 0)
def clear_entries(self):
entries = (self.trip_ent, self.raw_cutouts_ent) #list of entries to loop (there are a total of 12 in the actual code)
for entry in entries:
entry.delete(0,len(entry.get())) #this is where the trouble seems to happen
new_quote = QuoteForm(my_window)
My problem is that the on the second to last line of code (starting with 'entry.delete')
Typically you would do 'entry.delete(0,END)' but because entry is a variable the code won't run with END.
'END' is an invalid index, and 'end' just does the same as pulling the length, and so i tried to make it dynamic by making the 'end' the length of whatever is in the entry.
When i do that however, it deletes nothing [i also tried forcing it with int(len(entry.get()))]. If i manually enter an integer it will delete everything up to that integer, including if it's the same as the length of that entry, and I put breaks to confirm that i'm getting an int return and I am.
I realize i could just write a line of code to delete each entry individually, but there's a totaly of 12 and I would like to clean it up.
I'm adding the full code to be able to run below
import os
import re
import tkinter as tk
from tkinter import filedialog as fd
from tkinter import messagebox
import pandas as pd
my_window = tk.Tk()
class QuoteForm():
def __init__(self,master):
self.file_data = ''
self.master = master
self.master.rowconfigure(0, weight=1)
self.master.rowconfigure(1, weight= 1)
self.master.rowconfigure(2, weight = 1)
master.geometry('600x400')
master.resizable(False,False)
self.directory_frm = tk.Frame(master=master)
self.directory_frm.grid(row=0) #this is the frame for the directory
self.add_on_frm = tk.Frame(master=master)
self.add_on_frm.grid(row=1) #this is the frame for add-ons input
self.button_frm = tk.Frame(master=master)
self.button_frm.grid(row=2) #this is the frame for
self.load_directory_frame()
self.load_add_on_frame()
self.load_button_frame()
my_window.mainloop()
#staticmethod
def get_quote_data(filepath):
#read csv to get job infomation for pricing
try:
if filepath:
job_info = pd.read_csv(filepath,
index_col=0, #set index column
skiprows=range(4), #skip first 4 rows
usecols=['Item','Quan'])
job_info = job_info.drop(labels='Grand Total:', axis= 0)
customer_info = pd.read_csv(filepath, header=None,
skiprows= lambda x: x not in range(2), #skip any row beyond first two rows
usecols=[0,1]) #use first two columns
customer_info = {customer_info.at[0,0].replace(':',''): customer_info.at[0,1], ##formatting the data for legibility
customer_info.at[1,0].replace(':','') : customer_info.at[1,1]}
return [customer_info, job_info]
except:
messagebox.showerror("Data Invalid", "Please make sure you select a valid estimate CSV file.")
def sink_check(self):
####this is to be used at the submit buttons to confirm that there are not more sinks than cutouts
cutouts = self.um_sink_inst_ent.get()
sink_quan_list = (self.std_sink_ent.get(),self.upgrd_sink_ent.get(),self.van_sink_ent.get(),self.cust_sink_temp_ent.get())
sinks = sum(sink_quan_list)
if sinks > cutouts:
return False
###check that the sinks included does not exceed the number of sinks charged for install
return True
def validate_ent(self,input):
if not input:
return True
elif re.fullmatch(r'[0-9]',input):
return True
return False
def open_file(self):
file = fd.askopenfile(mode='r', filetypes=[('CSV Files', '*.csv')])
if file:
filepath = os.path.abspath(file.name)
file_data = self.get_quote_data(filepath)
cust_name = file_data[0]['Name']
job_addr = file_data[0]['Addr']
self.file_select_text['text'] = f"{job_addr} for {cust_name} is currently selected"
def load_directory_frame(self):
file_select_btn = tk.Button(master=self.directory_frm,text= "Select a file",command=self.open_file)
file_select_btn.grid(column=0, row=0)
self.file_select_text = tk.Label(master=self.directory_frm, text = "No File Selected")
self.file_select_text.grid(column=1, row=0)
def load_add_on_frame(self):
vcmd = (self.master.register(self.validate_ent), '%S')
#create inputs and labels for add-ons
self.trip_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd, name='trip_ent')
self.trip_ent.grid(column= 1, row = 0)
self.raw_cutouts_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.raw_cutouts_ent.grid(column= 3, row = 0)
self.radii_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.radii_ent.grid(column= 1, row = 1)
self.arcs_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.arcs_ent.grid(column= 3, row = 1)
self.splay_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.splay_ent.grid(column= 1, row = 2)
self.wtrfall_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.wtrfall_ent.grid(column= 3, row = 2)
self.um_sink_inst_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.um_sink_inst_ent.grid(column= 1, row = 3)
self.farm_sink_co_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.farm_sink_co_ent.grid(column= 3, row = 3)
self.std_sink_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.std_sink_ent.grid(column= 1, row = 4)
self.upgrd_sink_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.upgrd_sink_ent.grid(column= 3, row = 4)
self.van_sink_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.van_sink_ent.grid(column= 1, row = 5)
self.cust_sink_temp_ent = tk.Entry(master=self.add_on_frm,validate = 'key', validatecommand = vcmd)
self.cust_sink_temp_ent.grid(column= 3, row = 5)
trip_lbl = tk.Label(master=self.add_on_frm,text = "Extra Trip(s)")
trip_lbl.grid(column= 0, row = 0)
raw_cutouts_lbl = tk.Label(master=self.add_on_frm,text = "Unpolished Cutout(s)")
raw_cutouts_lbl.grid(column= 2, row = 0)
radii_lbl = tk.Label(master=self.add_on_frm,text = "Radii")
radii_lbl.grid(column= 0, row = 1)
arcs_lbl = tk.Label(master=self.add_on_frm,text = "Arc(s)")
arcs_lbl.grid(column= 2, row = 1)
splay_lbl = tk.Label(master=self.add_on_frm,text = "Splay(s)")
splay_lbl.grid(column= 0, row = 2)
wtrfall_lbl = tk.Label(master=self.add_on_frm,text = "Waterfal Leg(s)")
wtrfall_lbl.grid(column= 2, row = 2)
um_sink_inst_lbl = tk.Label(master=self.add_on_frm,text = "Install of UM Sink(s)")
um_sink_inst_lbl.grid(column= 0, row = 3)
farm_sink_co_lbl = tk.Label(master=self.add_on_frm,text = "Farm Sink C/O")
farm_sink_co_lbl.grid(column= 2, row = 3)
std_sink_lbl = tk.Label(master=self.add_on_frm,text = "Standard 18ga Sink(s)")
std_sink_lbl.grid(column= 0, row = 4)
upgrd_sink_lbl = tk.Label(master=self.add_on_frm,text = "Upgrade 18ga Sink(s)")
upgrd_sink_lbl.grid(column= 2, row = 4)
van_sink_lbl = tk.Label(master=self.add_on_frm,text = "Vanity Sink(s)")
van_sink_lbl.grid(column= 0, row = 5)
cust_sink_temp_lbl = tk.Label(master=self.add_on_frm,text = "Customer Sink Template(s)")
cust_sink_temp_lbl.grid(column= 2, row = 5)
def load_button_frame(self):
submit_btn = tk.Button(master=self.button_frm, text='Submit')
submit_btn.grid(column=0,row=0)
clear_btn = tk.Button(master=self.button_frm,text='Clear',command=self.clear_entries)
clear_btn.grid(column=1, row=0)
advanced_btn = tk.Button(master=self.button_frm,text='Advanced')
advanced_btn.grid(column=2, row=0)
def clear_entries(self):
entries = (self.trip_ent, self.raw_cutouts_ent, self.radii_ent, self.arcs_ent, self.splay_ent, #list of entry boxes on the form
self.wtrfall_ent, self.um_sink_inst_ent, self.um_sink_inst_ent, self.farm_sink_co_ent,
self.std_sink_ent, self.upgrd_sink_ent, self.van_sink_ent, self.cust_sink_temp_ent)
for entry in entries:
entry.delete(0,tk.END)
new_quote = QuoteForm(my_window)
It's all about yourvalidate_ent function. Only when it returns true then your entry text can change. While typing tkinter just sent single chars like '1','2','a'. Even when you remove with backspace, this function gets the character you are trying to remove. However when you try to clear it function gets as an input whole string like '123543123'. This is not takes place inside r'[0-9]' reguler expression and you return false so tkinter denies removing it.
There is two simple solution to fix this.
First one add another condition for longer input like:
def validate_ent(self,input):
if not input:
return True
elif re.fullmatch(r'[0-9]',input):
return True
if(len(input)>2):
return True
return False
However I do not recommend this one because if someone copy paste longer inputs then it can write letters inside entry boxes.
def validate_ent(self,input):
if not input:
return True
elif re.fullmatch(r'[0-9]*',input):
return True
return False
In here we added a asteriks to reguler expression. Now it's accepting numbers bigger then 9. Now people can also paste numbers that fits into this rule. Also removing works as expected!
from tkinter import *
main_window = Tk()
main_window.title("Register now")
frame = Frame(main_window)
frame.pack()
l_name = Label (frame, text = "Name:")
#metodo entende que é a mesma coisa que l_name.grid(row = 0, column = 0)
l_name.grid()
e_name = Entry(frame)
e_name.grid(row = 0, column =1)
l_lastname = Label(frame, text = "Last name:")
#metodo entende que é a mesma coisa que l_name.grid(row = 1, column = 0)
l_lastname.grid(row = 1)
e_lastname = Entry(frame)
e_lastname.grid(row = 1, column =1)
l_email = Label (frame, text = "E-mail:")
l_email.grid(row = 2)
e_email = Entry(frame)
e_email.grid(row = 2, column =1)
def convert_to_dict():
user = dict()
user['name'], user['last_name'], user['e-mail'] = e_name.get(), e_lastname.get(), e_email.get()
print(user)
Button(frame, text = "Send", command = convert_to_dict).grid(row=3,column =1)
Why i need this comanda user = dict()? I am telling the programn that the variable user is a dict() and thats ok, but i want to know if put user = {} would do same harm to the code
The statement user = dict() creates a new and empty python dictionary which you then fill with content on the subsequent line.
The approach looks rather non-pythonic to me. I would rather initialize and fill the dictionary in one go as TheLizzard suggested:
user = {"name": e_name.get(), "last__name": e_lastname.get(), ...}
You can achieve the same thing with the dict() method but it may result in more visual clutter in this case. However, the dict() method comes with a bunch of other handy ways to create and fill dictionaries in programmatic ways. e.g. this resource gives a nice overview: https://www.programiz.com/python-programming/dictionary
I have written this code and for some reason it refuses to return any sort of value or input for slef.REV when used in the function post(self) however it will return a value when I try and return a value in the getlen() function which is used to reurn the number of characters in the review.I dont have this problem for any other variables that I retrieve data from within this class. Below is the relevant code, any help would be appreciated. the lines where this problem occures is the first functio calld post(lines 1-5) and 4 lines up from the bottom
def post(self):
MovieID = self.MovID
REV = self.REV
AddReview(conn,cursor,Add_Review,MovieID,REV)
print(REV)
def shrek_film(self):
self.title = "Shrek"
self.MovID = 1
self.root4 = tk.Toplevel()
self.root4.title("Watch Shreck")
self.root4.geometry("1400x800")
frame_4 = tk.Frame(self.root4, bg = "black")
frame_4.pack(fill = tk.BOTH, expand = True, padx=0 , pady=0)
frame_4.grid_columnconfigure(1,weight=1)
self.Create_canvas = tk.Canvas(frame_4, width=2000, height=1080)
self.Create_canvas.place(x=-50, y=-50)
self.Create_img = PhotoImage(file="shrek-landscape.gif")
self.Create_canvas.create_image(20, 20, anchor = NW, image=self.Create_img)
play_button= tk.Button(frame_4,bg="orange",text="play", command = self.addHistory)
play_button.place(x=700,y=400)
play_button.config(font=("Ariel","30"))
def gtelen():
Review = reviewbox.get('1.0',END)
REVLEN = len(Review)
REVLENLEFT = (231-len(Review))
if REVLEN >=230:
lenbox = tk.Label(frame_4 ,text="No words left",bg="orange")
lenbox.place(x=360,y=460)
lenbox.config(font=("Ariel","15"))
else:
lenbox = tk.Label(frame_4 ,text=REVLENLEFT,bg="orange")
lenbox.place(x=360,y=460)
lenbox.config(font=("Ariel","15"))
print(Review)
Words_button = tk.Button(frame_4, bg="orange",text="check number of words remaining", command=gtelen)
Words_button.place(x=150,y=460)
Words_button.config(font=("Ariel","10"))
reviewlable=tk.Label(frame_4,text="Write a review",bg="orange")
reviewlable.place(x=10,y=460)
reviewlable.config(font=("ariel","15"))
Review_button= tk.Button(frame_4,bg="orange",text="See Reviews")#, command = self.ViewReviews)
Review_button.place(x=490,y=450)
Review_button.config(font=("Ariel","15"))
reviewbox= Text(frame_4,width=100,height=12)
reviewbox.place(x=10,y=500)
self.REV = reviewbox.get('1.0',END)
post_button = tk.Button(frame_4,bg="orange",text="Post Review", command = self.post)
post_button.place(x=830,y=650)
post_button.config(font=("Ariel","15"))
You can use Entry instead and use a StringVar
v = StringVar() # Create StringVar
reviewbox = Entry(frame_4, width = 100, height = 12, textvariable = v) # Create Entry widget
reviewbox.place(x = 10, y = 500) # Place Entry widget
self.REV = v.get() # Get contents of StringVar
The line self.REV = reviewbox.get('1.0',END) is being called about a millisecond after creating the text widget. The user will not even have seen the widget yet, much less have had time to type in it.
You can't call the get() method until after the user has had a chance to enter data, such as inside the post method.
def post(self):
MovieID = self.MovID
REV = reviewbox.get("1.0", "end")
AddReview(conn,cursor,Add_Review,MovieID,REV)
print(REV)
I have a GUI where results from a dataframe are populated into a treeview within python based on filters previously input. The user can then click on treeview and update the values to the desired number. The number of rows within the view can vary from 1 - 20+. Once the user has updated the view as desired, I have a button below "Check Allocation".
It is at this point I want to "export" the treeview into a dataframe for me to run against another table. I cant seem to simply export this as a dataframe. Is there any work around this? I only need the first and last columns (of the 8) to check the newly updated file.
Here is what I have so far.
def PrintAllocation(self):
treeview = tkk.Treeview(root)
treeview.grid(column = 1, row = 8, columnspan = 4, padx = 1, pady = 1)
cols = list(matches.columns)
treeview["columns"] = cols
for i in cols:
treeview.column(i, width = 100, anchor = "w")
treeview.heading(i,text=i,anchor='w',)
for index, row in matches.iterrows():
treeview.insert("",0,text=index,values=list(row))
def set_cell_value(event): # Double click to enter the edit state
for item in treeview.selection():
#item = I001
item_text = treeview.item(item, "values")
#print(item_text[0:2]) # Output the value of the selected row
column= treeview.identify_column(event.x)# column
print(column)
row = treeview.identify_row(event.y) #row
cn = int(str(column).replace('#',''))
rn = int(str(row).replace('I',''))
if column == '#8':
entryedit = Text(root,width=50,height = 1)
entryedit.grid(column = 2, row = 9, padx = 1, pady = 1)
else:
entryedit = Text(root,width=10,height = 1)
entryedit.grid(column = 2, row = 9, padx = 1, pady = 1)
def saveedit():
treeview.set(item, column=column, value=entryedit.get(0.0, "end"))
entryedit.destroy()
okb.destroy()
okb = ttk.Button(root, text='OK', width=4, command=saveedit)
okb.grid(column = 3, row = 9,padx = 1, pady=1)
def CheckAllocation():
children = treeview.getchildren()
for child in children:
print(treeview.set(child))
treeview.bind('<Double-1>', set_cell_value) # Double-click the left button to enter the edit
button_check = Button(root,text="Check Allocation", command = CheckAllocation)
button_check.grid(column = 2, row = 10, padx = 10, pady=10)
'''
I don't really understand your code but when I need to get a treeview to pandas I do it as follows:
First I create an empty list for each column in the treeview.
column_a_list = []
column_b_list = []
column_c_list = []
column_d_list = []
Then running through the lines of the treeview in a "for" function, I append to each column list the value of the column in each line.
for child in self.treeview.get_children():
column_a_list.append(self.treeview.item(child)["values"][0])
column_b_list.append(self.treeview.item(child)["values"][1])
column_c_list.append(self.treeview.item(child)["values"][2])
column_d_list.append(self.treeview.item(child)["values"][3])
Then I create a dictionary from all the lists, using the header as the key and lists are the values as a list.
full_treeview_data_dict = {'HEADER A': column_a_list, 'HEADER B': column_b_list, 'HEADER C': column_c_list, 'HEADER D': column_d_list,}
Then I create a dataframe from the dictionary.
treeview_df = pd.DataFrame.from_dict(full_treeview_data_dict)
Here is the full sample code in one chunk:
column_a_list = []
column_b_list = []
column_c_list = []
column_d_list = []
for child in self.treeview.get_children():
column_a_list.append(self.treeview.item(child)["values"][0])
column_b_list.append(self.treeview.item(child)["values"][1])
column_c_list.append(self.treeview.item(child)["values"][2])
column_d_list.append(self.treeview.item(child)["values"][3])
full_treeview_data_dict = {'HEADER A': column_a_list, 'HEADER B': column_b_list, 'HEADER C': column_c_list, 'HEADER D': column_d_list,}
treeview_df = pd.DataFrame.from_dict(full_treeview_data_dict)
I hope it helps.
You can also do this:
row_list = []
columns = ('name', 'school', 'c3', 'c4')
for row in treeview.get_children():
row_list.append(treeview.item(row)["values"])
treeview_df = pd.DataFrame(row_list, columns = columns)
The current answer is good, I just wanted to add my two cents here:
self.treeview_columns = [] # list of names here
# initialize empty df
# if too big you can preallocate but probably not needed
treeview_df = pd.DataFrame(None, columns=self.treeview_columns)
for row in self.treeview.get_children():
# each row will come as a list under name "values"
values = pd.DataFrame([self.treeview.item(row)["values"]],
columns=self.treeview_columns)
# print(values)
treeview_df = treeview_df.append(values)
This way you grow your df row by row using append and you reduce the number of subsets that you need to do.
Edit: I fixed the error, etc, with which this post was concerned, and now I am trying a different way to achieve advanced features I was originally going for. This time I have a display, and a display function. The buttons call the update function by inserting text into said display. I was wondering how I could use this function, and maybe textvariable =so that I can both change my buttons' text from 1, 2, 3, 4... 9, 0 to A, B, C, D...I, J. My code is below, any help would be appreciated. :)
from tkinter import *
import time
root = Tk()
root.title('Calculator')
displayb = Entry(root)
displayb.grid(row = 1, columnspan = 6)
mode = 0
i = 0
'''def update():
global mode
if mode == 0:
mode = 1
else:
mode = 0
return'''
def display(e):
global i
displayb.insert(i,e)
i += 1
txt1 = StringVar()
a = '1' if mode == 0 else 'A'
one = Button(root, text = '1', command = lambda : display(1))
txt1.set(a)
one.grid(row = 2, column = 0)
two = Button(root, text = '2', command = lambda : display(2))
two.grid(row = 2, column = 1)
three = Button(root, text = '3', command = lambda : display(3))
three.grid(row = 2, column = 2)
four = Button(root, text = '4', command = lambda : display(4))
four.grid(row = 3, column = 0)
five = Button(root, text = '5', command = lambda : display(5))
five.grid(row = 3, column = 1)
six = Button(root, text = '6', command = lambda : display(6))
six.grid(row = 3, column = 2)
seven = Button(root, text = '7', command = lambda : display(7))
seven.grid(row = 4, column = 0)
eight = Button(root, text = '8', command = lambda : display(8))
eight.grid(row = 4, column = 1)
nine = Button(root, text = '9', command = lambda : display(9))
nine.grid(row = 4, column = 2)
zero = Button(root, text = '0', command = lambda : display(0))
zero.grid(row = 5, column = 1)
'''shift = Button(root, text = 'sft', command = lambda : mode = 1 if mode == 0 else 0)
shift.grid(row = 2, column = 1)'''
root.mainloop()
When providing the command parameter a value, you need to give it a reference to your function by just giving it the function name.
shift = Button(root, text = 'sft', command = update())
Here you have () at the end of it which is calling your function. Either remove the () or use lambda like you did earlier.
Now, the reason you are getting the error you are is because mode is a global variable, defined outside your update function. So if you want to update the variable then you need to let it know that mode is global
def update():
global mode
if mode == 0:
mode = 1
else:
mode = 0
return mode
Also note that unless you will call this function somewhere else, you can't retrieve the return value on Button press.
More info on global and local scope.