Porting my first Application from Qt5 to Qt6

published at 13.07.2022 11:15 by Jens Weller
Save to Instapaper Pocket

Just this week I had the opportunity to recompile one of my Qt Applications to build it with Qt6. Here is some of the results.

The application in question manages parts of the conference, and hence is written in Qt for the UI and uses SQLite as datastorage, so Qt also used for this and as I learned for a few other things too. My initial expectation was that this should be an easy job to just recompile and get moving from there.

But all in all, it was a few minor things that needed to be fixed. Not always was it apparent what the best replacement is, some APIs have changed, and without the knowledge and deep understanding of the code where this now triggers an error, it was sometimes not directly obvious which was the best route to take.

I should mention that I was more or less unprepared for this job. I didn't do any research prior on issues, and was just curious if my code would compile or have too many issues. In that case, I'd spend some time on doing a bit of deeper research.

Compatibility Module

One of the first things I found out was that the type QStringRef now lives in the Qt5 compatibility module, which one has to install via the maintenance tool. Only to then find out that in the place where I used QStringRef, this class now wasn't compatible anymore even with installed compatibility module. A vector<QStringRef> was used here to store the parsed parent tags of a token reader for xml. But QXMLStreamReader now returns a QStringView instead a QStringRef for its name() method. Which returns the current tag name. So the correct fix was to replace QStringRef with QStringView and move on.

I've kept the compatibility module for the other potential fixes, but later removed this dependency to see if it had "solved" any issues for me that I'd have to fix with out it. Turns out I didn't need it, except that I could remove #include<QTextCodec>, as it does not exist when compiling without this module.

The other errors

It turned out replacing a type was the easy thing to figure out. Most of the other places where I had errors, this was caused by the API changes. Methods being removed or changed in Parameters. This did affect often code that used QPainters. This application has various uses for using QPainter. One class draws a bargraph on its own, another used to do my invoices with printing to PDF via Qt. And in 2019 I added a section that would print out badges for attendees that came in after the deadline for badge printing.

So in the invoicing code I just could get along with commenting the parts that had changed out. I'll clean up this deadcode later. But often the code was clustering around the same problems. So all my QPainter code uses a class called QFontMetrics. This gets initialized with the font from the painter, and then provides various useful numbers for drawing with the painters related to text with this font.

QFontMetrics fm(painter.font());
for(size_t i=0, s = values.size(); i < s; ++i)
{
   painter.drawRect(bars[i]);
   if(show_label)
      painter.drawText(x_lbl-fm.boundingRect(getLabel(i)).width()/2,y,getLabel(i));
//old painter.drawText(x_lbl-fm.width(getLabel(i))/2,y,getLabel(i));
     ...
}

The old code could here just query the width of the text, which is used to center it under the rectangle being the bar in the bargraph. This method does not exist anymore, and reading through the API and understanding what I actually try to do here, I've decided to go for the width of the boundingRect. This was also the fix for the badge printing code, as this is a common method when needing to display text via QPainter.

Then, in some places an enum that was in class level is now its own type. So QPrinter::A4 becomes QPrinter::PageSizeId::A4 or QPrinter::Millimeter is now QPrinter::Unit::Millimeter. Related to this there used to be a painter.setRenderHint(QPainter::HighQualityAntialiasing), but HighQualityAntialiasing seems to have been removed. I'll need to return to this part of the code to see which combinations of RenderHint Flags is needed to achieve the results needed. The Qt5.15 Documentation already hints at the HighQualityAntialiasing is obsolete, so removing this is the right thing at the moment.

Then, there is a helper function which writes the contents of a QString to a file. It uses for this QTextStream and has an option to do this in UTF 8. This used textstream.setCodec("UTF-8"), but again, this method does not exist anymore.  Its now setEncoding(QStringConverter::Utf8).

The last, and for me one of the weirdest errors I got was for this code: dt = QDateTime(QDate(2016,11,18)); Seems in 2016 I needed in this part of my application a QDateTime that was starting with the start of the conference day. Here the constructor has changed, and now also wants a QTime.

And thats already it. A handful of errors, mostly caused by changes in the API between Qt5 and 6. Some of this might have already shown up as deprecation warnings, but if it did I did not notice. Often its also been in code that was added for a certain functionality, which then did not find its use into the every year part. Like I do have a json export for the schedule which was used in 2016 to export the schedule for an external service Meeting C++ used back then. These codes often hang around, as they contain useful code elements if something like this is ever needed again. The invoicing code was once central to my business, but its now replaced with something else.

Warnings

But wait, there is more! While I was happy that the application did work again, there was also warnings to look at. Qt6 brings a few new friendly deprecation warnings. Like don't copy QSqlQuery, move it. Which is a good advice, except that I figured out that in this case what I needed is using a reference. This code used auto as an argument in a lambda, and early myself with auto might have assumed that this is no copy or that Qt would do a reference count based copy here. In this code the lambda is a helper function, the query is used after calling it later on also. So moving it into the lambda would be not the best thing.

Then there was QVariant code which checks for a certain Qt Type:

//how to do this now
if(id.typeId()==QMetaType::Type::QUrl)
  ...
//whats now giving a deprecation warning
else if(id.type() == QVariant::String)
  ...

Note this code gives you two deprecation warnings, one for using type() instead of typeId() and the other for QVariant::TypeEnum usage instead of QMetaType.

So its a good idea to look for deprecation warnings in your code before you start porting to Qt6, and also pay attention to them after you did so.

 

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!