Reputation: 15
I want to pass object which calls on signal, as an argument to function so I can manipulate with that object in that function. That object is QLineEdit widget. Here is the example:
self.fieldList = []
for i in range(10):
self.valueField = QtGui.QLineEdit()
self.fieldList.append(self.valueField)
self.fieldList[i].cursorPositionChanged.connect(lambda: (self.checkState(self.fieldList[i], palette1, palette2)))
def checkState(self, line, palette1, palette2):
if len(line.text()) > 3:
line.setPalette(palette1)
else:
line.setPalette(palette2)
So, as you can see, I'm trying to pass list element self.fieldList[i] as an argument with name line in checkState() function. If I explicitly define fieldList element (i.e. self.fieldList[0]) in the checkState() function, the code works perfectly, but I can't pass it as argument. What am I doing wrong?
Btw. compiler is not giving me error message when I try to run the program, but it simply won't do the job it's supposed to (change the color of the QLineEdit when I write more than 3 characters)
EDIT:
@dex19dt
Yes, this is an example, but you are right! Only the last QLineEdit works like it should!
The problem is, I can't give all those widgets names because their number depends on the layer selected, which means sometimes there are 5 widgets, sometimes 11 or 75 even.
I can easily name those widgets and set text like so:
self.fieldList[i].setObjectName(_fromUtf8("attributeValueField_{0}".format(i)))
self.fieldList[i].setText(_fromUtf8("{0}".format(value.toString())))
and this works just fine for every widget, but I don't know how to define signals? Do you have an idea?
EDIT:
@dex19dt
OK, so I asked about this problem of index not "sticking" to the function call, and I found out that this is just the way python works and to fix this problem I needed to replace this line:
self.fieldList[i].cursorPositionChanged.connect(lambda: (self.checkState(self.fieldList[i], palette1, palette2)))
with this one:
self.fieldList[i].cursorPositionChanged.connect(lambda old, new, i=i: (self.checkState(self.fieldList[i], palette1, palette2)))
so this way current index copies to the signal index. Although I'm not really sure why is there old, new
part. Does it refer to the lambda function or simply defines left i
as old and right i
as new variable... Tried googling it, but without success.
Upvotes: 1
Views: 3025
Reputation: 1643
Well, I'm assuming that the code in your question is an example and not your real code. Because, since you're creating your widgets in a loop with no additional parameters, the result would be a overlap of all the objects in the window. You see, all the lineEdits would be in the same place (by default, the top left part of the window). If that's the case, will be really hard to tell where is the problem since it appears to me that it's just a matter of knowing which instance of the lineEdit is sending the signal.
So I will tell you how to make the code above work and if it's not the case you can always reply.
It's all about the way the signal is connected. If you take a look at your code, you're creating all the lineEdits with the same name (self.valueField). The objects are all created and they have their own position in the memory, but their reference in the connect is always overriden by the last connect. You can even make the test. If you add this line to your checkState function:
print self.fieldList.index(line)
You'll see that the signal is always called for the last item of the list, which is also the last signal connected.
You can simply set all your lineEdits one by one with a different name. And create your list also typing all the names in it. This way your for loop will only connect the lineEdits and not create them anymore.
EDIT:
Ok. One way to make this work is to use a SignalMapper. Take a look at the docs here.
In the example above you can do something like this:
first declare the mapper, the same way you declare other widgets:
self.myMapper = QtCore.QSignalMapper()
After your first for loop. You iterate over the list:
for item in self.fieldList:
self.myMapper.setMapping(item, self.fieldList.index(item))
item.textEdited.connect(self.myMapper.map)
self.myMapper.mapped[int].connect(checkState)
So what this will do is that it will map every object in your list with its corresponding index on the list (int). So when a signal is emitted, the mapper calls the checkState function with that index value as argument. This way you can reach your object using the list again:
def checkState(i):
print (self.fieldList[i].objectName())
self.fieldList[i].setText("It works!")
As you can see the mapper will send only the index as argument. So I guess you will need to adapt your function with the additional args palette1, palette2
.
Or you can use a preliminar function like a filter and then call the right checkState function inside it:
def myfilter(i):
checkState(self.fieldList[i], palette1, palette2)
I'm pretty sure that exist other approaches to make this work, but this one does the job while a better solution doesn't show up.
Upvotes: 1