I'm trying to run some python code on a raspberry-pi in order to simulate different sensors. When calling a function twice, I'm being told that I'm providing too many arguments. I will have approximately 20 different functions all calling this outputPWM function, but I have just shown two functions as an example.
I've only started using python this week, so I'm not entirely sure how to fix this. If I can clear the outputPWM function of arguments in some way before the next call or I may have some fundamental flaw in my code
def outputPWM(n1,i):
num1 = (n1.get())
result = int(num1)
dutycycle = result
print(List[25])
List[i].start(0)
List[i].ChangeDutyCycle(dutycycle)
print("Duty cycle is %d" % dutycycle)
print("output is on port %d" %i)
def control():
ControlReturn = tk.StringVar()
ControlLabel = tk.Label(master, text="Control Return Air Temperature (Degrees Celcius)").grid(row=0, column=0)
ControlResult = tk.Label(master)
ControlEntry = tk.Entry(master, textvariable=ControlReturn).grid(row=0,column=2)
global outputPWM
outputPWM=partial(outputPWM,ControlReturn,20)
buttonCal = tk.Button(master, text="Enter", command=outputPWM).grid(row=0, column=3)
def display():
DisplayReturn = tk.StringVar()
DisplayLabel = tk.Label(master,text="Display Return Air Temperature (Degrees Celcius)").grid(row=1, column=0)
DisplayEntry = tk.Entry(master, textvariable=DisplayReturn).grid(row=1,column=2)
global outputPWM
outputPWM= partial(outputPWM,DisplayReturn,25)
buttonCal = tk.Button(master, text="Enter", command=outputPWM).grid(row=1, column=3)
display()
control()
The error I receive is :
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1550, in __call__
return self.func(*args)
TypeError: outputPWM() takes exactly 2 arguments (4 given)
Wherever you are creating a partial function, give it a name other than the global outputPWM:
global outputPWM
outputPWM_ = partial(outputPWM, ControlReturn, 20)
buttonCal = tk.Button(master, text="Enter", command=outputPWM_).grid(row=0, column=3)
This code is executed twice, first time in display() and second time in control() (with slightly different parameters, but that does not matter)
global outputPWM
outputPWM=partial(outputPWM,ControlReturn,20)
It replaces the original function with derived partial one which adds 2 args. The second round replaces that partial function with another partial one derived from the first partial function. The resulting function adds 2+2 args in two steps. That is the source of "takes 2 args (4 given)" error.
The solution is not reusing the same name (see https://en.wikipedia.org/wiki/Variable_shadowing) and not using global unless it is not really necessary.
Related
I'm new to python programming and I need assistance in calling a function inside another function. My first function is:
def on_enter_save(event):
btnSave['background'] = 'gray'
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_save(event):
btnSave['background'] = '#25219c'
My second function is :
def New():
btnSave = tk.Button(main, text="Save", bg="#25219c", fg="white", width=5, height=1, relief="flat", command=save)
btnSave['font'] = MyFont2
btnSave.place(x=750, y=1000, anchor=CENTER)
btnSave.bind("<Enter>", on_enter_save)
btnSave.bind("<Leave>", on_leave_save)
I don't know what is the problem if anyone has a solution?
You need to make sure of 2 main things:
Create the function that you want to call before the function you call it in.
Don't forget to call the main function too.
This format works:
def calledFunction():
print("This function is called in another function!")
def function():
print("This is the first function!")
calledFunction()
function()
The only function call is the winsound.Playsound function, the remainder is redundant. As commented elsewhere, you need to provide more information. However, to answer the question as stated, this is how you do it:
def f1(msg):
print(msg)
print("Calling f2")
f2("Hello from f1")
def f2(msg):
print("Starting F2")
print(msg)
def main():
f1("Hello from main")
if __name__ == "__main__":
main()
Thank you all for your support. This solved the issue.
SOLVED - answer found # How to take input from Tkinter
ONE GOTCHA - I tried copying the example's style in the solved link, though I kept some of my own formating. I found that if the object that is getting the returned value has been created with any other dot added to it, for example putting .pack() or in my case .grid() on the same line as the object is created, it will throw out an error when you try to pass the variable back. So, go ahead and use a second line to .pack() or .grid().
ORIGINAL POST
I've got a program that works fine on the command line and I am attempting to give it a GUI. Unfortunately, I'm still pretty confused about how to use functions / methods / commands inside the interface.
My example is to use a button to get everything rolling after getting input from the user. In the command line program, it was a user input passed as a variable to one function and return three other variables. Those three variables get passed to another function to provide a result, and then the result is formatted to display.
Well, I'm attempting to use tkinter. I've got an entry field which assigns the user input to a variable. I've got a button linked to a function that is meant to start the ball rolling... and I do not know how to send that variable to the needed function(s), or how to take the returned variable and apply it. In my specific example, I want to send the variable "notation" to the function "parse()", the output of "parse()" would be sent to "roll()", and the output of "roll()" sent to the variable "output" and then displayed. All this would be started using the button with "command=calculate", with "calculate()" being a function that gets the entire ball rolling.
I am so very sorry... I am entirely self taught, and I am sure that I am not even using the right terminology for a lot of this. I also understand that this example is not very pythonic - I will eventually put all of this into classes and change the functions to methods, but for now I just want to see it work.
Here is the code so far. Fair warning, this is not the only problem I am having... I just want to stick to one question until I can get it worked out.
#!/usr/bin/env python
from tkinter import *
from tkinter import ttk
import re
import random
# Parsing function from the original command line tool
# Turns dice notation format into useful variables
def parse(d):
dice, dtype_mod = d.split('d')
dnum = 1
dtype = 6
mod = 0
if dtype_mod:
if '-' in dtype_mod:
dtype, mod = dtype_mod.split('-')
mod = -1 * int(mod)
elif '+' in dtype_mod:
dtype, mod = dtype_mod.split('+')
mod = int(mod)
else:
dtype = dtype_mod
if not dtype: dtype = 6
if not mod: mod = 0
return (int(dice), int(dtype), int(mod))
# Rolling function from the original command line tool
# 'print()' will be changed into a variable, with the output
# appended in the working version.
def roll(a, b):
rolls = []
t = 0
for i in range(a):
rolls.append(random.randint(1, b))
t += int(rolls[i])
print(('Roll number %d is %s, totaling %d') % (i + 1, rolls[i], t))
return (int(t))
# Placeholder - the rest of the command line code will be used here later
# This code will be what starts everything rolling.
# For debugging, attempting to pass a variable, doing it wrong.
def calculate():
output = "this is something different"
return output
# Initialize
dice = Tk()
dice.title('Roll the Dice')
dice.geometry("800x600+20+20")
# Drawing the main frame
mainframe = ttk.Frame(dice, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
# Variables needed for program and widgets
random.seed()
notation = ""
output = """This is
an example of a lot of crap
that will be displayed here
if I ever get
this to work
and this is a really long line -Super Cali Fragil Istic Expi Ali Docious
"""
# Dice notation entry field, and it's label
notation_entry = ttk.Entry(mainframe, width=10, textvariable=notation)
notation_entry.grid(column=2, row=1)
ttk.Label(mainframe, text="Dice").grid(column=1, row=1)
# Section used for output
"""Huge laundry list of problems here:
1. textvariable is not displaying anything here. If I change it to text
it seems to work, but from what I can tell, that will not update.
2. I would love for it to have a static height, but height is not allowed here.
Need to figure out a workaround.
3. Also, have not figured out how to get the value returned by calculate()
to show up in here when the button is pressed..."""
output_message = Message(mainframe, textvariable=output, width= 600)
output_message.grid(column=1, row=2, rowspan=3, columnspan=3)
# The 'make it go' button.
"""Can I pass the function a variable?"""
ttk.Button(mainframe, text="Roll!", command=calculate).grid(column=3, row=5)
# This is a bunch of stuff from the command line version of this program.
# Only here for reference
"""while True:
notation = raw_input('Please input dice notation or q to quit: ')
if notation == "q":
raise SystemExit
else:
print(notation)
numbers = parse(notation)
(dice, dtype, mod) = numbers
total = roll(dice, dtype)
total += mod
print('Your total is %d' % total)"""
dice.mainloop()
When you are using textvariable when creating an object a string variable needs to be used instead of a normal string variable,i changed your variable notation to a string variable
notation = StringVar()
notation.set("")
Then to get the value of notation in your calculation sub i used
print(notation.get())
Your code that i have edited is pasted in full beneth
#!/usr/bin/env python
from tkinter import *
from tkinter import ttk
import re
import random
# Parsing function from the original command line tool
# Turns dice notation format into useful variables
def parse(d):
dice, dtype_mod = d.split('d')
dnum = 1
dtype = 6
mod = 0
if dtype_mod:
if '-' in dtype_mod:
dtype, mod = dtype_mod.split('-')
mod = -1 * int(mod)
elif '+' in dtype_mod:
dtype, mod = dtype_mod.split('+')
mod = int(mod)
else:
dtype = dtype_mod
if not dtype: dtype = 6
if not mod: mod = 0
return (int(dice), int(dtype), int(mod))
# Rolling function from the original command line tool
# 'print()' will be changed into a variable, with the output
# appended in the working version.
def roll(a, b):
rolls = []
t = 0
for i in range(a):
rolls.append(random.randint(1, b))
t += int(rolls[i])
print(('Roll number %d is %s, totaling %d') % (i + 1, rolls[i], t))
return (int(t))
# Placeholder - the rest of the command line code will be used here later
# This code will be what starts everything rolling.
# For debugging, attempting to pass a variable, doing it wrong.
def calculate():
print(notation.get())
output = "this is something different"
return output
# Initialize
dice = Tk()
dice.title('Roll the Dice')
dice.geometry("800x600+20+20")
# Drawing the main frame
mainframe = ttk.Frame(dice, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
# Variables needed for program and widgets
random.seed()
notation = StringVar()
notation.set("")
output = """This is
an example of a lot of crap
that will be displayed here
if I ever get
this to work
and this is a really long line -Super Cali Fragil Istic Expi Ali Docious
"""
# Dice notation entry field, and it's label
notation_entry = ttk.Entry(mainframe, width=10, textvariable=notation)
notation_entry.grid(column=2, row=1)
ttk.Label(mainframe, text="Dice").grid(column=1, row=1)
# Section used for output
"""Huge laundry list of problems here:
1. textvariable is not displaying anything here. If I change it to text
it seems to work, but from what I can tell, that will not update.
2. I would love for it to have a static height, but height is not allowed here.
Need to figure out a workaround.
3. Also, have not figured out how to get the value returned by calculate()
to show up in here when the button is pressed..."""
output_message = Message(mainframe, textvariable=output, width= 600)
output_message.grid(column=1, row=2, rowspan=3, columnspan=3)
# The 'make it go' button.
"""Can I pass the function a variable?"""
ttk.Button(mainframe, text="Roll!", command=calculate).grid(column=3, row=5)
# This is a bunch of stuff from the command line version of this program.
# Only here for reference
"""while True:
notation = raw_input('Please input dice notation or q to quit: ')
if notation == "q":
raise SystemExit
else:
print(notation)
numbers = parse(notation)
(dice, dtype, mod) = numbers
total = roll(dice, dtype)
total += mod
print('Your total is %d' % total)"""
dice.mainloop()
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.
from tkinter import *
def my_function(parameter1, parameter2):
total = int(entrada1.get()) + int(entrada2.get())
Label(root,text=calculated_property).pack()
root = Tk()
frame = Frame(root)
frame.pack()
root.title("Testing 123")
numero1 = IntVar()
numero2 = IntVar()
entrada1 = Entry(root,textvariable=numero1)
entrada1.pack()
entrada2 = Entry(root,textvariable=numero2)
entrada2.pack()
aceptar = Button(root,text="Calcular",command=my_function)
aceptar.pack()
root.mainloop()
I'm working on simple graphic interfaces in Python. I`m using the tkinter library to practice with.
The form generated is quite simple, it just consist of two inputs and a button that calls the function: my_function.
I have troubles calling that function because the attribute "command" does not allows any parameter, but my_function requires two that are given by the inputs of the form.
Then, the idea is calling several functions inside my_function and return in a label a calculated_property.
Can you give me any solution?
Thank U so much!
You can simply use a lambda function to pass the arguments:
aceptar = Button(root,text="Calcular", command=lambda: my_function(arg1, arg2))
This code is equivalent to doing:
def func():
my_function(arg1, arg2)
aceptar = Button(root,text="Calcular", command=func)
except that the function is created inline.
I am making a GUI Program in Tkinter and am running into problems.What I want to do is draw 2 checkboxes and a button. According to the user input next steps should take place. A part of my code has been shown below :-
CheckVar1 = IntVar()
CheckVar2 = IntVar()
self.C1 = Checkbutton(root, text = "C Classifier", variable = CheckVar1, onvalue = 1, offvalue = 0, height=5,width = 20).grid(row=4)
self.C2 = Checkbutton(root, text = "GClassifier", variable = CheckVar2, onvalue = 1, offvalue = 0, height=5, width = 20).grid(row=5)
self.proceed1 = Button(root,text = "\n Proceed",command = self.proceed(CheckVar1.get(),CheckVar2.get())).grid(row=6)
# where proceed prints the combined values of 2 checkboxes
The error that I am getting is typical ie a default value of both the selected checkboxes gets printed up and then there is no further input. The error that I get is NullType Object is not callable.
I searched on the net and I think the answer is related to lambda events or curry.
Please help ..
You're passing the value of self.proceed(CheckVar1.get(),CheckVar2.get()) to the Button constructor, but presumably what you want is for command to be set to a function which will call self.proceed(CheckVar1.get(),CheckVar2.get()) and return a new, possibly different value every time the button is pressed. You can fix that with a lambda, or by wrapping the call in a short callback function. For example, replace the last line with:
def callback():
return self.proceed(CheckVar1.get(), CheckVar2.get())
self.proceed1 = Button(root, text="\n Proceed", command=callback).grid(row=6)
This is pretty typical Tkinter. Remember: when you see a variable called command in Tkinter, it's looking for a function, not a value.
EDIT: to be clear: you're getting 'NullType Object is not callable' because you've set command to equal the return value of a single call to self.proceed (that's the NullType Object). self.proceed is a function, but its return value is not. What you need is to set command to be a function which calls self.proceed.
Like Peter Milley said, the command option needs a reference to a function (ie: give it a function name (ie: no parenthesis). Don't try to "inline" something, create a special function. Your code will be easier to understand and to maintain.