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.
Related
I am trying to create option menus in a loop, and the number of option menus is dependent on a variable. So I'm trying to use exec in my code.
I used the following to pass the value of 'i' to connect to which variable is changing the value.
But once I call the trace, the option I select in the Option menu, does not get updated in the Option menu box. If I do not call the trace funtion, it is getting updated in the display.
trackProcessMenu is the callback function.
Please let me know, where I am making the mistake.
Adding my code:
for i in range(0,numOfLibFiles):
exec('self.processOptionMenuVar_%d = StringVar()'%i)
process_menu = ("ff","ss","tt","fff","sss","ttt")
exec('self.processOptionMenu_%d = OptionMenu(self, self.processOptionMenuVar_%d, *process_menu )'%(i,i))
exec('self.processOptionMenu_%d.config(indicatoron=0,compound=RIGHT,image= self.downArrowImage, anchor = CENTER , direction = RIGHT)'%i)
exec('self.processOptionMenuVar_%d.set("--")'%i)
exec('self.processOptionMenu_%d.grid(row = i, column =1, sticky = N ,padx=30, pady =7 )'%i)
def trackProcessMenu(self,*args):
i = args[0]
exec('process = self.processOptionMenuVar_%d.get()'%i)
You should not use exec this way. A good rule of thumb is that you should never use exec until you can answer the question "why should I never use exec?" :-) exec has it's uses, but this isn't one of them.
Instead of trying to automagically generate variable names, keep your widgets in a list or dictionary.
For example:
option_vars = []
option_menus = []
for i in range(0,numOfLibFiles):
process_menu = ("ff","ss","tt","fff","sss","ttt")
var = StringVar()
om = OptionMenu(self, var, *process_menu)
om.config(indicatoron=0,compound=RIGHT,image= self.downArrowImage, anchor = CENTER , direction = RIGHT)
var.set("--")
om.grid(row = i, column =1, sticky = N ,padx=30, pady =7)
option_vars.append(var)
option_menus.append(om)
With the above, you can now reference the variables and menus with a simple index:
print("option 1 value is:", option_vars[1].get())
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 have created a list of entries in a for-loop. All entries are stored in a list so I can just obtain all of the inputs later:
inputs = [e.get() for e in self.entries]
However, I have also created a button next to each entry in the for-loop (so they each call the same function). How can I make it so that it recognizes which button belongs to which row/entry? Is there something I can do with event?
row = 0
self.entries = []
self.comments = []
for n in names:
e = Entry(self.top, bd = 5)
e.insert(0, n)
e.grid(column = 1, row = self.row, sticky = 'NSWE', padx = 5, pady = 5)
self.entries.append(e)
self.comments += [""]
commentButton = Button(self.top, text = "comment", command = self.commentSelected)
commentButton.grid(column = 3, row = self.row, sticky = 'NSWE', padx = 5, pady = 5)
self.row = self.row + 1
Yes -- use Callback Shims ( Currying Functions )
( courtesy Russell Owen )
I find I often wish to pass extra data to a callback function, in addition that that normally given. For instance the Button widget sends no arguments to its command callback, but I may want to use one callback function to handle multiple buttons, in which case I need to know which button was pressed.
The way to handle this is to define the callback function just before you pass it to the widget and include any extra information that you require. Unfortunately, like most languages, Python doesn't handle the mixing of early binding (information known when the function is defined) and late binding (informtation known when the function is called) particularly well. I personally find the easiest and cleanest solution is:
Write my callback function to take all desired data as arguments.
Use a callback shim class to create a callable object that stores my function and the extra arguments and does the right thing when called. In other words, it calls my function with the saved data plus the data that the caller supplies.
I hope the example given below makes this clearer.
The callback shim I use is RO.Alg.GenericCallback, which is available in my RO package. A simplified version that does not handle keyword arguments is given in the example below. All shim code is based on a python recipe by Scott David Daniels, who calls this "currying a function" (a term that is probably more common than "callback shim").
#!/usr/local/bin/Python
""" Example showing use of a callback shim"""
import Tkinter
def doButton(buttonName):
""" My desired callback.
I'll need a callback shim
because Button command callbacks receive no arguments.
"""
print buttonName, "pressed"
class SimpleCallback:
""" Create a callback shim.
Based on code by Scott David Daniels
(which also handles keyword arguments).
"""
def __init__(self, callback, *firstArgs):
self.__callback = callback
self.__firstArgs = firstArgs
def __call__(self, *args):
return self.__callback (*(self.__firstArgs + args))
root = Tkinter.Tk()
buttonNames = ( "Button 1", "Button 2", "Button 3" )
for name in buttonNames:
callback = SimpleCallback( doButton, name )
Tkinter.Button( root, text = name, command = callback ).pack()
root.mainloop()
You can also use lambda:
from tkinter import *
def bla(b):
...
root = Tk()
buttons = []
for i in range(...):
button = Button(root)
button.configure(command=lambda b=button: bla(b)) # Make sure the Button object already exists
buttons.append(button)
button.pack()
root.mainloop()
As far as I see, you can't create the buttons in a single list comprehension now, but it is simpler and more readable than a class imho.
I'm having some issues with the following code. This is the first time that I'm working with a GUI and it's been a while since I've worked with python as well. When I try to execute the solfield function with the button, it yields no output.
from Tkinter import *
import math
master = Tk()
n = float()
I = float()
def solfield():
pass
label_coils = Label(text='Number of Coils Per Meter', textvariable=n)
label_coils.grid()
coils = Entry(master)
coils.grid()
label_current = Label(text='Current in Amps', textvariable=I)
label_current.grid()
current = Entry(master)
current.grid()
calculate_button = Button(text='Calculate', command=solfield())
calculate_button.grid()
label_bfield = Label(text='B Field in +z Direction')
label_bfield.grid()
label_result = Label(text='solfield')
label_result.grid()
master.title('Coil Gun Simulation')
master.mainloop()
def solfield():
mu0 = math.pi*4e-7
solfield = mu0*n*I
print solfield
Any other tips would be appreciated as well, as there will eventually be much more coding for me to do.
This has been solved. If anyone is interested, here is the code after several fixes were made:
from Tkinter import *
import math
master = Tk()
label_coils = Label(text='Number of Coils Per Meter')
label_coils.grid()
coils = Entry(master)
coils.grid()
label_current = Label(text='Current in Amps')
label_current.grid()
current = Entry(master)
current.grid()
def solfield():
mu0 = math.pi*4e-7
n = float(coils.get())
I = float(current.get())
fieldmag = mu0*n*I
print fieldmag
calculate_button = Button(text='Calculate', command=solfield)
calculate_button.grid()
label_bfield = Label(text='B Field in +z Direction')
label_bfield.grid()
label_result = Label(text='solfield')
label_result.grid()
master.title('Coil Gun Simulation')
master.mainloop()
The problem is here:
calculate_button = Button(text='Calculate', command=solfield())
To pass the function solfield itself as the command, just use its name:
calculate_button = Button(text='Calculate', command=solfield)
What you're doing is calling the function, and then passing the return value of that function as the command.
Since you defined solfield above as do-nothing function, that return value is None, so you're telling calculate_button that its command=None, and it's properly doing nothing.
Meanwhile, as SethMMorton pointed out (but then deleted):
You have two functions named solfield, and you are naming a variable solfield in one of your solfield functions. Remove the empty function (the one with pass), and using a different variable name in the remaining function.
This isn't causing your actual problem, but it's certainly adding to the confusion that makes it harder for you to find the problem. (For example, if you hadn't included the excess empty definition of solfield at all, you would have gotten a NameError in the incorrect line, which would have made things easier to debug.)
Putting it all together, what you should do is:
Get rid of the empty (pass-only) definition of solfield.
Move the real implementation of solfield up above the point where you build the GUI.
Don't name a local variable solfield within the function.
Pass just solfield, not solfield() as the command for calculate_button.
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.