Skip to content

KDAB contributions to Qt 5.0 (part 1)

Yesterday Qt 5.0.0 was released. It is the culmination of huge amounts of work since the announcement of and start of Qt 5’s development. This is the first major release of Qt since the launch of open governance in Autumn 2011, when it became much easier for external individuals and companies to contribute to Qt in terms of code submissions, code reviews, design, and maintenance.

As KDAB benefits from a successful Qt, we have made investments to contribute to the long-term health of the frameworks. There are over 1100 commits from KDAB in the qtbase git repository alone, and many others in other Qt repositories. Excluding individual contributors, we have consistently been the next biggest contributor, second only to the owners of Qt (Nokia/Digia). We have been fixing bugs, implementing wishes from the JIRA tracker, designing writing and testing new features, implementing platform support, reviewing other contributors’ code and participating in discussions around the project.

A new blog series here will detail some of the interesting features of Qt 5.0 which were contributed by KDAB.

QEventLoopLocker

One of the early complete new features contributed by KDAB to Qt 5 was the QEventLoopLocker. This is a class whose need first appeared in KDE, and it relates to the quitOnLastWindowClosed concept in Qt. For KDE applications, there was a desire to quit applications when the last window was closed, but there was also a need to finish some operation before quitting the application. For example, the email with the large attachment is not fully sent yet, or we’re still uploading a large file to an FTP site somewhere. As these operations typically have separate, out-of-process UI, it is ok to not show any window until the job is finished.

That concept was generalized to be able to quit not just the application, but any event loop or any thread, using the new QEventLoopLocker RAII class. This will make implementation of job-queues much easier in Qt 5. For maintenance, the quitOnLastWindowClosed feature in Qt is implemented using the same implementation details as the QEventLoopLocker.

Improved MetaType features

The QMetaType system has seen some large improvements in Qt 5 contributed by KDAB. Some of the improvements were explained at Qt Developer Days in Berlin.

Declaring and using metatypes is now much more convenient in many cases. For example, if you wrote a MyObject type inheriting QObject, or MyWidget type inheriting QWidget in Qt 4, and you wished to put an instance of it into a QVariant, you would need to first use

 Q_DECLARE_METATYPE(MyObject*)

somewhere in your code. That would allow you to use

 QVariant var = QVariant::fromValue(myObjectInstance);

In Qt 5, the Q_DECLARE_METATYPE use is no longer needed if your type inherits directly or indirectly from QObject. It is also not needed for any Qt container or smart pointer using types which are themselves metatypes.

For example, these constructs are no longer needed when using Qt 5:

 Q_DECLARE_METATYPE(QList<int>)
 Q_DECLARE_METATYPE(QList<QObject*>)
 // Because MyObject* is automatically a metatype from above!
 Q_DECLARE_METATYPE(QList<MyObject*>) 
 Q_DECLARE_METATYPE(QSharedPointer<<MyObject*>>)
 // Because QList<MyObject*> is automatically a metatype from above!
 Q_DECLARE_METATYPE(QVector<QList<MyObject*>>) 
 

This is also true for types where the user does still have to use Q_DECLARE_METATYPE, for example, when the type does not inherit from QObject:

 struct SomeStruct {};
 Q_DECLARE_METATYPE(SomeStruct)
 // No need for Q_DECLARE_METATYPE(QList<SomeStruct>)

 // later:
 QList<SomeStruct> structs;
 QVariant var = QVariant::fromValue(structs);

In some cases in Qt 4, it is also necessary to use the qRegisterMetaType method. The reason to need this is usually that Q_PROPERTY is used with a metatype which is not built-in (types such as int and QString are built-in and don’t need to be marked as metatypes).

With the new possibility of automatically declaring certain types as metatypes, the question of whether we could automatically register them came up too. Because qRegisterMetaType only needs to be used in a small number of cases, most of which relate to QObject or Q_PROPERTY in some way, it was possible to use moc to automatically register types which have not yet been registered. So now, in Qt 5, there are even fewer reasons to have to use qRegisterMetaType.

Another new qRegisterMetaType related feature in Qt 5 is the detection of accidental ODR violations. This often does not have any effect, but can accidentally hide problems such as typos which can mysteriously make your application not work properly. Now, you will get a compile-time or runtime error if using Q_DECLARE_METATYPE and qRegisterMetaType together in a way which has undefined behavior.

Another new feature is the possibility for built-in qobject_cast-style conversion through the inherited types. For example:

 MyObject *myObjectInstance = ...;
 QVariant var = QVariant::fromValue(myObjectInstance);

 // elsewhere...
 if (var.canConvert())
 {
   QObject *obj = var.value();
   // Use the properties of obj
 }

This adds features ‘for free’ to domain-specific languages implemented using Qt where the language is based on QObject and properties. It only works for types which inherit from QObject, and can not be generalized for a generic static_cast. The reason for that limitation is that internally it uses the generated QMetaObject of the stored type. Future versions of Qt will extend this feature in a similar way for QSharedPointer conversions.

Another nice but minor feature is that pointers to QObject derived types contained in a QVariant now print out their address and objectName when used with qDebug. For example:

 QLabel* l = new QLabel;
 l.setObjectName("someLabel");
 QVariant var = QVariant::fromValue(l);
 qDebug() << var;
 // Qt 4 prints: QVariant(QLabel*, )
 // Qt 5 prints: QVariant(QLabel*, QLabel(0x7fff792fefa0, name = "someLabel") ))

The goals I wrote about elsewhere last year are now met, though the implementation is somewhat different in Qt 5. The SFINAE pattern I wrote about is used extensively to make these features possible.

CMake Config files

For many users of Qt, the CMake buildsystem tool provides advantages over the bundled qmake. Some of the differences compared to qmake were part of a presentation at the recent Qt Developer Days hosted by KDAB in Berlin. Part of the difference in how using CMake will differ when using it with Qt 4 versus Qt 5 is that ‘CMake Config files’ are now generated as part of the build-process of Qt itself and shipped in packages. I previously detailed what that means for users of CMake, and have added new comprehensive documentation to guide new users through the process of using CMake with Qt 5.

Although porting is required when changing from using Qt 4 to Qt 5, simple variable mappings can ease the porting burden.

19 thoughts on “KDAB contributions to Qt 5.0 (part 1)”

  1. Very nice! QEventLoopLocker sounds especially appealing. I always thought Amaroks “I’m going to keep running in systray unless you explicitly tell me otherwise” was a pretty neat workaround, but QEventLoopLocker sounds like a more purposeful solution.

  2. These are really great improvements in Qt meta-object features 🙂 Some of them have already made things easier in my daily work.
    Do I still need to use Q_DECLARE_METATYPE(QList*) ?
    Supposing QList doesn’t inherit from QObject, I gues the answer is yes, right ? 🙂

    Extracting QObject’s from QVariant’s created from QObject-derived classes is awesome ! Any chance for having this also working to extract a QList from QVariant’s containing QList ? Those weird reinterpret_cast’s always scare me 🙂

    1. Oops, some characteres have been removed. I mean:
      Do I still need to use Q_DECLARE_METATYPE(QList < QObject * > *) ?

      And any chance for extracting a QList < QObject * > from a QVariant build from a QList < MyObjectDerived * > ?

      1. steveire

        Hi there,

        Yes, you still need to use Q_DECLARE_METATYPE(QList < QObject * > *)

        Having a pointer to a QList is not really common enough to have explicit code for :).

        I hope to make it easier to handle QList < MyObjectDerived* > but it didn’t make it into Qt 5.0. We’ll see what is possible for 5.1, but no guarantees I’m afraid, due to backwards compatibility.

  3. […] In particular, many types in Qt have been marked with the Q_DECLARE_TYPEINFO macro as PRIMITIVE, or MOVABLE as appropriate. All QFlags are now automatically marked as Q_PRIMITIVE_TYPE, using a partial template specialization. This makes the Qt containers like QList perform better when used with flag types ‘for free’ in Qt 5. Partial template specialization is a C++ language feature which enables some of the metatype features I mentioned yesterday. […]

  4. […] In particular, many types in Qt have been marked with the Q_DECLARE_TYPEINFO macro as PRIMITIVE, or MOVABLE as appropriate. All QFlags are now automatically marked as Q_PRIMITIVE_TYPE, using a partial template specialization. This makes the Qt containers like QList perform better when used with flag types ‘for free’ in Qt 5. Partial template specialization is a C++ language feature which enables some of the metatype features I mentioned yesterday. […]

  5. Hi again,

    Maybe I misunderstood what you said about qRegisterMetaType, but when using QMetaProperty::read on a property with type QObjectDerived * or QList/QSet of QObjectDerived *, I get a run-time message:


    Unable to handle unregistered datatype 'QList' for property 'Teste::User::myClassList'

    Do I need to run qRegisterMetaType for those cases ? I’ve noticed that QVariant::fromValue implictly run a qRegisterMetatype.

    Thanks,

    1. steveire

      I’ve just tried it, and it works for me.

      Maybe try sending a minimal code example by email.

        1. steveire

          Thanks.

          The problem is that when moc is run on user.h, MyClass is only forward declared, so moc does not know it is a QObject subclass. That’s a limitation of the implementation.

          Sorry about that.

          1. No problem, that’s a good reason to use a full #include instead of forward decl.

            Thanks for help 🙂

  6. Hi there again,

    I’m facing the unfortunate case of having two classes with the same name in different namespaces:

    namespace QtUml {
    class QClass ...
    Q_PROPERTY(p1 ...)
    ...
    Q_PROPERTY(pn ...)
    }
    namespace QtMof {
    class QClass ...
    Q_PROPERTY(p1 ...)
    ...
    Q_PROPERTY(pm ...)
    }

    n > m

    I have no explicit Q_DECLARE_METATYPE in my code, but it seems moc auto declares both as ‘QClass *’. Both classes are loaded as plugins (QtMof::QClass is loaded after QtUml::QClass). I’m using qDeclareMetaType and qDeclareMetaType.

    For some reason, when both plugins are loaded, the metaObject class from QtUml::QClass shows only those properties declared in QtMof::QClass (apparently because moc is also declaring it as QClass * and therefore overriding previous QClass * declaration for QtUml::QClass *).

    Any hint for that issue ? Any way to tell moc to *not* auto-declare pointers to QObject-inheriting classes ?

      1. I found it, it’s my fault 🙂 I was using macros for defining namespaces:

        QT_BEGIN_NAMESPACE_QTMOF

        And it seems MOC looks for for explicit namespace directives. When I changed it to:

        QT_BEGIN_NAMESPACE
        namespace QtMof {

        everything works like a charm now !

        Thanks anyway 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *