How to pass parameters to functions in python - python

I have this simple program that I wrote so I could better understand the 'return' function and how to pass a value from one function to another. All this program does is to pass the value of buttontwo=2 to the function button_one_function,so if button two is pressed first then button one does nothing.I thought that I could do this without using a global statement - is there a way of writing the code below without using global? I have tried doing this by putting the value of buttontwo in to the button_one_function parentheses but this didnt work. Thanks for any tips
from tkinter import *
my_window = Tk()
my_frame = Frame(my_window, height=500, width=500, bd='4')
my_frame.grid(row=0, column=0)
def button_one_function():
if button_two == 2:
print('do nothing')
else:
label_one = Label(my_frame, text='label one')
label_one.grid(row=1, column=0, sticky='n')
def button_two_function():
global button_two
button_two = 2
label_two = Label(my_frame, text='label two')
label_two.grid(row=1, column=1, sticky='n')
return button_two
button_one = Button(my_frame, text='button1', command=button_one_function)
button_one.grid(row=0, column=0)
button_two = Button(my_frame, text='button2', command=button_two_function)
button_two.grid(row=0, column=1)
my_window.mainloop()

If I've understood corectly, you are interested in sth. like this:
from tkinter import *
root = Tk()
def click(a):
print(a)
Button(root, text='1', command=lambda: click('1')).pack()
Button(root, text='2', command=lambda: click('2')).pack()
root.mainloop()
What is happening is I'm not passing a full click function to a button, but a so called lambda function, which is essentially a one-line function. Example: if I did p = lambda: print('Hi') then each time I do p() I would see a little Hi pop up. Also, if I did k = lambda a,b: a*b then k(4,5) would return "20". More info about lambdas here.
Hope that's helpful!

def function(a,b):
print("a is :",a)
print("b is :",b)
function(10,20)

You can definitely do this without globals. You could extend the tk.button class to hold a variable like self.status = pressed.
There are a few ways you can go about this with classes. You could create either one or two classes. Maybe even have child classes for each button.
But you can just dump both your functions in one class and pass self as its first argument.
Whenever I feel the need for a global variable, I usually make a class.

Related

How can i update a variable after pressing buttons on tkinter?

There's an example of my code below.
I am trying to make a GUI with tkinter, in python. I want an app that has a variable, let's say var_list, that is introduced into a function as a parameter.I run this function using a button with command=lambda: analize(var_list)
I want to be able to modify the variable by pressing buttons (buttons to add strings to the list). And I have a function for that aswell:
def button_clicked(e):
if ((e["text"]).lower()) in var_list:
var_list.pop(var_list.index((e["text"]).lower())) #this adds a string to the list
else:
var_list.append((e["text"]).lower()) #this deletes the string from the list if it was already there
The function works, I tried printing the var_list and it gets updated everytime I press a button.
The problem is that I have to create the var_list as an empty list before, and when I run the function analize(var_list), it uses the empty list instead of the updated one.
Any idea on how to update the global var everytime I add/delete something from the list?
from tkinter import *
from PIL import ImageTk
def show_frame(frame):
frame.tkraise()
def button_clicked(e):
if ((e["text"]).lower()) in var_list:
var_list.pop(var_list.index((e["text"]).lower()))
else:
var_list.append((e["text"]).lower())
def analize(x):
#does stuff with the list
window = Tk()
frame1 = Frame(window)
frame2 = Frame(window)
canvas1 = Canvas(frame1,width = 1280, height = 720)
canvas1.pack(expand=YES, fill=BOTH)
image = ImageTk.PhotoImage(file="background.png")
var_list = []
button1 = Button(canvas1, text="Analize",font=("Arial"),justify=CENTER, width=10, command=lambda: [show_frame(frame2),analize(x=var_list)])
button1.place(x=(1280/2)-42, y=400)
button2 = Button(canvas1, text="String1",font=("Arial"),justify=CENTER, width=10, command=lambda: button_clicked(button2))
button2.place(x=(1280/2)-42, y=450)
button3 = Button(canvas1, text="String2",font=("Arial"),justify=CENTER, width=10, command=lambda: button_clicked(button3))
button3.place(x=(1280/2)-42, y=500)
Thank you
you can make a global variable eg:-global var
Now you can access it within other defination to manipulate the variable like this
global var
var = 0 # if you want to set a default value to the variable before calling the
function
def change_var():
global var
var = 1
USE OF GLOBAL
using global is highly recommended and is quite necessary if you are working with functions that contain or has the need to manipulate the variable
If global is not given inside the function, the variable will live inside the function and it cannot be accessed outside the function.
Hope this answer was helpful, btw, I am not sure if this the answer you are looking for as your question is not clear, maybe give a situation where you might think it might be necessary to change or update the variable
Sorry, I did not understand you but I guess this example will help you -
import tkinter as tk
root = tk.Tk()
var_list = []
def change_val(n):
var_list.append(n)
label1.config(text=var_list)
def remove():
try:
var_list.pop()
label1.config(text=var_list)
except:
pass
label1 = tk.Label(root,text=var_list)
label1.pack()
button1 = tk.Button(root,text='1',command=lambda:change_val(1))
button1.pack()
button2 = tk.Button(root,text='2',command=lambda:change_val(2))
button2.pack()
button3 = tk.Button(root,text='3',command=lambda:change_val(3))
button3.pack()
button4 = tk.Button(root,text='Pop Element',command=remove)
button4.pack()
root.mainloop()

Python Tkinter Entry.get() gives previous input

I am working on a relatively large project, but have managed to recreate my problem in just a few lines:
import tkinter as tk
root = tk.Tk()
root.geometry('200x200')
def doStuff():
pass
sv = tk.StringVar()
def callback():
print(E.get())
doStuff()
return True
E = tk.Entry(root, bg="white", fg="black", width=24, textvariable=sv, validate="key",validatecommand=callback)
E.grid(row=0, column=0, padx=30, pady=5, sticky=tk.E)
root.mainloop()
The desired output would be that every time the user changes the entry in this Entrywidget, a function is called.
This works just fine, but using E.get() returns the 'previous' entry, for example:
-entry is 'boo'
-E.get() is 'bo'
Python seems to run Callback() before the Entry Widget has been changed.
Validation by design happens before the text has been inserted or deleted. For validation to work, it must be able to prevent the data from being changed.
If you aren't doing validation, but instead just want to call a function when the value changes, the best way to do that is to put a trace on the associated variable.
def callback(*args):
print(E.get())
doStuff()
return True
sv = tk.StringVar()
sv.trace_add("write", callback)

functools.partial doesn't pass Tkinter argument

I am creating an application that requires input from the user. The code has an entry widget, and there is a button that calls a function with the input from one of the entry widgets as an argument. However, whenever I print the argument (the content of the entry widget) I get an empty list instead of what I entered.
#!/usr/bin/env python
import grammar
import shelve
from functools import partial
from Tkinter import *
def call_response(text):
print text
grammar.analyze.analyze(text)
class MainWindow(Tk):
def new_object(self):
main_frame = Frame(self)
bottom_frame = Frame(self)
main_frame.pack()
bottom_frame.pack(side=BOTTOM)
output = Text(main_frame)
output.pack()
input_entry = Entry(bottom_frame, width=50)
input_entry.grid(row=1, column=1)
send_input_button = Button(bottom_frame, text='Chat!', command=partial(
call_response, input_entry.get().split()))
send_input_button.grid(row=1, column=2)
mainloop()
root = MainWindow()
root.new_object()
Does anyone know what could be causing this to happen, or what might be wrong with my code?
You fetch the entry once, when creating the button; a partial() does not execute the expressions you used to create the arguments when it itself is called; the input_entry.get().split() expression is executed first, and the result is passed to the partial() object being created.
Use a lambda here to have the entry.get() executed when the button is clicked:
send_input_button = Button(bottom_frame, text='Chat!', command=lambda:
call_response(input_entry.get().split()))
call_response should be part of the class IMHO which eliminates the problems.
self.input_entry = Entry(bottom_frame, width=50)
send_input_button = Button(bottom_frame, text='Chat!', command=self.call_response)
def call_response(self):
text=self.input_entry.get().split()
grammar.analyze.analyze(text)
or just change it to
def call_response(text_fn):
text = text_fn().split()
print text
grammar.analyze.analyze(text)
....
send_input_button = Button(bottom_frame, text='Chat!', command=partial(
call_response, input_entry.get))
as an alternative if you really want to avoid lambda ... but lambda is fine #AlexMarteli has valid criticism of it ... but for something simple like this they work fine

How would one bind two different button click events to the same label, namely "<Button-1>" and "<Double-Button1>" to same label?

So, i know there has got to be a way to do this iv'e literally been trying all day to figure a solution and this is the closest i've come to getting it. The code works but the flaw is that it never reaches the else if statement because whatever button click is first in the statement will always be True hence it will never reach else if. Is their some way to combine the first two statements of my code into one because i believe that would solve it? This is using the tkinter GUI.
self.label1.bind("<Double-Button-1>",self._two_click)
self.label1.bind("<Button-1>", self._two_click)
def _two_click(self,event):
if self.label1.bind("<Button-1>"):
self.label1.configure(self.my_timeBB())
elif self.label1.bind("<Double-Button-1>"):
self.label1.configure(text="Blue")
I use extra argument in function to recognize click.
BTW: you can always bind <Button-1> and <Double-Button1> to one widget but with different functions ?
import Tkinter as tk
def test(event, extra=None):
print extra
master = tk.Tk()
b1 = tk.Button(master, text="QUIT", command=master.destroy, width=20, heigh=5)
b1.pack()
b2 = tk.Label(master, text="OK", width=20, heigh=5)
b2.pack()
b2.bind('<Double-Button-1>', lambda event:test(event,101))
b2.bind('<Button-1>', lambda event:test(event,1))
b2.bind('<Button-2>', lambda event:test(event,2))
b2.bind('<Button-3>', lambda event:test(event,3))
master.mainloop()
But I see one (big) problem - when I try to make double-click with my mouse I always get two text - first for single-click and second for double-click.
The only solution can be measure time between click and decide whether choose single or double click. But probably it will need to use after() too.
EDIT:
run only single or double clik
import Tkinter as tk
#----------------------------------------------------------------------
single = False
def test(event, extra=None):
global single
#print 'event-num:', event.num
#print 'extra:', extra
if extra == 1:
single = True
master.after(200, single_click)
elif extra == 101:
single = False
click('double click')
def single_click():
global single
if single:
single = False
click('single click')
def click(msg):
print 'It was', msg
#----------------------------------------------------------------------
master = tk.Tk()
b1 = tk.Button(master, text="QUIT", command=master.destroy, width=20, heigh=5)
b1.pack()
b2 = tk.Label(master, text="OK", width=20, heigh=5)
b2.pack()
b2.bind('<Double-Button-1>', lambda event:test(event,101))
b2.bind('<Button-1>', lambda event:test(event,1))
master.mainloop()

Have multiple commands when button is pressed

I want to run multiple functions when I click a button. For example I want my button to look like
self.testButton = Button(self, text = "test",
command = func1(), command = func2())
when I execute this statement I get an error because I cannot allocate something to an argument twice. How can I make command execute multiple functions.
You can simply use lambda like this:
self.testButton = Button(self, text=" test", command=lambda:[funct1(),funct2()])
You could create a generic function for combining functions, it might look something like this:
def combine_funcs(*funcs):
def combined_func(*args, **kwargs):
for f in funcs:
f(*args, **kwargs)
return combined_func
Then you could create your button like this:
self.testButton = Button(self, text = "test",
command = combine_funcs(func1, func2))
def func1(evt=None):
do_something1()
do_something2()
...
self.testButton = Button(self, text = "test",
command = func1)
maybe?
I guess maybe you could do something like
self.testButton = Button(self, text = "test",
command = lambda x:func1() & func2())
but that is really gross ...
You can use the lambda for this:
self.testButton = Button(self, text = "test", lambda: [f() for f in [func1, funct2]])
Button(self, text="text", command=func_1()and func_2)
I think the best way to run multiple functions by use lambda.
here an example:
button1 = Button(window,text="Run", command = lambda:[fun1(),fun2(),fun3()])
I've found also this, which works for me. In a situation like...
b1 = Button(master, text='FirstC', command=firstCommand)
b1.pack(side=LEFT, padx=5, pady=15)
b2 = Button(master, text='SecondC', command=secondCommand)
b2.pack(side=LEFT, padx=5, pady=10)
master.mainloop()
... you can do...
b1 = Button(master, command=firstCommand)
b1 = Button(master, text='SecondC', command=secondCommand)
b1.pack(side=LEFT, padx=5, pady=15)
master.mainloop()
What I did was just renaming the second variable b2 the same as the first b1 and deleting, in the solution, the first button text (so only the second is visible and will act as a single one).
I also tried the function solution but for an obscure reason it don't work for me.
this is a short example : while pressing the next button it will execute 2 functions in 1 command option
from tkinter import *
window=Tk()
v=StringVar()
def create_window():
next_window=Tk()
next_window.mainloop()
def To_the_nextwindow():
v.set("next window")
create_window()
label=Label(window,textvariable=v)
NextButton=Button(window,text="Next",command=To_the_nextwindow)
label.pack()
NextButton.pack()
window.mainloop()
check this out, I have tried this method as,I was facing the same problem. This worked for me.
def all():
func1():
opeartion
funct2():
opeartion
for i in range(1):
func1()
func2()
self.testButton = Button(self, text = "test", command = all)
I was looking for different solution. One button doing two functions on two different clicks. Example START button - after click changes text to STOP and starts function. After second click stop function and changes text to START again.
Tried this and it works. I do not know if it is ok or elegant or does not break any python rules (I am a lousy programmer :-) but it works.
from tkinter import *
root=Tk()
def start_proc():
print('Start')
myButton.configure(text='STOP',command=stop_proc)
def stop_proc():
print('stop')
myButton.configure(text='START',command=start_proc)
myButton=Button(root,text='START' ,command=start_proc)
myButton.pack(pady=20)
root.mainloop()

Categories