Python Tkinter Return Value from Function Used in Command

How to return a value from a function when it is used as a command function? How to close a user interface when a button is pressed?

At this moment you assign it to local variable filename but if you add global filename inside function then it will assign to global variable.

It may be good to assign to global variable some default value at start - so it will exists even if you don't select file.

If you want only filename (instead of reference to opened file) then use askopenfilename instead of askopenfile

And if you click Cancel then askopenfilename should give None and it could be better to keep it as None instead of convert to string. And later you should check if filename is None to run code only if you really selected filename.

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd

filename = None # default value in global variable

# %% create a command associated to the window button
def open_file():
global filename # inform function to use global variable instead of local one

initialDir = '/'
fileTypes = (('Text files', '*.txt'), ('Csv files', '*.csv'), ('All files', '*.*'))
Title = 'Choose a file'

# use askopenfilename instead of askopenfile to get only name

filename = fd.askopenfilename(title=Title, filetypes=fileTypes)

# destroy GUI
window.destroy()

# %% create a GUI
window = tk.Tk()
window.title('Tkinter GUI Open File Dialog')
window.resizable(False, False)
window.geometry('300x150')

# %% open file button
open_button = ttk.Button(window, text='Open a File',command=open_file)
open_button.grid(column=0, row=1, sticky='w', padx=10, pady=10)

# %% run window - automatic closing after 10 seconds
window.after(10000, window.destroy)
window.mainloop()

if filename: # check if filename was really selected
print('selected:', filename)
else:
print("you didn't select filename")

If you want to keep all in function then it may need to use global in all functions

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd

def get_filename():
global filename

filename = None # default value in global variable

# %% create a command associated to the window button
def open_file():
global filename # inform function to use global variable instead of local one

initialDir = '/'
fileTypes = (('Text files', '*.txt'), ('Csv files', '*.csv'), ('All files', '*.*'))
Title = 'Choose a file'

# use askopenfilename instead of askopenfile to get only name

filename = fd.askopenfilename(title=Title, filetypes=fileTypes)

window.destroy()

# %% create a GUI
window = tk.Tk()
window.title('Tkinter GUI Open File Dialog')
window.resizable(False, False)
window.geometry('300x150')

# %% open file button
open_button = ttk.Button(window, text='Open a File',command=open_file)
open_button.grid(column=0, row=1, sticky='w', padx=10, pady=10)

# %% run window - automatic closing after 10 seconds
window.after(10000, window.destroy)
window.mainloop()

return filename

# --- main ---

filename = get_filename()

if filename: # check if filename was really selected
print('selected:', filename)
else:
print("you didn't select filename")

Popular method is also to create main window, hide it and directly run askopenfilename without button

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd

def get_filename():
global filename

filename = None # default value in global variable

# %% create a command associated to the window button
def open_file():
global filename # inform function to use global variable instead of local one

initialDir = '/'
fileTypes = (('Text files', '*.txt'), ('Csv files', '*.csv'), ('All files', '*.*'))
Title = 'Choose a file'

# use askopenfilename instead of askopenfile to get only name

filename = fd.askopenfilename(title=Title, filetypes=fileTypes)

window.destroy()

# %% create a GUI
window = tk.Tk()

#window.iconify() # minimize to icon
window.withdraw() # hide it

window.after(10000, window.destroy)

open_file()

window.mainloop()

return filename

# --- main ---

filename = get_filename()

if filename: # check if filename was really selected
print('selected:', filename)
else:
print("you didn't select filename")

EDIT:

Reduced version which can run with parameters

import tkinter as tk
from tkinter import filedialog

def get_filename(title='Choose a file', directory='/', file_types = (('Text files', '*.txt'), ('Csv files', '*.csv'), ('All files', '*.*'))):

window = tk.Tk()

#window.iconify() # minimize to icon
window.withdraw() # hide it

filename = filedialog.askopenfilename(title=title, initialdir=directory, filetypes=file_types)

window.destroy()

return filename

# --- main ---

filename = get_filename(directory='/')

if filename: # check if filename was really selected
print('selected:', filename)
else:
print("you didn't select filename")

Python tkinter return value from function call in a button

You can't get the return value when the function is called as the result of an event (button press, etc). The code that calls the function (inside of mainloop) ignores the return value.

If other parts of the code need the value, you need to store it as an attribute of an object or as a global variable.

The other problem in your revised code is that you're calling the print statement before the user has a chance to click the button, so the value will be blank even if you use a global variable. You have to wait to use the value until after you've clicked the button and selected the file.

For example, create a function that prints the value, and then attach that function to a button. Click your button to choose the file, and then click the button to display the data. As long as you save the value in the first function into a global variable, it will be available in the second function.

How to return a value of a function given as a command in tkinter

Most times, callbacks drop the return value (all tkinter callbacks do), if any. This is because is is a bit awkward to manage return values using the observer pattern which is most often used for callbacks.

There are two main ways to transfer a value from the CB to where it is needed:

One is to use a container object - that may be a simple python list or dict, or a more elaborate data class that is accessible by the callback, and mutated to assign the proper values to it.

here is a very simple example using a dictionary:

import tkinter as tk 

def print_info():
print(f'from print_info: {information_transfer}')

def submit():
information_transfer['name'] = name_entry.get()
print(f'after input: {information_transfer}')

root=tk.Tk()
root.geometry("600x400")

name_label = tk.Label(root, text = 'Username', font=('calibre', 10, 'bold'))
name_entry = tk.Entry(root, font=('calibre', 10, 'normal'))

sub_btn = tk.Button(root,text='Submit', command=submit)
info_btn = tk.Button(root,text='print info', command=print_info)


name_label.grid(row=0, column=0)
name_entry.grid(row=0, column=1)
sub_btn.grid(row=2, column=1)
info_btn.grid(row=3, column=1)

information_transfer = {'name': None, 'other': None}
print(f'before input: {information_transfer}')

root.mainloop()

Another is the OOP approach described by @DavidGildour in another answer.

How to return a value from one function to other using a button in tkinter

From what I have understood, you are looking for something like this

from tkinter import *

def verify():
global id_
id_=id_entry.get()
if id_==ID and pass_entry.get()==PASS:
print('Success')
else:
print('Invalid')

root=Tk()

id_entry=Entry(root)
id_entry.pack()
pass_entry=Entry(root)
pass_entry.pack()
ent_button=Button(root,text='Enter',command=verify)
ent_button.pack()

ID='example'
PASS='password'

root.mainloop()

Using retrun will be pointless in this case since callbacks can't really return anything. It has been explained well in this post.

UPDATE

If you declare the variable as global, you can access it from anywhere in the code

def verify():
global id_
id_=id_entry.get()
if id_==ID and pass_entry.get()==PASS:
success()
else:
print('Invalid')

def success():
print(id_,'logged in successfully')

If you don't want to use global then you can pass it to the target function

def verify():
id_=id_entry.get()
if id_==ID and pass_entry.get()==PASS:
success(id_)
else:
print('Invalid')

def success(id_):
print(id_,'logged in successfully')

How to pass a variable to a function via a Button?

The common way to do it when working with tkinter is to use lambda functions. The tricky part is getting the return value back because the return value of command= functions is ignored. Folks often use global variables to workaround that, but that tactic won't work if the function is defined in another module. Fortunately tkinter has several type-specific container Variable subclasses that can be passed as arguments to such functions that will permit values to be retrieved from them.

Here's runnable code showing one way of using them to do this:

script.py

from tkinter import filedialog

def load_file(input_path):
file_name = filedialog.askopenfilename(filetypes=[('EXE files', '*.exe')])
input_path.set(file_name)

def simulation(input_path, input_par): # this works fine
# the function runs an EXE file ang gives me two lists lst1 and lst2
return lst1, lst2

main.py

from script import load_file, simulation
import tkinter as tk

root = tk.Tk()
frame_file = tk.Frame(root)
frame_file.pack()

input_path = tk.StringVar()
input_var = 42
button = tk.Button(frame_file, text='Open file', command=lambda: load_file(input_path))
button.grid(row=1, column=1)
button2 = tk.Button(frame_file, text='Run simulation',
command=lambda: simulation(input_path.get(), input_var))
button2.grid(row=1, column=2)

root.mainloop()

Update

Here's a slightly more complicated but much more flexible version, that would work with many more types of return values, including user-defined classes (not just the four tkinter supports — bool, float, int, string). Supporting more types of returned data automatically, also means that called functions don't need to be modified.

It does this by defining a CallBack class whose instances "wrap" the functions to be called and save their return values in a tkinter StringVar widget so they can be retrieved later as needed by calling a method get_result() that instances of the class have. To work with more datatypes it uses Python's built-in pickle module to temporarily store the data.

script2.py

from tkinter import filedialog

def load_file():
file_name = filedialog.askopenfilename(filetypes=[('EXE files', '*.exe')])
return file_name

def simulation(input_path, input_par): # this works fine
# the function runs an EXE file and gives me two lists lst1 and lst2
print(f'simulation({input_path=}, {input_par=}) called')
list1, list2 = ['param', input_par], list('spam')
return list1, list2

main2.py

from script2 import load_file, simulation
import pickle
import tkinter as tk
from tkinter import messagebox


class CallBack:
def __init__(self, func):
self.func = func
self.var = tk.StringVar()
def get_result(self):
pickled_result = self.var.get()
return pickle.loads(pickled_result.encode('latin1')) # Unpickle and return it.
def __call__(self):
res = self.func()
pickled_result = pickle.dumps(res).decode('latin1') # Pickle result.
self.var.set(pickled_result) # And save it in tkinter StringVar.


root = tk.Tk()
frame_file = tk.Frame(root)
frame_file.pack()

input_par = 42
callback1 = CallBack(load_file) # Wrap function from other module.

button = tk.Button(frame_file, text='Open file', command=callback1)
button.grid(row=1, column=1)

def run_sim():
""" Run simulation function and display value returned. """
# Wrap other function in other module to allow results to be retrieved.
callback2 = CallBack(lambda: simulation(callback1.get_result(), input_par))
callback2() # Call the function and save its return value.
results = callback2.get_result() # Get function's return value.
messagebox.showinfo(title='Simulation Results', message=str(results))

button2 = tk.Button(frame_file, text='Run simulation', command=run_sim)
button2.grid(row=1, column=2)

root.mainloop()

How can I return a value from an function that is executed from tkinter button?

You keep making your code more and more complicated and I still don't understand what you want.

So, I made this minimal program to show that when its the case that a new button appears, and the user presses that button, that a value is calculated and used in a call to an arbitrary function from another module:
file1.py

import tkinter as tk

def func(var):
var['file'] = 42
return var['file']

def do_something(var, f):
# root = var['root']
mainframe = var['mainframe']
bt = tk.Button(mainframe, text='submit', command=lambda: f(func(var)))
# When the user presses submit, the function f is called with the 'return' value
bt.place(relx=0.5, rely=0.5)

file2.py

import tkinter as tk
root = tk.Tk()
mainframe = tk.Frame(root, relief='raised')
mainframe.place(relx=0.1, rely=0.1, width='1100', height='380')
val = {}
val['root'] = root
val['mainframe'] = mainframe

def f(value):
print(value) # Just prints the value, but could do anything!

def chk():
import file1
file1.do_something(val, f) # f passed from here as a callback
# Note - no return value

btn = tk.Button(mainframe, text='Check', command = chk)
btn.place(relx=0.5, rely=0.8)
root.mainloop()

Note: the frame starts small and you need to drag it to resize it bigger to see the buttons.

Returning values in lambda function while using Tkinter button

Lambdas are just a shorthand notation, functionally not much different than an ordinary function defined using def, other than allowing only a single expression

x = lambda a, b: a + b

is really equivalent to

def x(a, b):
return a + b

In the same way, what you tried to do would be no different than doing:

def x():
a, b = read_input_data()
run(a, b)
start_button = tk.Button(text = 'Start', command = x, bg = 'firebrick', fg = 'white', font = ('helvetica', 9, 'bold'))


Related Topics



Leave a reply



Submit