Passing Arguments with Newstyle signals in PyQT

我的未来我决定 提交于 2019-12-25 04:53:38

问题


I've been having some trouble with a set of dynamically created buttons in PyQT.

I'm using a list to create buttons corresponding to different values. Because each button is different, I need to have them pass an argument to the "Button Clicked" function identifying themselves separately so I can perform different actions with them. Here is what I have so far:

for x in self.buttons:
    name = str(x)
    button = QtGui.QPushButton(Frame)
    button.setText(name)
    cp_result = self.changeProject(name)
    if cp_result is None:
        print 'changeProject(%s) is None!', name
    else:
        print 'changeProject(%s) is OK (%r)', name, cp_result
        #button.clicked.connect(cp_result)
    setattr(self, "btn_%s" % name, button)
    self.btnList.append(button)

def changeProject(self, name):
    for x in self.btnList:
        if x.isDown:
            #Change UI Frame to blahblah
    return 'A String'

Working with Alex, this is the latest code, testing a return value on changeProject, which clearly cannot be a string, but I still need to determine which button is pressed between the auto-generated buttons.

Current error: TypeError: connect() slot argument should be a callable or a signal, not 'str'


回答1:


You can add attribute dynamically to objects using setattr

for x in buttons:
    name = str(x)
    button = QtGui.QPushButton(Frame)
    button.setText(name)
    button.clicked.connect(self.changeProject(name))
    setattr(self, "btn_%s" % name, button)



回答2:


What the error message is telling you is that self.changeProject("%s") for one of the values you're substituting for that %s returns None. Presumably you meant for that method to return something different?

It's impossible to help you much with the task of debugging changeProject further without seeing the code for it, of course. However, you could for example split the call into something like (once you've gotten rid of that ton of execs as per lazy1's suggestion):

cp_result = self.changeProject(name)
if cp_result is None:
    logging.error('changeProject(%s) is None!', name)
else:
    logging.info('changeProject(%s) is OK (%r)', name, cp_result)
    button.clicked.connect(cp_result)

This way instead of uselessly trying to "connect to None", you'll see all the names causing that to-you-surprising return value in your error log, and can then continue debugging based on that information. However, more likely than not, your bug might in fact become obvious by looking at the source of changeProject.

Edit: the argument to connect is of course coming from changeProject (not from another connect!-) -- fixed the snippet accordingly.




回答3:


I think you running 'exec' is too much, IMO. Your problem is that you are trying to connect to the QPushButton's .clicked(), which is really a signal. Do you really need to pass the argument here

exec 'self.btn_%s.clicked.connect(self.changeProject("%s"))' % (x, x)

Because if you don't you can connect it like this:

self.connect(self.btn_%s, SIGNAL('clicked()'), self.changeProject)

If you need to know which button is clicked you can iterate through the list to find which button is clicked:

for x in buttons:
if x.isDown(): (function)


You can also use pyqtSignal.




回答4:


Thanks for the assistance. I stumbled QSignalMapper, and this turned out to be exactly what I needed.

http://pysnippet.blogspot.com/2010/06/qsignalmapper-at-your-service.html




回答5:


QSignalMapper is the accepted way to accomplish this with Qt, but I have found it to be somewhat cumbersome with PyQt since there are some simpler options available. The easiest is probably to 1) tag each button with a unique id so you can tell them apart (or just use the button's text), then 2) use QObject.sender() to determine which button emitted the signal. For example:

for x in self.buttons:
    name = str(x)
    button = QtGui.QPushButton(Frame)
    button.setText(name)
    button.uniqueId = name  ## make this whatever you want..
    button.clicked.connect(buttonClicked)

def buttonClicked():
    button = QObject.sender()
    uid = button.uniqueId  ## got your ID back



回答6:


I'd like to necro this and mention another strategy/hack I just employed to solve this strategy. This is a somewhat specific strategy that wouldn't always work, but what I opted to do for a more recent implementation of this kind of setup was set the buttons as checkable.

This wound up being the path of least-resistance since I was then able to iterate through the layout and simply check which button held true for .isChecked(). No SignalMapping or extra attributes necessary.



来源:https://stackoverflow.com/questions/3807733/passing-arguments-with-newstyle-signals-in-pyqt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!