Search code examples
pythonpyqtsignaturekeyword-argumentpyqt6

PyQt6: 'Title' keyword not recognized for QMessageBox


I'm very new to PyQt, so it's possible I'm doing this entirely wrong. I'm currently struggling to set the title property of a QMessageBox during instantiation in PyQt6. Here is my code:

import PyQt6.QtWidgets as Qtw

app = Qtw.QApplication([])
alert_window = Qtw.QMessageBox(text="Foo", title="Bar")
alert_window.exec()

When running this, I get the following error:

TypeError: 'title' is an unknown keyword argument

However, __init__ for QMessageBox (located in the Python stub QtWidgets.pyi) seems to clearly define title as a string argument, which I am passing correctly:

@typing.overload
    def __init__(self, icon: 'QMessageBox.Icon', title: str, text: str, buttons: 'QMessageBox.StandardButton' = ..., parent: typing.Optional[QWidget] = ..., flags: QtCore.Qt.WindowType = ...) -> None: ...

I've tried to run this by removing the title keyword argument and just leaving text and it seems to work just fine. Am I missing something entirely, or is the .pyi file incorrect? And, if this file is incorrect, is there any other documentation for PyQt that I can reference to find which arguments I can and cannot use? Thanks.


Solution

  • You have two issues:

    • you're confusing positional and named arguments;
    • QObject properties can be used as named arguments;

    Positional vs. Named arguments

    def function(positional1, positional2, named1=something, named2=whatever):
    

    The above means, simply put, that function:

    • requires two arguments (positional1 and positional2`);
    • can have two optional arguments (named1 and named2);

    The positional arguments:

    • are required when calling the function;
    • must respect the order (and, possibly, their expected type);
    • their names are just for reference;

    The named arguments:

    • are not required when calling the function;
    • if not specified, use their default value, which is whatever is evaluated after the = sign;
    • might be added as positional arguments, while respecting the order (function(x, y, z) means that named1 will be equal to z within the function);
    • might not be in the same order (function(x, y, named2=z, named1=w));

    The above means that calling function(x) will be correct, while function(positional1=x) will not.

    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)
    

    The problem

    Why this doesn't work?

    Qtw.QMessageBox(text="Foo", title="Bar")
    

    That's for two reasons:

    1. the title argument listed in the constructor is just a positional argument, so, as explained above, using a named argument title="Bar" is wrong;
    2. title is not listed in QMessageBox property list (nor in its inherited classes);

    Why this does work?

    Qtw.QMessageBox(text="Foo")
    

    This is for two reasons:

    1. Among the QMessageBox constructors, there is one that also doesn't require positional arguments;
    2. text() is a valid Qt property of QMessageBox;

    Final notes

    1. Don't use pyi stubs/definitions as reference; use the documentation, starting with the official C++ API (which is 99.9% the same as it's for Python) and eventually check the interactive Python console using help(class.function) for arguments and return values;
    2. Do more careful research and study about function arguments in Python, including argument [un]packing (conventionally, *args and **kwargs);
    3. PyQt (just as PySide) is a binding; it just wraps classes and functions that actually exist in the C++ side of the Qt library; that's why you can (and should) use the official C++ documentation instead of looking for the official PyQt one (or even the, sometimes misleading, PySide one) unless when strictly required;