PySide6 Unsupported keyword on Grid Layout
-
Somehow this code gives
AttributeError
when usingrowSpan
andcolumnSpan
keywords.import sys from PySide6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget app = QApplication(sys.argv) window = QWidget() grid_layout = QGridLayout(window) widget = QLabel("Hello") grid_layout.addWidget(widget, 0, 0, rowSpan=1, columnSpan=1) window.show() app.exec()
I have PySide6
$ pip freeze | grep PySide PySide6==6.8.1.1 PySide6_Addons==6.8.1.1 PySide6_Essentials==6.8.1.1
This is the error message.
$ /bin/python grid_layout_problem.py Traceback (most recent call last): File "<................>/grid_layout_problem.py", line 19, in <module> grid_layout.addWidget(widget, 0, 0, rowSpan=1, columnSpan=1) AttributeError: PySide6.QtWidgets.QGridLayout.addWidget(): unsupported keyword 'rowSpan'
-
There are 2 overloads; the function does not have default parameters (and thus no named parameters):
QGridLayout::addWidget(QWidget*,int,int,QFlags<Qt::AlignmentFlag>=) QGridLayout::addWidget(QWidget*,int,int,int,int,QFlags<Qt::AlignmentFlag>=)
-
@friedemannkleint said in PySide6 Unsupported keyword on Grid Layout:
the function does not have default parameters (and thus no named parameters)
Hi @friedemannkleint. I always respect your replies as you have shown you know what you are talking about. However, although you are correct that the solution here is for the OP to use positional parameters like
addWidget(0, 0, 1, 1)
, I believe your explanation is not correct here.Python allows named parameters not only for optional ones but also for mandatory ones if the coder desires. The only requirements being that any named parameters come after any positional ones and that the name be correct. Within this coder can mix in named parameters and/or positional parameters as they please, regardless of default parameters or not.
When I look (in PyCharm) at the declaration of
QGridLayout.addWidget()
method it gives me@overload def addWidget(self, arg__1: PySide6.QtWidgets.QWidget, row: int, column: int, rowSpan: int, columnSpan: int, alignment: PySide6.QtCore.Qt.AlignmentFlag = ...) -> None: ...
Therefore in principle the OP should be able to use
..., rowSpan=1, columnSpan=1)
.To test this I introduce into the OP's code the following:
def addWidget(arg__1: QtWidgets.QWidget, row: int, column: int, rowSpan: int, columnSpan: int, alignment: QtCore.Qt.AlignmentFlag = ...) -> None: pass addWidget(widget, 0, 0, rowSpan=1, columnSpan=1, alignment=QtCore.Qt.AlignmentFlag.AlignLeft)
That is a "global"
addWidget()
function just to test. And this works without error. (You may remove the final truly optionalalignment=...
, it makes no difference.) So why not in the OP's case?If I try the OP's code under PyQt6 I get the same
'rowSpan' is not a valid keyword argument
error but a bit more information about what is going on:Traceback (most recent call last): File "/home/jon/QtTests/PyQt/namedparams/namedparams.py", line 14, in <module> grid_layout.addWidget(widget, 0, 0, rowSpan=1, colSpan=1, alignment=QtCore.Qt.AlignmentFlag.AlignLeft) TypeError: arguments did not match any overloaded call: addWidget(self, w: Optional[QWidget]): too many arguments addWidget(self, a0: Optional[QWidget], row: int, column: int, alignment: Qt.AlignmentFlag = Qt.Alignment()): 'rowSpan' is not a valid keyword argument addWidget(self, a0: Optional[QWidget], row: int, column: int, rowSpan: int, columnSpan: int, alignment: Qt.AlignmentFlag = Qt.Alignment()): 'rowSpan' is not a valid keyword argument
Python is having trouble matching the call to any overloaded definition.
I don't know exactly what or why, but I think the problem with using the keywords here --- which would normally work --- is because of
QGridLayout.addWidget()
being an@overload
, and the implementation of that (which involves something about rewriting the function declaration/definition each time it meets@overload
) causing it to be unable to find a correct match when a named (perhaps non-default?) parameter is used, confusing/changing its look up rules in this case?I am unable to track down the exact rules when mixing overloads and named parameters in Python. However I note that, say, Mixing function overloading and named parameters opens with:
What are the problems that occur when a language allows both function overloading and named parameters? Most languages seem to only support one or the other.
Overload resolution definitely becomes harder
It's not always problematic in Python. For example, the OP uses
widget = QLabel("Hello")
which matches@overload def __init__(self, text: str, parent: Optional[PySide6.QtWidgets.QWidget] = ..., f: PySide6.QtCore.Qt.WindowType = ...) -> None: ...
But here I am also able to write
QLabel(text="Hello")
if I wish and this does work. So sometimes a named parameter does work with@overload
and sometimes not. I don't know why this is the case but not forQGridLayout.addWidget()
, I can only guess it's to do with the various different overloads?I wonder if it's to do with the following: I believe Python implements
@overload
by type of the parameters being different to pick which one to call. I wonder/think it might do this by changing the names of the parameters to include a "type-specifier", at least in the case where there are alternative overloads? I wonder whether therowSpan
/columnSpan
parameter names might have been altered in this way and so no longer match the plain word? I do not have time to investigate this at the moment, but may return to it later.....I am always interested in learning if you know better/more specifically than this?
-
It is simply a restriction of shiboken that it does not generate code for named parameters unless the argument has default values (see PYSIDE-1964 ).
-
@friedemannkleint
That is a very brief answer. Could you explain in that case why I findQLabel(text="Hello")
does work if Shiboken "does not generate code for named parameters unless the argument has default values"? init(text[, parent=None[, f=Qt.WindowFlags()]]) does not havetext
taking any default value. Thanks.Also, glancing at the bug stuff, why does
from PySide6.QtCore import QRect print(QRect(left=1,top=2,width=3,height=4))
print a
QRect
with 0 size instead of erroring with OP'sunsupported keyword 'left'
?'Coz it looks like I spent a long time playing with this when I didn't need to... :( ;-)
-
What probably kicks in here is the ability to pass property=value pairs to the QObject-constructors. About the QRect, I don't know.
-
@friedemannkleint
Hi, yes, I was just coming to the same conclusion on this one! See e.g. https://stackoverflow.com/a/73285686/489865Qtw.QMessageBox(text="Foo")
text()
is a valid Qt property of QMessageBox;QObject properties
Almost all QObjects accept named arguments that correspond to the object's properties, even if they're not declared in the constructor's definition.
For instance, all QWidgets support the enabled property, meaning that you can do the following:
button = QPushButton(enabled=False)
So if a
QObject
has a property you can use its name aspropname=value
and that is not to do with the parameters to the constructor here.In effect
QLabel(text="Hello")
is not calling the constructor overload which acceptstext
as its first argument, it's calling the constructor which takes no parameter explicitly and than "augmenting" it by an assignment to property namedtext
instead.It also says there
The OP isn't really confused here, because, in Python terms, calling QMessageBox(text="Foo", title="Bar") would normally be entirely correct. The real problem is that the bindings cannot fully resolve all the differences between the Python and C++ call syntaxes, so various work-arounds must be used (such as keyword support for object properties)
My goodness it's complicated! I now have to accept your "restriction of shiboken that it does not generate code for named parameters unless the argument has default values" :) May I ask, I see you contributing on the bug forum, are you a Qt [Company] developer/contributor or just a punter like myself? If I had realised you were TQtC I probably would not have queried your reply further! Thanks.
-
I am actually working on Qt for Python for the Qt Company ;-)