Development/Tutorials/Graphics/HiDPI
Introduction
HiDPI devices are very common today. Users need to scale up UI to make them looks like normal size. Common scale factors are: 1.25, 1.5, 1.75, 2, 3. This means UI dimensions are not integers anymore. Qt provides classes and functions that accept and return qreal
instead of int
. They usually end with F.
- QRect --> QRectF
- QPoint --> QPointF
- QWidget::devicePixelRatio() --> QWidget::devicePixelRatioF()
KDE and Qt applications may render blur icons and graphics. This guide shows how to make everything sharp and clear in HiDPI devices. Both Qt 5 Widgets and Qt 5 Quick applications are supported.
Migrate to Qt 5 and KF5
Qt 4 doesn't support HiDPI rendering. Please migrate your application to Qt 5 and KF5.
Texts
You don't need to do anything special. Text rendering should support HiDPI out of box.
Icons
In your application's main function, add the following lines at the very beginning:
int main(int argc, char *argv[])
{
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
QApplication app(argc, argv);
...
}
Then all the icons in your application should look sharp.
QPixmap
Change all QPixmap from
pixmap = QPixmap( width, height );
to
pixmap = QPixmap( width * dpr, height * dpr );
pixmap.setDevicePixelRatio(dpr);
So where is the dpr
variable from? You can get it from any QWidget
objects with function devicePixelRatioF()
. F means the function return a float number (qreal
). If you are not in a QWidget
class context, you can get the value via (the disadvantage is that it may not work well in multi-display set up with different DPR):
const qreal dpr = qApp()->devicePixelRatio();
And don't forget to change width
and height
from int
to qreal
.
QPainter
QPainter
has various drawing functions. You need to change parameters of those functions from integer types:
QRect
QPolygon
QPoint
int
to floating types:
QRectF
QPolygonF
QPointF
qreal
For example, you need to change this code:
QPainter p;
p->drawText(0, 0, 12, 50, Qt::AlignLeft | Qt::AlignTop, text);
to:
QPainter p;
p->drawText(QRectF(0, 0, 12, 50), Qt::AlignLeft | Qt::AlignTop, text);
Similarly, You should set float number width for QPen
. Please change:
QPen pen(Qt::green, 3);
to:
QPen pen(Qt::green, qreal(3));
After all these changes, your QPainter
object should be able to render HiDPI graphics.
Note: you don't need to scale x, y, width, height values.
Qt Quick Controls 1
If the QML contains Qt Quick Controls 1, all text components will be blurred and small in HiDPI display. This is likely a Qt bug.
To solve this, we suggest you migrating to Qt Quick Controls 2.
Dual-screen setup
If you have a dual-screen setup with 1080p and 4K, set scale factor to 1.5, and put them side by side, running Qt Widgets applications in such setup will cause wrong rendering. This is likely a Qt bug. But we noticed that only some applications are affected, like Kate, Qt Creator, VirtualBox, Spectacle. Other applications look fine.
In Spectacle, we found the cause is that when we set widget position to (0, 0) and the size is too big, Qt/X11 will move it to a position like (-640, 0), (960, -640). The positive value doesn't affect, but the negative value brings a weird offset. To solve it, we simply detect the position and change the negative value to 0. Note this can only be done in paintEvent() rather than moveEvent(). Check the patch here.
In Kate, the cause is KonsolePart widget embedded in the project plugin. When calling a specific function, the rendering is messed. Change to another function solved the issue. Check the patch here.
We haven't found a common solution for those issues. But there might be different hacks we can do.
Examples
- KSysGuard patch #1 (Icons)
- Font Manager patch (QPainer, QPixmap, QImage)
- Filelight patch (QPainer)