You are not logged in.

#1 2012-11-18 19:13:41

initbox
Member
Registered: 2008-09-27
Posts: 172

[Solved] Python (PyQt) reference to callable problem?

So, I'm having trouble with creating menu options that are connected to a function call with specific arguments...

Sample code:

	for i in range(0,8):
		a = QtGui.QAction(QtGui.QIcon(), "Option %s" % i, self)
		
		exec("""def f(): getattr(win, "show_option")(%i)""" % i)
		a.triggered.connect(f)
		self.menu.addAction(a)

This is the only way I could make it work and it seems like such a hack...

.connect obviously needs a reference to a callable and not a call.

I've tried everything I could think of, normally I would do this with a lambda, i.e.

a.triggered.connect(lambda: function("option"))

But it doesn't work in this case. All options will always call it with "7" (so the lambda is connected to the original "i"?)

I can see the problem but I don't know what to do about it.

Last edited by initbox (2012-11-19 16:20:22)

Offline

#2 2012-11-19 04:15:30

rockin turtle
Member
From: Montana, USA
Registered: 2009-10-22
Posts: 227

Re: [Solved] Python (PyQt) reference to callable problem?

Don't you want to register a different callback function for each menu option?

def option0 (): ...
def option1 (): ...
def option2 (): ...
etc.

for i in range(0,8):
	a = QtGui.QAction(QtGui.QIcon(), "Option %d" % i, self)

	a.triggered.connect("option%d" % i)
	self.menu.addAction(a)

I don't think Qt will provide any arguments when it calls your funtion, so the only way for you to know which option was selected is to provide a different function for each action.

Offline

#3 2012-11-19 04:48:09

vadmium
Member
Registered: 2010-11-02
Posts: 63

Re: [Solved] Python (PyQt) reference to callable problem?

initbox wrote:
a.triggered.connect(lambda: function("option"))

Assuming you meant something like

a.triggered.connect(lambda: function(i))

In that case the lambda function knows to use the i variable from the scope of the outer function where your loop is defined. But the value of the variable only gets looked up when the lambda is run. This is presumably after the loop has finished when i is left with the value of 7. Nested variable scoping in Python can be tricky like that. It’s the same story as using global variables.

Have you heard of functools.partial()?

docs.python.org/3/library/functools#functools.partial

I suggest something like

a.triggered.connect(partial(function, i))

which constructs a partial() object holding the current value of i, rather than a lambda function referencing the variable name.

Offline

#4 2012-11-19 10:26:03

initbox
Member
Registered: 2008-09-27
Posts: 172

Re: [Solved] Python (PyQt) reference to callable problem?

rockin turtle wrote:

Don't you want to register a different callback function for each menu option?

You don't need to create separate functions for it if you do it "cleverly", like I did, e.g. with a lambda so you can create a callable that calls a function with the argument you want.

Except it didn't work in this case as seen. It does work in other cases (i.e. when you don't need to use a changing variable like I did).

rockin turtle wrote:

I don't think Qt will provide any arguments when it calls your function, so the only way for you to know which option was selected is to provide a different function for each action.

It might provide some sort of source object reference, but not anything direct like a fancy message (unless you get the source object's displayed text or some sort of other "identifier" and act based on that). You could probably create a custom message that gets passed if you use Qt signals?


vadmium wrote:

In that case the lambda function knows to use the i variable from the scope of the outer function where your loop is defined. But the value of the variable only gets looked up when the lambda is run. This is presumably after the loop has finished when i is left with the value of 7. Nested variable scoping in Python can be tricky like that. It’s the same story as using global variables.
Have you heard of functools.partial()?

Yeah, that sounds correct, thanks, I'll try that. I'm not that great with the terminology.

I'm actually surprised that it can even get "i" afterwards, shouldn't it get discarded after the loop is done? Actually, I guess not, because the lambdas are still referencing it. Tricky.

Offline

#5 2012-11-19 16:20:06

initbox
Member
Registered: 2008-09-27
Posts: 172

Re: [Solved] Python (PyQt) reference to callable problem?

vadmium wrote:

functools.partial()

Yes! This solved it. Thanks!

Offline

Board footer

Powered by FluxBB