[PyQt] Inconsistent handling of QFlags?

Phil Thompson phil at riverbankcomputing.com
Tue Aug 8 16:46:34 BST 2017

On 30 Jul 2017, at 7:13 pm, Shaheed Haque <srhaque at theiet.org> wrote:
> Hi,
> I'd like to understand the rules about how function arguments are
> encoded by SIP. For example, I have these two uses of QFlags in one
> module:
> ==============
>        void setCompatibility(QFlags<KAlarmCal::KACalendar::Compatibility>c)
>            [void (QFlags<KAlarmCal::KACalendar::Compatibility> c)];
>        void set(const KDateTime &dt, const QString &text, const
> QColor &bg, const QColor &fg, const QFont &font,
> KAlarmCal::KAEvent::SubAction action, int lateCancel,
> QFlags<KAlarmCal::KAEvent::Flag>flags, bool changesPending = false)
>            [void (const KDateTime &dt, const QString &text, const
> QColor &bg, const QColor &fg, const QFont &font,
> KAlarmCal::KAEvent::SubAction action, int lateCancel,
> QFlags<KAlarmCal::KAEvent::Flag> flags, bool changesPending = false)];
> ===============
> Both have %MethodCode as needed. Also, in case it is relevant, the
> second (problematic) method also has this overload:
>        void set(KCalCore::Event *__0)
>            [void (const QSharedPointer<KCalCore::Event> &__0)];
> In the first case, the QFlags-based argument a0 is declared as a plain
> QFlags value and passed as follows:
> ===============
>        QFlags<KAlarmCal::KACalendar::Compatibility> a0;
>        ...
>        if (sipParseArgs(&sipParseErr, sipArgs, "B", &sipSelf,
> sipType_KAlarmCal_KAEvent, &sipCpp))
> ===============
> In the second case, SIP generate a pointer-to-a-typedef-to-QFlags for
> a7 like this:
> ===============
>        const  ::KDateTime* a0;
>        const  ::QString* a1;
>        int a1State = 0;
>        const  ::QColor* a2;
>        int a2State = 0;
>        const  ::QColor* a3;
>        int a3State = 0;
>        const  ::QFont* a4;
>         ::KAlarmCal::KAEvent::SubAction a5;
>        int a6;
>         ::KAlarmCal::KAEvent::Flags* a7;
>        int a7State = 0;
>        bool a8 = 0;
>         ::KAlarmCal::KAEvent *sipCpp;
>        if (sipParseArgs(&sipParseErr, sipArgs, "BJ9J1J1J1J9EiJ1|b",
> &sipSelf, sipType_KAlarmCal_KAEvent, &sipCpp, sipType_KDateTime, &a0,
> sipType_QString,&a1, &a1State, sipType_QColor, &a2, &a2State,
> sipType_QColor, &a3, &a3State, sipType_QFont, &a4,
> sipType_KAlarmCal_KAEvent_SubAction, &a5, &a6,
> sipType_KAlarmCal_KAEvent_Flags, &a7, &a7State, &a8))
> ===============
> Now it turns out that thee in indeed a typedef of the form of a7 in
> place, but it does not introduce a pointer "typedef
> QFlags<KAlarmCal::KAEvent::Flag> Flags;". And in any case, that's not
> what I wrote into the function definition given to SIP.
> So why does this second case (a) use the typedef and (b) introduce the
> "*"? The only difference I can see is that the enum inside the QFlags
> in the first case does not have a definition in SIP (it is only in the
> .h file) whereas the enum in the second case does have a SIP
> definition, though it seems counter-intuitive that would change the
> way QFlags is handled.
> Any clues welcome.

SIP can't handle template arguments in a Python signature and so asks you to provide %MethodCode and a C++ signature. However you also need to replace the template argument type with something that is valid in a Python signature. In the current version of SIP it then (wrongly) ignores that argument when generating the argument parser. Tonight's snapshot will be fixed so that it is treated as a fatal error.

Applying this to your two examples, the first one will now trigger an error. The second one will continue to work as now. Even though it still has a template argument, SIP knows (because of the typedef) how to convert it to a valid Python type. Therefore (unless there is another reason for it) there is no need to provide a separate C++ signature and %MethodCode.


More information about the PyQt mailing list