I am trying to bind two functions to a button, so for example I want to toggle a buzzer and LED with a single button. Is there a way to attach two functions to the same button or can a button only do a single thing at once?
For example:
button.when_pressed = led.toggle && buzzer.toggle
Bind to a function that calls both functions:
def toggle_led_and_buzzer():
led.toggle()
buzzer.toggle()
button.when_pressed = toggle_led_and_buzzer
You can use 2 solutions. Just merge the two functions into a single function, then call up that single function with the button, or alternatively using a lambda
SOLUTION N.1
def ledtoggle_buzzertoggle():
led.toggle()
buzzer.toggle()
button.when_pressed = ledtoggle_buzzertoggle
SOLUTION N.2
You can also use lambda
button.when_pressed = lambda:[led.toggle(), buzzer.toggle()] #or without square brackets
Related
I've created multiple buttons at runtime and stored them in a list.
keys = []
keys.append(Button(label="-- Parent --"))
for key in node_obj.children.keys():
keys.append(Button(label=key))
Note that the number of children of node_obj may vary, so the number of buttons is not always the same. I'm trying to create callbacks for all the buttons and did it like this:
def test_fn(button):
print(button.label)
for button in keys:
button.on_click(lambda : test_fn(button))
but it always prints the label of the last button in the list. How can I modify it such that the label of the button that was clicked is printed?
This is a result of the way Python works. When the lambda is actually executed it uses the value of button from the outer scope—which is the last value of the loop. You will need to use the standard library functools.partial function to "bake in" each different button ahead of time:
from functools import partial
def test_fn(button):
print(button.label)
for button in keys:
button.on_click(partial(test_fn, button=button))
I the following code I want to bind all frame1 items to <'Enter'> Event, but it does not work. I mean canvas.focus_set() does not take effect. How can I solve my problem?
for w in frame1.winfo_children():
w.bind('<Enter>',canvas1.focus_set())
The comment made by Lafexlos actually sends you in the right direction. When you do
w.bind('<Enter>', canvas1.focus_set())
you call canvas1.focus_set() and use the return value of this function call (which is None) to bind to the event. This isn't what you want, because now every time the event is triggered, None is executed instead of canvas1.focus_set().
What you should do is pass a function reference to the bind function. The reference for calling canvas1.focus_set() is canvas1.focus_set. However, using
w.bind('<Enter>', canvas1.focus_set)
still doesn't work.
This is because the bind function passes an event object to the function it has been given, so it will call canvas1.focus_set(event) instead of canvas1.focus_set(). Because focus_set does not accept any arguments, this fails.
You can fix this in two ways. You could make an extra function, which does accept an event object and then calls canvas1.focus_set() without arguments, and then bind the event to this new function. The other option is to use an anonymous "lambda" function to basically do the same like
w.bind('<Enter>', lambda e: canvas1.focus_set())
This way the lambda function accepts the event object as e, but doesn't pass it to focus_set.
P.S. The <Enter> event is not the event that is triggered when you press the Enter button on your keyboard (that is <Return>). The <Enter> event is triggered whenever you move the mouse onto a widget and is accompanied by the <Leave> event for when you leave the widget with your mouse. This might be what you want, but it often leads to confusion.
by using canvas1.bind_all which is the parent of frame1 I solved my problem. Thanks for all solutions.
If there is any mistake I see you making it is likely you are not calling the write command for the Enter key. Hopefully, if you are attempting to do this on windows, you should rather use Return.
More like:
for w in frame1.winfo_children():
w.bind('<Return>',canvas1.focus_set())
Newbie programmer here. I am building a tk based desktop app and ran into an issue:
I have a main window with several stuff in it including two tabs:
global nBook
nBook = ttk.Notebook(self, name="book")
nBook.place(x=300,y=400)
frameOne = ttk.Frame(nBook, width=100, height=100)
frameTwo = ttk.Frame(nBook, width=100, height=100)
nBook.add(frameOne, text='T1')
nBook.add(frameTwo, text='T2')
frameOne.bind("<<NotebookTabChanged>>", self.routine())
frameTwo.bind("<<NotebookTabChanged>>", self.routine())
routine() is a function that SHOULD perform a check every time T2 is selected
def routine(self):
if str(nBook.index(nBook.select())) == "2":
# Do stuff
else:
pass
Problem is that it doesn't do anything when the tab is changed except for calling the routine function as soon as I open the app and never again. I just can't figure out what I'm doing wrong.
Could anyone point out the mistake(s) I'm making?
EDIT: Same issue if I try
nBook.bind("<<NotebookTabChanged>>", self.xbRoutine())
The error comes from the event binding statements: when using self.routine() the callback is called when the bind statement is executed, not when the event is triggered. To get the correct behavior, the second argument of bind should be the name of a function not a call to this function, so simply remove the parentheses.
Another error: when using bind, the callback function is expected to have a first argument (traditionnaly called event) storing the event parameters. So you should define your callback as:
def routine(self, event):
...
I had the same problem. The answer given by #sciroccorics is not complete.
What you bind is not the tab itself, but the notebook.
So, it should be
nBook.bind("<<NotebookTabChanged>>", self.xbRoutine)
Alternatively you could use lambda.
In your case this will look something like this:
frameOne.bind("<<NotebookTabChanged>>", lambda _: self.routine())
Don't forget the _, otherwise you will get a TypeError, since the event is passed as an argument.
lamba is really helpful if your function requires one or more arguments.
I am using Tkinter and Python3.
In my application there are multiple buttons, that are calling their own functions.
Now I would like to add a function, that gets called by clicking on any button, without unbinding the functions specific to the button itself.
What I am trying to do is:
Multiple buttons have their own function:
**Button1** calls **function A**
**Button2** calls **function B**
**Button3** calls **function C**
Whenever ANY button is called, ONLY Function D is called, a timer of 10 minutes is set, and when any button is called now, it calls his own function.
The simplest way is to have all three buttons call the one function, with all the state and logic contained within that one function. Each time any button is pressed the state determines whether that press event calls function D or the appropriate function for the button pressed (A, B or C).
If you cannot arrange that then just make functions A, B and C do nothing but call function D, which arranges to execute the D code or A, B or C code depending on the state.
I have a taskbar menu that when clicked is connected to a slot that gets the trigger event. Now the problem is that I want to know which menu item was clicked, but I don't know how to send that information to the function connected to. Here is the used to connect the action to the function:
QtCore.QObject.connect(menuAction, 'triggered()', menuClickedFunc)
I know that some events return a value, but triggered() doesn't. So how do I make this happen? Do I have to make my own signal?
Use a lambda
Here's an example from the PyQt book:
self.connect(button3, SIGNAL("clicked()"),
lambda who="Three": self.anyButton(who))
By the way, you can also use functools.partial, but I find the lambda method simpler and clearer.
As already mentioned here you can use the lambda function to pass extra arguments to the method you want to execute.
In this example you can pass a string obj to the function AddControl() invoked when the button is pressed.
# Create the build button with its caption
self.build_button = QPushButton('&Build Greeting', self)
# Connect the button's clicked signal to AddControl
self.build_button.clicked.connect(lambda: self.AddControl('fooData'))
def AddControl(self, name):
print name
Source: snip2code - Using Lambda Function To Pass Extra Argument in PyQt4
use functools.partial
otherwise you will find you cannot pass arguments dynamically when script is running, if you use lambda.
I'd also like to add that you can use the sender method if you just need to find out what widget sent the signal. For example:
def menuClickedFunc(self):
# The sender object:
sender = self.sender()
# The sender object's name:
senderName = sender.objectName()
print senderName
In general, you should have each menu item connected to a different slot, and have each slot handle the functionality only for it's own menu item. For example, if you have menu items like "save", "close", "open", you ought to make a separate slot for each, not try to have a single slot with a case statement in it.
If you don't want to do it that way, you could use the QObject::sender() function to get a pointer to the sender (ie: the object that emitted the signal). I'd like to hear a bit more about what you're trying to accomplish, though.