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):
.....
Related
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.
For an A-level computing project, I am making a car data monitoring system. I have a button that opens the filedialog.askopenfilename method. When I pass this through a method like below, it doesn't work. However when I pass it straight into the button, it works fine. Any ideas as to why?
Doesn't work:
def get_data_file():
filedialog.askopenfilename
return
OpenfileButton=Button(master,text="Select File",width=20,command=get_data_file).grid(row=3, column=2)
works:
OpenfileButton=Button(master,text="Select File",width=20,command=filedialog.askopenfilename).grid(row=3, column=2)
You need to actually call the function
def get_data_file():
filedialog.askopenfilename()
When you pass the function to the button you should not call it but simply pass it to be called when the button is clicked, but as you have now wrapped it in another function it must be called by you.
The return is redundant and can be left out if you wish. All python functions return None by default.
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))
I have a program that at some point opens a new window (filled with buttons and gizmo's for the user to select and play around with) that is defined as follows:
def window(self,master):
def close(self):
# change some variables
self.destroy()
top = self.top = Toplevel()
# Several lines of buttons
top.lift()
top.protocol("WM_DELETE_WINDOW",close(self))
I initially had a close button there that would wrap everything up nicely but I noticed that if the user used the standard 'X' in the corner of the window, this function obviously would not be called and that would give a lot of problems later on. I found out about the 'WM_DELETE_WINDOW' suggestion from some other questions on this website but it gives me a rather strange error:
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1630, in wm_protocol
'wm', 'protocol', self._w, name, command)
TclError: bad window path name ".33862072"
I assume that it somehow has gotten the wrong window ID and is unable to catch the event. My question is thus, is that true or not and secondly how should I continue to deal with this issue.
Let's examine this line of code:
top.protocol("WM_DELETE_WINDOW",close(self))
This line of code is saying "immediately call the function close(self), and assign the result to the protocol handler. See the problem? It's immediately calling close, likely before self has been fully constructed. You don't want the function to be called, you want to pass in a reference to the function.
Make close be a method of self (rather than an embedded function) and change the call to top.protocol to look like this (note the lack of trailing parenthesis):
top.protocol("WM_DELETE_WINDOW", self.close)
If you prefer to keep the nested function, you can use lambda:
top.protocol("WM_DELETE_WINDOW", lambda window=self: close(window))
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.