PySide-6.6: clicked signal sends extra boolean argument to slot

2024/10/7 4:26:50

I just upgraded PySide6 (PySide6==6.6.0 from 6.2.2) and a new behavior is wreaking havoc with my GUI program. Every place where I have a clicked signal, the hooked up slot is receiving an extra bool==False argument.

x = QPushButton('New', clicked = self.new_append_title)
# ..... add x to my guidef new_append_title(self, *args, **kwargs): print(f' Args: {args}' )print(f' Kwargs: {kwargs}' )

This returns
Args: (False,) Kwargs: {}

Previously with earlier versions I was getting no arguments. I can fix all my code so it takes an extra dummy argument, but does anybody know what is going on here? I also updated a few other things in my requirements - like moving to python 3.12, so it might not be a PySide issue...


In response to ekhumoro I am actually trying to send and argument here. I have tried every permutation I can think of, including the one he gives in his answer below.

self.save_button.clicked.connect(lambda *, row=row: self.save_row(row))def save_row(self, row):print(f'row={row})

Finally worked. I am not clear on what * does, but it seems to hold the arg from qPushButton signal and therefore it is not necessary to have it in the member function.

Answer

I can indeed reproduce this odd difference in behaviour between various versions of PySide. However, unlike my previous answer on this topic, the current issue relates to how the signal connections are made. It seems PySide has been highly inconsistent in this regard, whereas PyQt is much more reliable (at least in the few versions I tested with).

The important difference with the current case, is that signal connections made via the clicked keyword argument, do not behave the same way as explicit connections made via the connect method. The whole sorry mess is detailed below, but the immediate take-home message here is this: to future-proof your PySide code, do not ever rely on default signal arguments being silently dropped. Always assume that some future version will start sending the default arguments, and protect your slots accordingly. This is how PyQt5/6 has always behaved, and it seems likely that PySide6 will eventually do likewise. In my own code, I have most commonly employed keyword-only arguments to work-around this issue, like this:

button.connect(self.slot)
...
def slot(self, *, mykeyword=True):

As you will see below, this behaves the same way across all versions of PySide and PyQt I tested, and is possibly the least intrusive in terms of API changes and general code-churn. (But see also my previous answer for several other options - with some provisos).


Now for the gory details.

Below is a test script that exposes the problems. As you will see from the output, both PySide2 and PySide6 show different behaviour depending on how the signal connection is made, and on the precise version used. The good news is that PySide-6.6.0 shows the same behaviour as PyQt5/6 when making connections via the clicked keyword argument. The bad news is that using connect shows the same inconsistent behaviour as PySide2. Note also that PySide2 itself shows some differences in how slot arguments are treated: when using connect(slot), the first argument is over-written, whereas with clicked=slot, it isn't.

TEST SCRIPT:

import sysif (pkg := sys.argv[1]) == 'ps2':from PySide2 import QtCore, QtWidgets
elif pkg == 'ps6':from PySide6 import QtCore, QtWidgets
elif pkg == 'pq6':from PyQt6 import QtCore, QtWidgets
else:from PyQt5 import QtCore, QtWidgetstry:KEYWORD = sys.argv[2] == 'kw'
except:KEYWORD = Falseprint(f'{QtCore.__package__} (Qt-{QtCore.qVersion()}), keyword={KEYWORD}')app = QtWidgets.QApplication(['Test'])def slot1(n=2): print(f'slot1: {n=!r}')
def slot2(*args, n=2): print(f'slot2: {args=!r}, {n=!r}')
def slot3(x, n=2): print(f'slot3: {x=!r}, {n=!r}')
def slot4(*, n=2): print(f'slot4: {n=!r}')for index in range(1, 5):slot = globals()[f'slot{index}']if KEYWORD:button = QtWidgets.QPushButton(clicked=slot)else:button = QtWidgets.QPushButton()button.clicked.connect(slot)button.click()

OUTPUT:

PySide2:

$ python3 test-signals.py ps2
PySide2 (Qt-5.15.11), keyword=False
slot1: n=False
slot2: args=(), n=2
TypeError: slot3() missing 1 required positional argument: 'x'
slot4: n=2$ python3 test-signals.py ps2 kw
PySide2 (Qt-5.15.11), keyword=True
slot1: n=2
slot2: args=(), n=2
TypeError: slot3() missing 1 required positional argument: 'x'
slot4: n=2

PySide6:

$ python3 test-signals.py ps6
PySide6 (Qt-6.2.2), keyword=False
slot1: n=False
slot2: args=(), n=2
TypeError: slot3() missing 1 required positional argument: 'x'
slot4: n=2$ python3 test-signals.py ps6 kw
PySide6 (Qt-6.2.2), keyword=True
slot1: n=2
slot2: args=(), n=2
TypeError: slot3() missing 1 required positional argument: 'x'
slot4: n=2$ python3 test-signals.py ps6
PySide6 (Qt-6.6.0), keyword=False
slot1: n=False
slot2: args=(), n=2
TypeError: slot3() missing 1 required positional argument: 'x'
slot4: n=2$ python3 test-signals.py ps6 kw
PySide6 (Qt-6.6.0), keyword=True
slot1: n=False
slot2: args=(False,), n=2
slot3: x=False, n=2
slot4: n=2

PyQt5/6:

$ python3 test-signals.py pq6
PyQt6 (Qt-6.6.0), keyword=False
slot1: n=False
slot2: args=(False,), n=2
slot3: x=False, n=2
slot4: n=2$ python3 test-signals.py pq6 kw
PyQt6 (Qt-6.6.0), keyword=True
slot1: n=False
slot2: args=(False,), n=2
slot3: x=False, n=2
slot4: n=2$ python3 test-signals.py pq5
PyQt5 (Qt-5.15.11), keyword=False
slot1: n=False
slot2: args=(False,), n=2
slot3: x=False, n=2
slot4: n=2$ python3 test-signals.py pq5 kw
PyQt5 (Qt-5.15.11), keyword=True
slot1: n=False
slot2: args=(False,), n=2
slot3: x=False, n=2
slot4: n=2
https://en.xdnf.cn/q/118863.html

Related Q&A

SMTPConnectError when using Django

Im using django-registration for handling of users registration. I tried to signup in order to test it, after testing it, I got this errorSMTPConnectError at /accounts/register/Being trying to find a s…

how to have a single search API using path parameters (No form used)

I have been using this view for searching a word as:db refers mongo connection (just for ref)@app.route(/) def index():return render_template(index.html)@app.route(/words-<word>, methods=[GET, PO…

understanding the return type of anonymous function lambda

I am trying to understand how can lambda function be used. def adder_func(a, b):return a + bprint(adder_func(4, 5))# trying with lambda print(list(lambda a, b: a + b))When trying to use lambda as a add…

What is the meaning of Failed building wheel for flask-mysqldb in pip3 install?

I have a MacBook Air with macOs Sonoma 14.0 and when I write in the terminal $ pip3 install flask-mysqldbI get the error:How can I fix this?

How do I install pygame for a new version of idle? (Windows) [duplicate]

This question already has answers here:Error on install Pygame(3.9) install (Win10) [duplicate](1 answer)Unable to install pygame on Python via pip (Windows 10)(6 answers)Closed 3 years ago.I installed…

How to parse a dynamic dom element?

I want to make a parser for scraping price, however I cant find the working method of parsing innerHTMLI dont know why, but selenium (getAttribute(innerHTML)), phantomjs (page.evaluation function(){ret…

Get a string in Shell/Python with subprocess

After this topic Get a string in Shell/Python using sys.argv , I need to change my code, I need to use a subprocess in a main.py with this function :def download_several_apps(self):subproc_two = subpro…

Python Indentation Error when there is no indent error [duplicate]

This question already has answers here:Im getting an IndentationError (or a TabError). How do I fix it?(6 answers)Closed 7 months ago.Is it me or the interpreter? I see no indentation error in my cod…

How to mark rgb colors on a colorwheel in python? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.Want to improve this question? Add details and clarify the problem by editing this post.Closed 8 months ago.Improv…

Cant get Selenium to loop through two dialogue box options correctly

So basically: the goal is to click on each symbol for each sector on this website, that pops up a table with contact details, I want to copy all of that information and store it in a file. Right now ev…