📜 ⬆️ ⬇️

Porting Qt4 application to Qt5

Somewhere here not so long ago there was a post about innovations in Qt5. Everything seems to look great, but what about the actual applications that already exist? In this article, I will look at an example of porting one of my projects to Qt5 while maintaining source compatibility with Qt4.

So, as expected, my GUI application is not going to. And it's not going to because I use standard Qt widgets. After a brief trial, it turns out that the widgets are now in a separate module and must be explicitly included. Let's make it compatible with Qt4 in the way - we add the following code to our project qmake file (* .pro | * .pri):
greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets DEFINES += HAVE_QT5 } 

Also, from now on, thanks to the added define, you can insert #ifdef HAVE_QT5 anywhere in your code and then write code specific to Qt5. The same effect with ifdef could be achieved by creating a precompiled header with the contents:
 #define HAVE_QT5 (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) 


And again, my project is not going to. This time the reason is missing Q_WS_ * defaults. With Q_WS_WIN and Q_WS_MAC everything seems to be very clear, just change them to Q_OS_WIN and Q_OS_MAC respectively. But what to do with Q_WS_X11? Quite often, Q_WS_X11 is used as a simple sign of Unix-like systems with the exception of Mac OS (X), but it also happens that under this define hides X11-specific code. But in my case, this is basically the same thing, so you can simply replace it with some other define (we cannot simply redefine Q_WS_X11, as some parts of Qt still use this definition) and declare it in the proper place, in the project file for example:
 unix:!mac:DEFINES += HAVE_X11 

')
Go ahead. My project is quite old and it uses a lot of obsolete classes or methods of classes or anything that is already thrown out or prohibited in Qt5. Build again does not go. Some of what is obsolete can be found in the Qt5 documentation . As a rule, the same documentation states that it should be used in return, although there are exceptions, which I will discuss below. If, in the Qt5 documentation, no obsolete classes and their methods are found, then these are probably Qt3 residues and can be found in the Qt4 documentation . From what I stumbled on it is worth noting: QIconSet, QMenuItem (qt3. Change to QIcon and QAction, respectively), QAbstractItemModel :: reset (use beginResetModel / endResetModel), QKeySequence is no longer cast into int (do a loop on sequence elements), part QUrl migrated to the new QUrlQuery class, Qt :: escape removed (your inline wrapper over Qt :: escape and QString :: toHtmlEscaped), qInstallMsgHandler (qInstallMessageHandler) ...

Further QPA . Everything would be fine, but with the advent of this thing, the very useful class QX11Info disappeared from Qt, and the documentation on this subject does not say anything sane. He disappeared because he was tightly tied to Xlib. Instead, QPlatformNativeInterface appeared in Qt5, which, however, was soon banned and now has only a private class / header. In my project, as a quick solution still tied to Xlib, I simply wrote a wrapper on QX11Info, which in the case of Qt5 has its own implementation:
x11info.h
 #ifndef X11INFO_H #define X11INFO_H typedef struct _XDisplay Display; class X11Info { static Display *_display; public: static Display* display(); static unsigned long appRootWindow(int screen = -1); }; #endif // X11INFO_H 

x11info.cpp
 #include "x11info.h" #ifdef HAVE_QT5 # include <X11/Xlib.h> # include <QtGlobal> #else # include <QX11Info> #endif Display* X11Info::display() { #ifdef HAVE_QT5 if (!_display) { _display = XOpenDisplay(NULL); } return _display; #else return QX11Info::display(); #endif } unsigned long X11Info::appRootWindow(int screen) { #ifdef HAVE_QT5 return screen == -1? XDefaultRootWindow(display()) : XRootWindowOfScreen(XScreenOfDisplay(display(), screen)); #else return QX11Info::appRootWindow(screen); #endif } Display* X11Info::_display = 0; 

I can’t vouch for the logical fidelity of the implementation, since the code has not been tested yet.
I also have native event handlers, such as QApplication :: x11EventFilter for example. In Qt5, of course, they had to be rewritten. To do this, we need QAbstractNativeEventFilter and some xcb programming knowledge (Xlib will not work here because QPA knows nothing about it). In principle, the transition to xcb is not too complicated due to the similarity of the API of these two libraries, but it does not hurt to stock up with manuals. In my case, the implementation was rather trivial: in its application class, next to x11EventFilter, added another method called xcbEventFilter and several ifdefs to compile only the desired method. Then I created a class inherited from QAbstractNativeEventFilter and from it simply redirected to handle all xcb events to our method:
 #ifdef HAVE_X11 # ifdef HAVE_QT5 class XcbEventFiter : public QAbstractNativeEventFilter { MyApplication *app; public: XcbEventFiter(MyApplication *app) : app(app) {} virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE { if (eventType == "xcb_generic_event_t") { return app->xcbEventFilter(message); } return false; } }; # endif // .... #endif 

If you like, application initialization can be replaced by a singelton application custom in nativeEventFilter.

Next, the plugins. In Qt5, they are loaded in a different, incompatible way and must also be declared differently. Made such an unpleasant breakdown in order to optimize (now it is not necessary to load the entire plugin as a full-fledged shared library, to make sure that it is a Qt plugin at all and to get any meta information from it, such as, for example, the version of the program with which this plugin Compatible. More details here ), but nevertheless it should be repaired anyway.
For a start, we make all macros for exporting our plug-ins conditional like this:
 #ifndef HAVE_QT5 Q_EXPORT_PLUGIN2(myplugin, MyPlugin); #endif 

Also in the plugin class, conditionally add a new Qt5-specific Q_PLUGIN_METADATA macro somewhere near Q_INTERFACES but after Q_OBJECT:
 #ifdef HAVE_QT5 Q_PLUGIN_METADATA(IID "tld.domain.Project.MyPluginInterface" FILE "myplugin.json") #endif 

The FILE part of “myplugin.json” is only needed if we actually need metadata in the plugin, and as for the interface “tld.domain.Project.MyPluginInterface”, this is the same interface as in Q_DECLARE_INTERFACE. In my case, the metadata will store: the version of the plug-in, the minimum version of the program itself and the priority of loading. You also need to remember to add magic with the HAVE_QT5 declaration to the plug-in project files, or, as a quick version without magic, use #if QT_VERSION> = 0x050000 .
In the case of static plugins, you will have to change the call to the Q_IMPORT_PLUGIN macros. As a parameter, they now take the name of the plug-in class, and not what was the first parameter in Q_EXPORT_PLUGIN2.

So, launch! And, as it should have been, segfault. My code expects QMetaType :: Void == 0, but this is not so in Qt5. Great, fix, run, and segfault again. This time the problem is that some types are declared in one place, and Q_DECLARE_METATYPE for them is declared in another. Because of this, the latter, even with an explicitly enabled hider with a type, does not work correctly. I did not begin to understand what the snag was, I just transferred Q_DECLARE_METATYPE for types to their header files. And again start - it works!

The program has started, but there is still a lot of work. The transition to xcb should still be complete, i.e. my X11Info class should be rewritten using xcb. You also need to check on the performance of everything that was patched, however, as well as not patched. But, I hope, the hardest is over!

I hope my experience will be useful to you. Below are some links that helped me solve Qt5 puzzles:

www.kdab.com/porting-from-qt-4-to-qt-5
xcb.freedesktop.org/tutorial
qt-project.org/doc/qt-5.0/qtwidgets/tools-plugandpaint.html
google.com

UPDATE: A useful crutch for resolving prohibited in Qt5

Source: https://habr.com/ru/post/164721/


All Articles