on_press event in kivy button not working as expected - python

Below is small snippet from my code .
issue:- get function is call when a screen "X" is loaded and some params are passed. In get i am create buttons with some names and assigning on_press event. But issue is on_press event is called automatically without pressing button and next screen comes into picture.
def get(self, service):
""" some code"""
but = Button(size_hint=(1, None))
but.text = str(i['name'][0][:10])
but.bind(on_press = self.change_screen(dict))
print "adding widget to home " + str(but)
self.home_box.add_widget(but)
def change_screen(self, dict):
self.screen_manager.current = 'Per_settings'
Any idea whats happening ?

but.bind(on_press = self.change_screen(dict))
You're calling self.change_screen(dict) - this is normal python syntax for a function call, the bind method doesn't even know about it and is only passed the result of the call.
You must pass the function itself. You can use functools.partial to automatically include the argument, though note that bind also passes extra arguments.
from functools import partial
but.bind(on_press=partial(self.change_screen, dict))

Related

in TKInter Why am I getting an error - takes 1 positional argument but 2 were given

Just learning Python and TKInter and have come across this error in my code. Don't know what I'm missing and hoping someone can help. I've included the button code and the function to show you what I have.
def change_font(self):
self.label_name['font'] = "Sawasdee"
self.button1 = Button(self.myframe2, text="Change font")
self.button1.bind("<Button-1>", self.change_font)
When you bind a function to an event, tkinter will call that function with an argument which represents the event which triggered the function to be called. That is why the error says it expected one argument (self) but got two (self, event).
You need to account for that event parameter even if you don't need it. The easiest way is to make it an optional named parameter:
def change_font(self, event=None):
self.label_name["font"] = "Sawasdee"
It's usually incorrect to use bind on a button. The Button widget accepts an attribute named command which can be used to tie the button to a function. In this case, the function will not get the event parameter:
def change_font(self):
self.label_name["font"] = "Sawasdee"
self.button1 = Button(self.myframe2, text="ChangeFont", command=change_font)
The advantage to using command is that it automatically supports not just clicking with the mouse, but also interacting with the button using the keyboard.

How do I pass parameters to a function from a guizero object?

I am unable to pass any parameter to a function called by a guizero widget either with the attribute "command" at initialization or by invoking an event.
This works as expected (no parameters passed):
from guizero import App, PushButton
def go():
print (10)
app = App(title="Test")
button = PushButton(app, text = "Click Here", command = go)
app.display()
but the following prints the number 10 once, before the button is clicked and then when I click, nothing happens
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here", command = go(10))
app.display()
The same result I get with this:
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here")
button.when_clicked = go(10)
app.display()
What am I missing?
Thanks in advance!
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here", command = lambda: go(10))
app.display()
Whenever you write go(10) anywhere, you are invoking the function go. You might think you are passing go with arguments, but you aren't because the parentheses next to go() invoke the function right then and there. If you want to pass the function go to another function, and go should also be passed with some arguments, then you need to wrap the function go and pass the wrapped function as the argument "command". Using a lambda function to wrap go(10) is one such way of doing this.
The reason this works is that the lambda function is NOT invoked right then and there. You are saying that that command() should invoke the declared anonymous lambda function eventually, and when that lambda function is called it will itself call go(10). You are declaring an anonymous lambda function, NOT invoking it. The lambda function will be invoked later on as command().
In all GUI frameworks command= (or similar) expects callback - it means function's name without () and without parameters.
If you use go(10) then it does
result = go(10)
PushButton( ...., command=result)
so it executes go(10) before you even click button - and it assign result to command=. Because go() returns None so you get PushButton( ...., command=None) and when you click button then you get nothing.
You have to create new function which you can run without ()
def go_10():
go(10)
PushButton( ...., command=go_10)
Or you can use lambda to create function directly in command=
PushButton( ...., command=lambda:go(10))
I found the solution!
Using a lambda function is a clever way to work around the callback limitation but I found a more proper way to pass arguments to a function by using the widget properties (pun not intended). As it turns out I missed a very brief reference to the property args that can be set at instantiation and is used to pass a list to the function called by command
So here is my solution:
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here", args = [10], command = go)
app.display()
As expected, it prints '10' every time you click the button and not before.
Thanks for all the other answers!

Python TK Notebook tab change check

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.

Python: Creating Default Argument for Pushbutton Connected Function

I have a QPushButton connected to trigger a function by:
self.pbLoad.clicked.connect(self.loadData)
This works great, however I would like to give that function a default argument in case I want to load a file manually in the code (for debugging). I tried:
def loadData(self,fileName = None):
if fileName is None:
fileName = QtGui.QFileDialog.getOpenFileName(self, 'Choose data file to open', '.', filter='*.lvm')
if fileName:
self.filename = fileName
self.parseFilename()
However this breaks my button and it stops working. Similarly, if I connect the button instead as:
self.pbLoad.clicked.connect(self.loadData())
The button is pressed as soon as the GUI is launched. Any ideas how I can get this to work without declaring an extra wrapper function? Thanks.
You need to have compatibility between the signal and the slot.
In that case, your signal sends no parameters, your slot must have no parameter.
I'd use a wrapper function as a slot and call the actual real function that you want to test.
About self.pbLoad.clicked.connect(self.loadData())
when you try to link the button to the function, you actually CALL the function : in that case, you connect your button with the return valuer of the function, which is None, hence, NOT connecting the button.
I'd use:
def __init__(self):
self.pbLoad.clicked.connect(self.buttonClickedSlot)
def buttonClickedSlot(self):
self.loadData()
def loadData(self,fileName = None):
.....

PyQt sending parameter to slot when connecting to a signal

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.

Categories