I'm using Tkinter and matplotlib to create a small sorting array project. My Tkinter GUI has a listbox that I want to use to choose the desired sorting algorithm. I'm using matplotlib's FuncAnimation() to iterate repeatedly through my chosen sorting function and animate them. FuncAnimation() takes the name of the function that you decide to use as an argument. I wanted to assign the argument a variable that I could reassign to the name of any function that I wanted to use.
I believe the problem is that listbox.get(ANCHOR) gives me a string and FuncAnimation wants some sort of function object. I've looked into possible ways to a string into a function object or callable function but I'm either not understanding or not finding anything.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from Tkinter import *
import Tkinter as tk
#example of one of the sorting functions.
def Quick_Sort(i):
global arr # I don't know why I have to use a globalized variable. Wouldn't let me pass it arr
n=len(arr)
less_than=[]
equal_to=[]
greater_than=[]
if i in range(1,n-1):
if arr[i-1]>arr[i] or arr[i]>arr[i+1]:
#break into sub arrays
pivot=arr[i]
del arr[i]
for y in range(0,n-1):
plt.clf()
if arr[y]< pivot:
less_than.append(arr[y])
if arr[y]==pivot:
equal_to.append(arr[y])
elif arr[y]>pivot:
greater_than.append(arr[y])
del arr[:]
less_than.append(pivot)
arr=less_than + equal_to + greater_than
del less_than[:], greater_than[:], equal_to[:]
plt.bar(arr_ind,arr)
fig.canvas.draw()
elif i>n-1:
print("'i' is out of range. Exiting program.")
print ("Final array is ", arr)
sys.exit()
return i
def choose_and_run():
choice=listbox.get(ANCHOR)
fig=plt.figure()
ax=fig.add_axes([0,0,1,1])
fill_array(arr,arr_ind,arr_size)
fig.canvas.draw()
anim=animation.FuncAnimation(fig,choice,interval=50)
plt.show()
#---TKINTER STUFF-------
window=tk.Tk()
window.title("Sorting Arrays")
window.geometry("150x00")
listbox=tk.Listbox(window)
# Algorithm Options
listbox.insert(1,"Bubble_Sort")
listbox.insert(2,"Insertion_Sort")
listbox.insert(3,"Quick_Sort")
listbox.insert(4,"Selection_Sort")
listbox.pack()
# Select and run button
button1=tk.Button(window,text="Get and Go",command=choose_and_run).pack()
window.mainloop()
Hope this was enough information. Any help is appreciated.
You wouldn't normally convert a string to a function name directly, even if with Python just about anything's possible. However, functions are just objects so just use a dict:
chosenfunc = {"Bubble_Sort":Bubble_Sort, "Insertion_Sort":Insertion_Sort,
"Quick_Sort":Quick_Sort, "Selection_Sort":Selection_Sort}
selection=listbox.get(ANCHOR)
choice = chosenfunc[selection]
You could use locals() or globals() to get access to a dictionary with the local or global symbol table, respectively. You can use this if your functions are declared in the same module.
If not, you can use getattr on a module object:
import module
dynamically_imported_method = getattr(module, 'bar')
dynamically_import_method()
Related
I found this answer (https://stackoverflow.com/a/59905309/7462275) to display a progress bar very very simple to use. I would like to use this simple solution for functions that take many arguments.
Following, the above mentioned answer, I write this code that works :
from tqdm.contrib.concurrent import process_map
import time
def _foo(my_tuple):
my_number1, my_number2 = my_tuple
square = my_number1 * my_number2
time.sleep(1)
return square
r = process_map(_foo, [(i,j) for i,j in zip(range(0,30),range(100,130))],max_workers=mp.cpu_count())
But I wonder, if it is the correct solution (using a tuple to assign function variable) to do that. Thanks for answer
This is what I want to do drive a computational experiment
import pandas
def foo(a = 1, b = 2):
print("In foo with %s" % str(locals()))
return a + b
def expand_grid(dictionary):
from itertools import product
return pandas.DataFrame([row for row in product(*dictionary.values())], columns=dictionary.keys())
experiment = {"a":[1,2], "b":[10,12]}
grid = expand_grid(experiment)
for g in grid.itertuples(index=False):
foo(**g._asdict())
That works, but the issue is it has a private _asdict() call. There is a standard in Python not to use "_" calls in external code, so here is my question:
Can you do the above without the _asdict() and if so how?
Also note while foo(a =g.a, b=g.b) is a solution, the actual code is a heavily parameterized call, and for my own knowledge, I was just trying to figure out how to treat it as kwargs without the _ call if it was possible.
Thanks for the hint.
I wrote the following code in python and it works fine until I try to make it a function, can anyone help?
import random
def club():
members=int(input("members"))
print (random.randint(1, members))
You have to use 4 spaces ( or tab) per indentation level. And call the function of course.
import random
def club():
members=int(input("members"))
print (random.randint(1, members))
club()
members is local to the club function, thus not visible to your print call
members is considered to be a local variable within the club function. If you want to use it, you can return it. You can do something like this:
import random
def club():
members = int(input("Members: "))
return members
print(random.randint(1, club()))
Also, since Python is an Object-Oriented Language, you can set the value that club returns to a variable (or anything for that matter):
a = club() # Then a might be 5
b = random.randint(1, a)
And then use that variable elsewhere:
print(b)
In Matlab, nargout is a variable that tells you if the output is assigned, so
x = f(2);
and
f(2);
can behave differently.
Is it possible to do similar in Python?
I have a function that plots to screen and returns a matplotlib figure object. I want that if output is assigned to a variable then do not plot to screen.
Here's a way you can do it (not that i'd advise it), but it has many cases where it won't work - to make it work you'd essentially need to parse the python code in the line and see what it is doing, which would be possible down to some level, but there are likely always going to be ways to get around it.
import inspect, re
def func(x, noCheck=False):
if not noCheck:
#Get the line the function was called on.
_, _, _, _, lines, _ = inspect.getouterframes(inspect.currentframe())[1]
#Now we need to search through `line` to see how the function is called.
line = lines[0].split("#")[0] #Get rid of any comments at the end of the line.
match = re.search(r"[a-zA-Z0-9]+ *= *func\(.*\)", line) #Search for instances of `func` being called after an equals sign
try:
variable, functioncall = match.group(0).split("=")
print variable, "=", functioncall, "=", eval(functioncall.strip()[:-1] + ", noCheck=True)")
except:
pass #print "not assigned to a variable"
#Actually make the function do something
return 3*x**2 + 2*x + 1
func(1) # x = func(1)
x = func(1)
Another way to do it would be to examine all of the set local variables when you call the code, and check if any of them have been set to the result of your function, then use that information to help parse the python.
Or you could look at object IDs, and try and do things that way, but that's not goign to be straightforward, as not all objects work the same way (i.e. do a=10 and c=10 and then have a look at each object's IDs, they're the same ven though a and c are seperate. The same happens with short strings too)
If you can think up a way to do this that would work universally, i'd be interested to know how you do it, I'd pressume it will need to be done by digging around in inspect though, rather than through parsing the actual code.
Others have mentioned that this is complex, but can be done with inspect. You may want a simple approach by having a separate function to plot it, or pass an extra variable that says to plot.
def create_plot(x):
return plot
def display(plot):
# show the plot
x = create_plot(2)
display(x)
Plot variable
def plot(x, show=False)
# create the plot
if show:
# show the plot
plot(2, True)
x = plot(2)
It is probably not worth the time and easier to just create the two functions.
Personally, I think this is ugly, nasty, and I do not believe that functionality should be based on something catching the return value. However, I was curious, and I found a way. You could probably turn this into a decorator if you want to use it in the future, but I still suggest that you use two separate methods instead of checking for an output.
import inspect
def f(val):
has_output = False
frame = inspect.currentframe()
name = frame.f_code.co_name
outer = inspect.getouterframes(frame)[1] # may want to loop through available frames.
for i in range(len(outer)):
item = str(outer[i]).replace(" ", "")
check = "="+name+"("
if check in item and "="+check not in item: # also check assignment vs equality
# Your method has an output
has_output = True
break
if has_output:
print("Something catches the output")
return val*val
# end f
In many cases this will not work either. You will have to make really good regex for the check if you always want it to work.
import my_lib
x = my_lib.f(2)
I am trying to print out the button number when i click each buttons that are created by for loop. The following is what i have tried.
import Tkinter as tk
root=tk.Tk()
def myfunction(a):
print a
for i in range(10):
tk.Button(root,text='button'+str(i),command=lambda:myfunction(i)).place(x=10,y=(10+(25*i)))
root.mainloop()
But instead of printing out each button number, it actually giving me the last button number everytime. Is there anything i can do so that when i click button 1, it will print 1,2 for 2 ,and so on?
Easy fix is to initialize the lambda function with the current value of i each time the lambda function is created. This can be done using Python default values for another dummy variable j.
command = lambda j=i: myfunction(j)
Blender's answer is a clever solution but in case you get thrown off by the function abstraction, here is another possible way to do it. It really just creates a mapping, saved in buttons, from Button widgets to their proper numbers.
import Tkinter as tk
root = tk.Tk()
def myfunction(event):
print buttons[event.widget]
buttons = {}
for i in range(10):
b = tk.Button(root, text='button' + str(i))
buttons[b] = i # save button, index as key-value pair
b.bind("<Button-1>", myfunction)
b.place(x=10,y=(10+(25*i)))
root.mainloop()
The problem is that the Button command in tk ignores any parameters so insomething like mycommand(data) the data is ignored.
I use buttons quite a bit and decided to subclass a tk Button to include data. I called it a DataButton and added a data member ( in this case an index number). That way when clicked it passes back its data. ( index number) Now I use the DataButton whenever I want it to hold information like an index or even a message.
It's because i in your anonymous function refers to the counter variable, not to the value:
from __future__ import print_function
x = [lambda: print(i) for i in range(10)]
for f in x:
f()
This produces an output of 10 consecutive 9s.
To get around this, you'd have to use two lambdas and shadow i with a second function (which creates your first function):
from __future__ import print_function
x = [(lambda i: (lambda: print(i)))(i) for i in range(10)]
for f in x:
f()
Although at that point, you'd be better off just making a named function:
def my_command(i):
def inner_function():
return my_function(i)
return inner_function
And using it like this:
tk.Button(root, text='button' + str(i), command=my_command(i))